Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by DiligentGear · Dec 27, 2013 at 10:53 PM · c#optimizationfindobjectpool

Finding inactive game objects in a pool

In order to avoid instantiate/destroy at runtime, I built a simple object pool that contains enemy scripts (all enemy classes are inherited from the same abstract class, so I can create a pool that holds all types of enemy in one list).

However, I don't know how to find them and reference them correctly. Here's what I did:

     public List<GameObject> GetEnemy (int amount, string type) {
         //amount: how many enemies are needed
         //type: what type of enemy is needed
         List<GameObject> go = new List<GameObject>();
         //end ForEach loop if got enough enemy
         //the Enemy.cs had a public string that indicates the type of enemy
         enemyRef.ForEach(script => {if (go.Count > amount) return; else if( (!script.gameObject.activeInHierarchy) && (script.type == type) ) go.Add(script.gameObject);} );
         return go;
     }

Although it works, it allocates about 1 kb memory and cause a 2.5 ~ 3ms spike for 800 game objects. This doesn't really kill the frame rate, but there must be a better way.

Is there any better way to find and reference a collection of game objects under certain conditions?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by sparkzbarca · Dec 28, 2013 at 01:11 AM

i just created an object pool here it is if you like.

 public class ObjectPool : MonoBehaviour {
     List<List<GameObject>> GameObjectPools;
     List<GameObject> GameObjectPool;
     GameObject Temp;
     public int POOL_SIZE;
     public int GROWTH_SIZE;

     int Counter;
     // Use this for initialization
     void Start () {
         Counter = 0;
         GameObjectPools = new List<List<GameObject>> ();
 
     }

 void FixedUpdate()
 {

 }
 public GameObject RetrieveObject(GameObject BaseObject)
 {
     foreach(List<GameObject> Item in GameObjectPools)
     {
         Temp = Item[0];
         if((Temp.name == (BaseObject.name + "(Clone)") || Temp.name == BaseObject.name) && Item.Count > 1)
         {
             Item.RemoveAt(0);
             Temp.SetActive(true);
             Counter++;
             return Temp;
         }
     }
     throw new System.NullReferenceException("RetrieveObject(BaseObject)  BaseObject is not pooled, null error");
 }
 public bool PoolObject(GameObject BaseObject)
 {
     foreach(List<GameObject> Item in GameObjectPools)
     {
         Temp = Item[0];
         if(Temp.name == BaseObject.name)
         {
             BaseObject.SetActive(false);
             Item.Add(BaseObject);
             return true;
         }
     }
     throw new System.NullReferenceException("PoolObject(BaseObject)  BaseObject is not pooled, null error");
     return false;
 }
 IEnumerator PrePoolHelper(List<GameObject> GameObjectPool, GameObject BaseObject)
 {
     for (int counter = 0; counter < POOL_SIZE; counter++)
     {
         Temp = Instantiate(BaseObject) as GameObject;
         Temp.SetActive(false);
         GameObjectPool.Add(Temp);
         if(counter % GROWTH_SIZE == 0)
         {
             yield return new WaitForEndOfFrame();
         }
     }
     GameObjectPools.Add(GameObjectPool);
 }
 public void PrePool(GameObject BaseObject)
 {
     GameObjectPool = new List<GameObject>();

     StartCoroutine(PrePoolHelper(GameObjectPool, BaseObject));
 }

}

the way it works basically you supply it with how many of each object you want to pool and how many you want to add per frame (this is basically for pre pooling so if you want 1,000 items you can set it to 10 and it'll spread out the pooling over a 100 frames to help prevent you from trashing your framerate with 1,000 objects being pooled at once, unity will also actually throw up errors if you try to instantiate thousands of objects in a single frame)

when you want to use it in game

you do

 ObjectPool PoolComponent = new PoolComponent();
 
 foreach(gameobject object in poolist)
 {
 PoolComponent.prepool(object);
 }

when you want to instantiate a new object wtihout using instantiate

//its set up to work with clone word attached or not to the object name

 PoolComponent.retrieveobject(object);

to send it back

 PoolComponent.poolobject(object);

stores it again

the key i'd say with my thing is that i have a list of lists

each object you want to pool gets its own list so for example if you have 10 objects and 1,000 clones each

it only searches the first item in each of the 10 lists to see if it can fidn the object to pool it doesn't iterate through all 10,000 doing a comparison.

checking one object in a list ensures all the clones are removed from any chance at being looked at.

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image sparkzbarca · Dec 28, 2013 at 01:13 AM 0
Share

any code you'd like explained just ask :)

avatar image DiligentGear · Dec 28, 2013 at 06:57 AM 0
Share

wow, I'm impressed by such robust answer! Thanks!

I think my design has serious problem, since I'm trying to find a certain amount of objects ins$$anonymous$$d of one. This should be done by the game controller, not the pool.

$$anonymous$$y last solution was very similar to yours, and it worked like a charm: find the first inactive item that return the reference. Here's what I did before:

 instanceList.Find(script => {if (script.gameObject.activeInHierarchy && (script.type == type) )})

However, this will always refer to the same element if I don't active it immediately after this. Then I ran into a hole that I had to retrieve a list from the pool.

I'll definitely try to use remove/add ins$$anonymous$$d of checking activation, since it's probably GetComponent<> based property.

$$anonymous$$y only question is, since you consistently remove/add objects in the pool, do you keep the reference that retrieved somewhere else?

For example, do I need to do something like this:

 public class EnemyController : ObjectPool {
     List<GameObject> reference; // reference to the objects that are retrieved
     
     void GetEnemies (int amount, GameObject type) {
         // get clones of certain type of enemy
         for (int i = 0; i < amount; i++)
         {
             reference.Add (RetrieveObject(type));
         }
     }
     
     IEnumerator SetEnemy (int amount, GameObject type, Vector3 pos, float waitTime) {
         // set the positions
         for (int i = 0; i < amount; i++)
         {
             if (reference[i] == type.name)
             {
                 reference[i].transform.position = pos;
                 reference[i].SetActive(true); // I'd prefer active after setting position
                 yield return new WaitForSeconds(waitTime);
             }
         }
         yield return null;
     }
     
     // in state machine:
     void GenerateEnemy () {
         if (condition)
         {
             StartCoroutine(SetEnemy (amount,type,pos,waitTime));
         }
     }
 }
avatar image sparkzbarca · Dec 28, 2013 at 01:26 PM 0
Share

if you'd like to return multiple copies of an object you can modify to retreive a list of X number of clones ins$$anonymous$$d of just one easy

 public GameObject RetrieveObject(GameObject BaseObject)
 {
 foreach(List<GameObject> Item in GameObjectPools)
 {
 Temp = Item[0];
 if((Temp.name == (BaseObject.name + "(Clone)") || Temp.name == BaseObject.name) && Item.Count > 1)
 {
 Item.RemoveAt(0);
 Temp.SetActive(true);
 Counter++;
 return Temp;
 }
 }
 throw new System.NullReferenceException("RetrieveObject(BaseObject) BaseObject is not pooled, null error");
 }

thats the current code

this is the new function that would work for a list

 //CHANGED RETURN TYPE, CHANGED NA$$anonymous$$E, ADDED COUNT PARA$$anonymous$$ETER
 public list<GameObject> RetrieveList(GameObject BaseObject, int Count)
 {
 
 list<GameObject> ReturnList; //ADDED
 
 foreach(List<GameObject> Item in GameObjectPools)
 {
 Temp = Item[0];
 
 //$$anonymous$$ODIFIED item.Count > 0 to > Count to make sure enough clones
 //were availabe
 if((Temp.name == (BaseObject.name + "(Clone)") || Temp.name == BaseObject.name) && Item.Count > Count)
 {
 
 //this will get the first Count number of clones which is how many you passed you wanted
 //and put them in the return list.
 Returnlist.addrange(Item.getrange(0, Count));
 
 //then we'll remove the whole range as opposed to just one
 Temp.removerange(0,Count);
 
 //then we'll set the whole range to active ins$$anonymous$$d of just one
 foreach(GameObject ReturnItem in ReturnList)
 {
 ReturnItem.setactive(true);
 }
 Counter++;
 
 //then we'll return the list ins$$anonymous$$d of just one
 return ReturnList;
 }
 }
 throw new System.NullReferenceException("RetrieveObject(BaseObject) BaseObject is not pooled, null error");
 }
avatar image DiligentGear · Dec 28, 2013 at 01:54 PM 0
Share

Thanks for updating! I just get up and merge the codes.

Actually, I think the old one is better. if I need 50 instances and there are only 49 available, old one would return 49 and throw 1 exception, but this one will just throw an exception.

I still have some problems about memory optimization, but I guess it's out of topic for this question.

avatar image DiligentGear · Dec 28, 2013 at 02:01 PM 0
Share

There's another thing I'm wondering, here's qoute from the doc:


The foreach statement is used to iterate through the collection to get the information that you want, but can not be used to add or remove items from the source collection to avoid unpredictable side effects.


Source: link text

Since the we can simply get the length of the list by List.Count, how about using for loop to iterate through the list?

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

20 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How do I efficiently maintain a constantly-changing list without triggering GC? 2 Answers

Is there a more efficient way to write a "Find" script? 2 Answers

Is there a more efficient way to writ a "Find" script? 0 Answers

How big is too big? Terrain Question. 1 Answer

Optimization Question about Singleton 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges