- Home /
Code optimisation - how to use arrays?
Hello!
So I have made a starfield, loaded procedurally by simply instantiating a star in a random position when the scene loads.
To make the starfield infinite, I added a script to each individual star prefab. The code says if it gets too far away from the camera on either the X or Y axis, move it to the other side of the camera. This works pretty well to create an infinite starfield.
if (transform.position.y < cam.transform.position.y-7)
{
transform.position.y = transform.position.y+14;
}
if (transform.position.y > cam.transform.position.y+7)
{
transform.position.y = transform.position.y-14;
}
if (transform.position.x < cam.transform.position.x-12)
{
transform.position.x = transform.position.x+24;
}
if (transform.position.x > cam.transform.position.x+12)
{
transform.position.x = transform.position.x-24;
}
The problem is that it clearly isn't very efficient having this code running on 700 seperate objects. I have been looking into controlling all the objects from one script, probably using arrays, but I can't find a way to apply logic to all GameObjects inside an array. How would I do this? I can apply it to a single object in the array by using something along the lines of "myArray [1].transform.position", but not how to do it for every object in the array. Thank you!
Answer by fafase · May 03, 2014 at 02:28 PM
One way is not to use the array but more likely to use the engine to perform the check for you.
Place a trigger box on the outside and when the object enters it then, send it on the other side. This goes on the box:
void OnTriggerEnter(Collider col){
if(col.gameObject.tag == "Star")// This can be removed if only stars get there
// reposition the star
}
The engine is optimized using any kind of algorithm to skip check if the object is not meant to be close to the trigger box. So I would recommend it.
Otherwise, your code is not too much of a big deal, it is just some basic arithemetics done in a few clicks, I would not bother doing some micro optimization, even for thousands of objects. You could do a check up front to skip the four next checks.
if((tansform.position - cam.position).sqrMagnitude > range) return;
then if the distance is not great enough, you are not likely to be out of bound.
You could cache the transform:
private new Transform transform;
void Start(){
transform = base.transform;
}
Once you have a check returning true, the following ones won't happen so change your if for if/else if.
If the camera is not moving then store the values instead of making them up each round. If it moving cache the Transform of the camera:
Transform cam;
void Start(){
cam = Camera.main.transform;
}
Those are little details that may get your fps a little bump up.
As a result:
private new Transform transform;
private Transform cam;
void Start(){
transform = base.transform;
cam = Camera.main.transform;
}
void Update(){
Vector3 position = transform.position;
Vector3 camPos = cam.position;
if((position - camPos).sqrMagnitude > range) return;
if (position.y < camPos.position.y-7)
{
transform.position.y = position.y+14;
}
else if (position.y > camPos.position.y+7)
{
transform.position.y = transform.position.y-14;
}
else if (position.x < camPos.position.x-12)
{
transform.position.x = position.x+24;
}
else if (position.x > camPos.position.x+12)
{
transform.position.x = position.x-24;
}
}
Answer by _dns_ · May 03, 2014 at 02:23 PM
Hi, I would go for particle: http://docs.unity3d.com/Documentation/ScriptReference/ParticleSystem.html
You can have billboards or 3d objects to represent the stars and you have access to each particle position individually using GetParticles (http://docs.unity3d.com/Documentation/ScriptReference/ParticleSystem.GetParticles.html) and SetParticles().
You would then have 1 object that iterates with a for loop through each particle to change it's position where you want it (with setting the life of particles to infinite or a very big value first).
I would also recommend to cache the bounds of the camera in local variables, like float myXLimit = camera.transform.position.x and comparing against myXLimit (accessing transfom can become expensive in a loop like that)
Answer by mattyman174 · May 03, 2014 at 11:57 AM
Are you looking to iterate over an Array?
for (int i = 0; i < starArray.Length; i++)
{
// Do something here.
// Use the i variable to control which element of the array you wish to manipulate, instead of directly using a specific integer to determine which element to use.
}
foreach (GameObject star in starArray)
{
// Do something here.
// Use the star variable as the reference to the current element in the array that the loop is looking at.
}
A for/foreach loop is a great way to iterate over an Array and provide specific code to apply to each element.
Is this what you were after?
Won't this apply the code to each star in turn? I want to find any star object in the array which meets a certain condition, then apply code to it if it does.
You need to iterate over all the stars and check which ones fit your conditions, then you can apply specific logic to those stars. Or do you already have an array of all the stars you want to apply particular logic to?
As a side note, foreach looks neater but is also less memory efficient than the basic for loop. Reason being that the compiler creates some new objects during the iteration (some IEnumeratpr object) so I always use the basic for loop. Also, foreach does not allow the modification of the collection so all in all, I use basic for loop.
So for example. Consider the code below.
for (int i = 0; i < starArray.Length; i++)
{
// Do something here.
// Use the i variable to control which element of the array you wish to manipulate, inside of directly using a specific integer to deter$$anonymous$$e which element to use.
if (starArray[i].renderer.material.color == Color.red)
{
// Ive found a Red star in my array of all stars and now i want to do something with it here.
starArray[i].renderer.material.color = Color.blue; // I want to change all red stars to blue.
}
}
foreach (GameObject star in starArray)
{
// Do something here.
// Use the star variable as the reference to the current element in the array that the loop is looking at.
if (star.renderer.material.color == Color.red)
{
// Ive found a Red star in my array of all stars and now i want to do something with it here.
star.renderer.material.color = Color.blue; // I want to change all red stars to blue.
}
}
That makes sense, but I want to check if the star is too far away, and if it is move it to a new position - so that the starfield appears infinite.
If it loops through the objects in the array in steps, then won't there be a delay in the star being moved if the code happens to be on a different star at that exact moment?
Answer by bloodyatheist · May 03, 2014 at 01:57 PM
you can do several things:
1) you could try looking at the c# Hashtable
class and sort the stars in it by distance that way you can apply for example on all stars up to a distance of X one thing and the rest an other thing and its much easier and its more efficient
2) if your moving and the stars stay at place then its a waste to calculate all this. you can separate the stars by areas and calculate distance to an area that way its less calculations per frame and its much simpler.
and the decide the star's distance by its area without calculating anything(it will give less accurate results but its enough for these kind of things)
i hope i helped :)
I think hashtables seem to be the thing I was looking for! I'll look into implementing it, thank you! Also, thank you to everyone else who replied, some really useful stuff! I thought I knew what I was doing pretty well by this point... Turns out I have a long way to go!
Your answer
Follow this Question
Related Questions
New Object Pooling Problem 1 Answer
Merging together all GameObjects of an Array? 1 Answer
script optimization (choosing an object in game by click) 1 Answer
C# Should I store array of GameObjects or the scripts attached to them? 1 Answer
How to randomly pick a gameobject except for one gameobject in an array ? 1 Answer