Sorting gameobjects using sorting algorithms - c#

I want to sort 5 spheres in Unity by using sorting algorithms. They will swap places in sorted order after I click sort button. I manage to create a list for gameobjects but as I understand it is only sorting the list then do nothing. How to create such script that I want? It will swap objects by gameobject name. The Envrioment,
the code that I made so far;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace Assets
{
class Gameobjects : MonoBehaviour
{
public Button s_YourButton;
[SerializeField]
private GameObject[] deck;
public List<GameObject> instanciatedObjects;
void Start()
{
Button btn = s_YourButton.GetComponent<Button>();
//Calls the TaskOnClick method when you click the Button
btn.onClick.AddListener(TaskOnClick);
}
void TaskOnClick()
{
Fill();
instanciatedObjects = instanciatedObjects.OrderBy(Sphere => Sphere.name).ToList();
}
public void Fill()
{
instanciatedObjects = new List<GameObject>();
for (int i = 0; i < deck.Length; i++)
{
instanciatedObjects.Add(Instantiate(deck[i]) as GameObject);
}
}
}
}
Any idea is welcome for me to do futher research, I am new to Unity.

First you can do is store a list of Vector3 of the old one. basicly List.Add(spawnedObject.transform.position); inside the for loop in Fill().
Then after you sorted them, you loop the instanciatedObjects and set them in the same order as the Vector3 list.
List<Vector3> vectorList = new List<Vector3>();
void TaskOnClick()
{
Fill();
instantiatedObjects = instantiatedObjects.OrderBy(Sphere => Sphere.name).ToList();
for(int i = 0; i < instanciatedObjects.Count; i++)
{
instantiatedObjects[i].transform.position = vectorList[i];
}
}
public void Fill()
{
vectorList.Clear();
instantiatedObjects = new List<GameObject>();
for (int i = 0; i < deck.Length; i++)
{
GameObject spawnedObject = Instantiate(deck[i]) as GameObject;
instantiatedObjects.Add(spawnedObject);
vectorList.Add(spawnedObject.transform.position);
}
}
btw, a typo in your code: instanciatedObjects should be instantiatedObjects

Related

Is this a more optimized alternative to instantiate?

I'm trying to make a custom object pooling class (this is my first time trying to avoid using Instantiate().
I know object pooling is objectively better than instantiating but I'm worried that the way I set it up might actually be worse than instantiating since I have a long List of gameobjects, and in order to find a reusable gameobject I have to use a foreach loop to loop through all the elements of the list.
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooler : MonoBehaviour
{
[SerializeField] GameObject redHurtEffect;
[SerializeField] GameObject Arrow;
static List<GameObject> pooledObjects = new List<GameObject>(2);
private void Awake()
{
for (int i = 0; i < 15; i++)
{
GameObject theEffect = Instantiate(redHurtEffect);
pooledObjects.Add(theEffect);
theEffect.SetActive(false);
}
for (int i = 0; i < 550; i++)
{
GameObject theEffect = Instantiate(Arrow);
pooledObjects.Add(theEffect);
theEffect.SetActive(false);
}
}
public static void Spawn(GameObject prefab, Vector3 spawnWhere, Quaternion? rotation =null, int repetitions =1, Vector2? setScale = null)
{
if (repetitions == 1)
{
Debug.Log(prefab.name);
GameObject spawned = prefabToPooled(prefab);
spawned.SetActive(true);
spawned.transform.position = spawnWhere;
if(setScale!=null || setScale.HasValue)
{
spawned.transform.localScale = setScale.Value;
}
if (rotation.HasValue)
{
spawned.transform.rotation = rotation.Value;
}
}
else
{
for (int i = 0; i < repetitions+1; i++)
{
// convertedPrefab.SetActive(true);
// convertedPrefab.transform.position = spawnWhere;
}
}
}
public static GameObject Spawn(GameObject prefab, Vector3 spawnWhere, Quaternion? rotation = null, Vector2? setScale = null, Transform parent = null)
{
GameObject spawned = prefabToPooled(prefab);
Transform spawnedTransform = spawned.GetComponent<Transform>();
Debug.Log(prefab.name);
spawned.SetActive(true);
spawnedTransform.position = spawnWhere;
if (rotation.HasValue)
{
spawnedTransform.rotation = rotation.Value;
}
if (setScale != null || setScale.HasValue)
{
spawnedTransform.localScale = setScale.Value;
}
spawnedTransform.SetParent(parent);
return prefabToPooled(prefab);
}
public static void rePool(GameObject gameObj)
{
gameObj.transform.SetParent(null);
pooledObjects.Add(gameObj);
gameObj.SetActive(false);
}
public static GameObject prefabToPooled(GameObject prefab)
{
string prefabName = prefab.name;
foreach (var item in pooledObjects)
{
if(item.name.Contains(prefabName))
{
item.SetActive(true);
pooledObjects.Remove(item);
return item;
}
}
return null;
}
}
The performance seems the same? I tried fake instantiating arrows every frame using the object pooling, and then I tried normally instantiating, and unless I'm blind the results seemed to be the same.
Also I'm worried if it'll be slower since I just started using this, I have so much more items to add to the game. The pooledObjects List might contain thousands or even tens of thousands of elements (since I want to add treasure like "diamond crown, emerald crown, coin, necklace" and I'd like to have many of those per level)
Would foreach looping through those thousands of elements be worse than instantiating?
Thanks!

Unity. Script does not working with clones

I have created a script who will spawn objects and give tag and color for one element of prefab. But script not working with clones. This 2 scripts, GameManager( he must spawn objects) and
RandomTagAndColor( he give to element of prefab tag and name). And in scene of game, where objects is spawning, script give tag and colour only to first prefab. In game those prefabs 10. Well, I'm sorry if question is stupid, this first thing, what i doing without books,guides.
GameManager script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class GameManager : MonoBehaviour
{
// Start is called before the first frame update
public GameObject firstBarrier;
public GameObject secondBarrier;
public int numOfBarriers = 0;
System.Random rnd = new System.Random();
System.Random rndY = new System.Random();
Vector3 vector = new Vector3(5, 1/3 , 1);
void Start()
{
for (numOfBarriers = 0; numOfBarriers < 10; numOfBarriers++)
{
int ewq = rnd.Next(1, 20);
int rY = rndY.Next(1, 4);
if(ewq <= 10)
{
Instantiate(firstBarrier, vector, Quaternion.identity);
}
else
{
Instantiate(secondBarrier, vector, Quaternion.identity);
}
vector.x -= 7;
vector.y = rY;
}
}
void Update()
{
}}
RandomColorAndTag script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class RandomColorAndTag : MonoBehaviour
{
GameObject Bar;
System.Random randomElement = new System.Random();
int el = 0 ;
void Start()
{
el = randomElement.Next(2, 6);
GameObject.Find($"Cube ({el})").GetComponent<Renderer>().material.color = Color.green;
if (GameObject.Find($"Cube ({el})").GetComponent<Renderer>().material.color == Color.green)
{
GameObject.Find($"Cube ({el})").transform.tag = "Green";
}
}
void Update()
{
}
}
As mentioned there are lot of unclear issues in your codes.
I will assume the RandomColorAndTag is attached to the objects you spawn. In that case don't use Find but rather simply assign the random color and tag to yourself
what is a random value between 1 and 19 good for, of all you ever do with it is check whether it is bigger or smaller than 10? => Simply use only a random 0 or 1 and check whether it is 0 ;)
I'm unsure exactly what items should be green now .. I assume among the 10 spawned items you want to pick one and make it special..
finally I wouldn't even let the objects assign their own tag and color but let the GameManager trigger it
So I would do something like
public class GameManager : MonoBehaviour
{
public TagAndColorController fortBarrier;
public TagAndColorController secondBarrier;
public int numOfBarriers = 10;
public Vector3 vector = new Vector3(5, 1f/3f , 1);
public Color specialColor = Color.Green;
public string specialTag = "Green";
private void Start()
{
var specialIndex = Random.Range(0, numberOfBarriers);
for (var i = 0; i < numOfBarriers; i++)
{
// upper bound is exclusive -> this returns 0 or 1
var rndBarrier = Random.Range(0, 2);
// not sure if intended but again: upper bound is exclusive -> this return 1, 2, or 3
var rY = Random.Range(1, 4);
var tagAndColor = Instantiate(rndBarrier == 0 ? firstBarrier : secondBarrier, vector, Quaternion.identity);
if(i == numberOfBarriers)
{
tagAndColor.SetColorAndTag(specialColor, specialTag);
}
vector.x -= 7;
vector.y = rY;
}
}
}
And the other script has no logic whatsoever but rather only is a quick access to the objects renderer and bundles the behavior
public class TagAndColorController : MonoBehaviour
{
[SerializeField] private Renderer _renderer;
public void SetColorAndTag(Color newColor, string newTag)
{
gameObject.tag = newTag;
if(!_renderer) _renderer = GetComponent<Renderer>();
_renderer.material.color = newColor;
}
}

CullingGroup.onStateChanged does not get called

I'm experimenting with the CullingGroup Api, however i'm unable to achieve any success because it looks like that my onStateChanged callback will not get called.
I have 24 spheres and a cube which has the following script attached to.
using UnityEngine;
public class CullingGroupBehaviour : MonoBehaviour
{
CullingGroup localCullingGroup;
public Transform[] Spheres;
public BoundingSphere[] cullingPoints;
void Awake()
{
localCullingGroup = new CullingGroup();
GameObject[] spheres = GameObject.FindGameObjectsWithTag("Spheres");
cullingPoints = new BoundingSphere[spheres.Length];
Spheres = new Transform[spheres.Length];
for (var i = 0; i < spheres.Length; i++)
{
Spheres[i] = spheres[i].transform;
cullingPoints[i].position = Spheres[i].position;
cullingPoints[i].radius = 4.0f;
}
localCullingGroup.onStateChanged = (CullingGroupEvent evt) => Debug.Log("Changed");
localCullingGroup.SetBoundingSpheres(cullingPoints);
localCullingGroup.SetBoundingSphereCount(cullingPoints.Length);
localCullingGroup.SetBoundingDistances(new float[] { 10.0f, 50.0f });
localCullingGroup.SetDistanceReferencePoint(transform.position);
}
void FixedUpdate()
{
localCullingGroup.SetDistanceReferencePoint(transform.position);
for (var i = 0; i < Spheres.Length; i++)
{
cullingPoints[i].position = Spheres[i].position;
}
}
void OnDestroy()
{
localCullingGroup.Dispose();
localCullingGroup = null;
}
}
The expected behaviour is that when i'm moving the cube the distances should change and the lambda expression should be called yet nothing happens.
Any ideas are appreciated!
Update:
The strange thing is that visibility events are sent correctly when using a camera but distance events does not trigger
Looks like you must set a camera in order to calculate distances, which is strange because the DOC says
If targetCamera is assigned then the bounding spheres will only be
culled from the perspective of that camera.
and
To have the CullingGroup perform visibility calculations, specify the
camera it should use
Yet nothing happens when you dont set the camera.
Additional Note:
The onStateChnaged event will only trigger when a BoundingDistance was passed eg: from 1 you enter into 2 (The example only contains one)
Working Solution:
using UnityEngine;
public class CullingGroupBehaviour : MonoBehaviour
{
private CullingGroup cullingGroup;
private BoundingSphere[] bounds;
Transform[] targets;
public Transform ReferencePoint;
void Start()
{
// All the objects that have a sphere tag
var gobjs = GameObject.FindGameObjectsWithTag("Sphere");
targets = new Transform[gobjs.Length];
for(int i = 0; i < gobjs.Length; i++)
{
targets[i] = gobjs[i].transform;
}
cullingGroup = new CullingGroup();
cullingGroup.targetCamera = Camera.main;
// Will automatically track the transform
cullingGroup.SetDistanceReferencePoint(transform);
// The distance points when the event will trigger
cullingGroup.SetBoundingDistances(new float[] { 25.0f });
// Creating Boundingspheres
bounds = new BoundingSphere[targets.Length];
for (int i = 0; i < bounds.Length; i++)
{
bounds[i].radius = 1.5f;
}
// Assigning the Bounding spheres
cullingGroup.SetBoundingSpheres(bounds);
// if not set it will use all of the array elements(so below code is redundant)
cullingGroup.SetBoundingSphereCount(targets.Length);
// Assigning an event when the distance changes
cullingGroup.onStateChanged = OnChange;
}
void Update()
{
for (int i = 0; i < bounds.Length; i++)
{
bounds[i].position = targets[i].position;
}
}
void OnDestroy()
{
cullingGroup.Dispose();
cullingGroup = null;
}
void OnChange(CullingGroupEvent ev)
{
if (ev.currentDistance > 0)
{
targets[ev.index].gameObject.GetComponent<Renderer>().material.color = Color.green;
}
else
{
targets[ev.index].gameObject.GetComponent<Renderer>().material.color = Color.red;
}
}
}

How can I get images from "assets/resource/mat "?

I have some images in "assets/resource/mat" . I want to get this images and put them to array . But when I try to get this images I'm getting ArrayIndexOutOfBoundsException . I think that there is problem with Resource.LoadAll("mat") method . But I can't fix it . Please help me
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class test : MonoBehaviour
{
private string t;
public Sprite[] Icons;
void Start()
{
Object[] loadedIcons = Resources.LoadAll("mat");
Icons = new Sprite[loadedIcons.Length];
for (int x = 0; x < loadedIcons.Length; x++)
{
Icons[x] = (Sprite)loadedIcons[x];
Debug.Log("Loading....");
}
GameObject sp = new GameObject();
sp.GetComponent<SpriteRenderer>().sprite = Icons[0];
}
}
Loading all images from a particular folder, with LINQ, would look like this...
using UnityEngine;
using System.Linq;
public class Four : MonoBehaviour
{
public Sprite[] icons;
void Start()
{
icons= Resources.LoadAll("met", typeof(Sprite)).Cast<Sprite>().ToArray();
}
}
I'm not sure but I guess you instead of Sprite first have to load a Texture2D and then create a Sprite from it.
Also note you used GetComponent<SpriteRenderer>() on a newly created empty GameObject so obviously there will never be a SpriteRenderer component attached. Instead use AddComponent.
Sprite[] Icons;
Texture2D LoadedTextures;
private void Start()
{
LoadedTextures = (Texture2D[])Resources.LoadAll("mat", typeof(Texture2D));
Icons = new Sprite[loadedIcons.Length];
for (int x = 0; x < loadedIcons.Length; x++)
{
Icons[x] = Sprite.Create(
LoadedTextures[x],
new Rect(0.0f, 0.0f, LoadedTextures[x].width, LoadedTextures[x].height),
new Vector2(0.5f, 0.5f),
100.0f);
}
GameObject sp = new GameObject();
// Note that here you created a new empty GameObject
// so it obviously won't have any component of type SpriteRenderer
// so add it instead of GetComponent
sp.AddComponent<SpriteRenderer>().sprite = Icons[0];
}

Sorting List of Gameobjects Using Any Sorting Algorithm

I want to transform this script to make the code sort using any kind of sorting algorithm in example quick sort, bubble sort, selection sort... etc.
Is there a way to replace
instantiatedObjects = instantiatedObjects.OrderBy(go => go.name).ToList();
and change sorting method to quick sort in example? Is it necessary to use arrays or while using list like this is it possible to make it work with quick sort and other sorting algorithms?
The script is briefly creates clone of the gameobjects at the scene then sort them, then it destroys the original gameobjects so the clones will the only ones at the scene that are sorted.
Beginning, Randomized, Sorted
Here is my script;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace Assets {
class Gameobjects : MonoBehaviour {
public Button s_YourButton;
[SerializeField]
private GameObject[] deck;
public List<GameObject> instantiatedObjects;
void Start() {
Button btn = s_YourButton.GetComponent<Button>();
//Calls the TaskOnClick method when you click the Button
btn.onClick.AddListener(TaskOnClick);
}
List<Vector3> vectorList = new List<Vector3>();
void TaskOnClick() {
Fill();
instantiatedObjects = instantiatedObjects.OrderBy(go => go.name).ToList();
for (int i = 0; i < instantiatedObjects.Count; i++) {
instantiatedObjects[i].transform.position = vectorList[i];
}
string name = "1";
string name1 = "2";
string name2 = "3";
string name3 = "4";
string name4 = "5";
GameObject go1 = GameObject.Find(name);
GameObject go2 = GameObject.Find(name1);
GameObject go3 = GameObject.Find(name2);
GameObject go4 = GameObject.Find(name3);
GameObject go5 = GameObject.Find(name4);
//if the tree exist then destroy it
if (go1 & go2 & go3 & go4 & go5) {
Destroy(go1.gameObject);
Destroy(go2.gameObject);
Destroy(go3.gameObject);
Destroy(go4.gameObject);
Destroy(go5.gameObject);
}
}
public void Fill() {
vectorList.Clear();
instantiatedObjects = new List<GameObject>();
for (int i = 0; i < deck.Length; i++) {
GameObject spawnedObject = Instantiate(deck[i]) as GameObject;
instantiatedObjects.Add(spawnedObject);
vectorList.Add(spawnedObject.transform.position);
}
}
}
}

Categories

Resources