Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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 VozzeTheCreator · Mar 31, 2018 at 04:49 PM · listlistsremove

Unable to remove GameObjects from list

Hello everyone,

I am trying to keep track of every object in my scene by storing them in certain lists and I am keeping track of those lists in one big list. My code works fine up untill the part where I need to remove the object from the lists when they get destroyed. I got it working with a Debug.Log mesage that can tell me that I have to remove "That" gameobject from "this" list but for whatever reason the .Remove doesn't actually seem to happen. Can somebody help me out please?

Thanks in advance, Vozze

PS. The relevant part of the code is the objectDestroyed part that gets called by the destroyed object before it actually gets destroyed.

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class GameManagerScript : MonoBehaviour {
 
     //Lists
     List<List<GameObject>> objectContainerLists;
     public List<GameObject> targetableObjects;
     public List<GameObject> players;
     public List<GameObject> Team1;
     public List<GameObject> Team2;
 
     bool checkedLists;
     GameObject objectToRemove;
 
     //HealthManager
     GameObject destinationObject;
     HealthManager healthManager;
 
     //Matchmaker
     int playerCount;
 
 
 
     
     // Use this for initialization
     void Start () {
         players.AddRange(GameObject.FindGameObjectsWithTag("Player"));
         playerCount = players.Count;
 
         targetableObjects.AddRange(GameObject.FindGameObjectsWithTag("Target"));
         targetableObjects.AddRange(GameObject.FindGameObjectsWithTag("Player"));
 
         objectContainerLists = new List<List<GameObject>>();
         objectContainerLists.Add(new List<GameObject>(targetableObjects));
         objectContainerLists.Add(new List<GameObject>(players));
         Debug.Log(objectContainerLists);
     }
     
     // Update is called once per frame
     void FixedUpdate () {
 
         Debug.Log("--------------------------");
         foreach (GameObject obj in targetableObjects) {
             Debug.Log(obj.name);
                 }
     }
 
     public void objectDestroyed(GameObject destroyedObject)
     {
         Debug.Log("Message Recieved");
         foreach (List<GameObject> list in objectContainerLists)
         {
             if (list.Contains(destroyedObject))
             {
                 list.Remove(destroyedObject);
                 Debug.Log("Removed" + destroyedObject.name + " from " + list);
             }
         }
     }


Comment
Add comment · Show 5
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 andrew-lukasik · Mar 31, 2018 at 05:04 PM 0
Share

$$anonymous$$ake sure destroyedObject is not null. Otherwise it's expected behaviour.

avatar image VozzeTheCreator andrew-lukasik · Mar 31, 2018 at 05:36 PM 0
Share

Yes, I know for sure it's not null. That's why I'm confused. I got it working with:

 targetableObjects.Remove(destroyedObject);
         players.Remove(destroyedObject);

but this is only a temporary solution.

avatar image hexagonius · Mar 31, 2018 at 06:10 PM 0
Share

putting a breakpoint and seeing what comes in and what list you're dealing with is the easiest solution to finding out what's happening here

avatar image pako · Mar 31, 2018 at 08:30 PM 0
Share

How/where do you call objectDestroyed(GameObject destroyedObject) ?

Also, before Debug.Log("$$anonymous$$essage Recieved"); use another debug statement Debug.Log(destroyedObject.name);

avatar image VozzeTheCreator pako · Mar 31, 2018 at 09:14 PM 0
Share

objectDestroyed can get called by every healthmanager in the scene. here is the code: using System.Collections; using System.Collections.Generic; using UnityEngine;

 public class Health$$anonymous$$anager : $$anonymous$$onoBehaviour {
 
     [Header("Health")]
     public float health = 100;
 
     ExplosionCode explosionCode;
     Rigidbody rigidbody;
 
     GameObject game$$anonymous$$anager;
     Game$$anonymous$$anagerScript game$$anonymous$$anagerScript;
 
     float damage;
     int frames;
 
     // Use this for initialization
     void Start () {
         health -= damage;
 
         explosionCode = GetComponentInChildren<ExplosionCode>();
         rigidbody = GetComponent<Rigidbody>();
 
         game$$anonymous$$anager = GameObject.Find("Game$$anonymous$$anager");
         game$$anonymous$$anagerScript = game$$anonymous$$anager.GetComponent<Game$$anonymous$$anagerScript>();
 
     }
     
     // Update is called once per frame
     void Update () {
 
         health -= damage;
 
         if (health < 0)
         {
             game$$anonymous$$anagerScript.objectDestroyed(gameObject);
 
             if (explosionCode != null)
             {
                 explosionCode.ExplosionEnter(rigidbody.velocity);
             }
 
             Destroy(gameObject, 10f); //10 seconds to make sure everything gets executed right.
         }
     }
 
     public void takeDamage(float inco$$anonymous$$gDamage)
     {
         damage = inco$$anonymous$$gDamage;
         if (damage > 0)
         {
             Debug.Log("Damage taken: " + damage);
         }
     }
 }
 

4 Replies

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

Answer by pako · Apr 01, 2018 at 11:29 AM

EDIT:

After going through your repro project it became quite apparent what's going on:

In GameManagerScript you have the public targetableObjects variable, which is what you observe in the Inspector during gameplay.

However, in the objectDestroyed(GameObject destroyedObject) method you remove the destroyedObject from a List, which is contained in the objectContainerLists List of Lists. Since destroyedObject is found inside one of the Lists in objectContainerLists, it is removed from that particular List, but this doesn't affect the targetableObjects List, because in Start() you have initialized objectContainerLists with a clone of targetableObjects.

If you want to directly remove the destroyedObject from targetableObjects by way of removing it from objectContainerLists, you should include a reference to it, rather than initialize it with a clone. So, change Start() like this:

     void Start () {
         //players.AddRange(GameObject.FindGameObjectsWithTag("Player"));
         //playerCount = players.Count;
 
         targetableObjects.AddRange(GameObject.FindGameObjectsWithTag("Target"));
         targetableObjects.AddRange(GameObject.FindGameObjectsWithTag("Player"));
 
         //missiles.AddRange(GameObject.FindGameObjectsWithTag("Missile"));
 
         objectContainerLists = new List<List<GameObject>>();
         //objectContainerLists.Add(new List<GameObject>(targetableObjects)); //Creates clone of targetableObjects
         //objectContainerLists.Add(new List<GameObject>(players));  //Creates clone of players
 
         objectContainerLists.Add(targetableObjects); //References targetableObjects
         objectContainerLists.Add(players);  //References players
 
         Debug.Log(targetableObjects);
     }
 

Additionally, I commented out LateUpdate() and added some Debug.Log statements inside objectDestroyed(GameObject destroyedObject) so that it reports what happens in more detail:

     public void objectDestroyed(GameObject destroyedObject)
     {
         /*targetableObjects.Remove(destroyedObject);
         players.Remove(destroyedObject);*/
         /*foreach (List<GameObject> list in objectContainerLists)
         {
             for (int i = 0; i < list.Count; i++)
             {
                 // Check if list at index of i is equal to destroyedObject / list is containing destroyedObject
                 if (list[i] == destroyedObject)
                 {
                     list.Remove(list[i]);
                 }
             }
         }*/
 
         foreach (List<GameObject> list in objectContainerLists)
         {
             if (list.Contains(destroyedObject))
             {
                 Debug.Log("Before removing item from " + list + ", objectContainerLists contains:");
 
                 foreach (GameObject go in list)
                 {
                     Debug.Log(go.name);
                 }
 
                 list.Remove(destroyedObject);
                 Debug.Log("Removed: " + destroyedObject.name + " from " + list + " in objectContainerLists");
                 //Debug.Log(list);
 
                 Debug.Log("List " + list + " in objectContainerLists now contains:");
 
                 foreach (GameObject go in list)
                 {
                     Debug.Log(go.name);
                 }
             }
         }
     }
 

Sorry, I didn't catch this earlier, but it's much easier to see what's going on when a repro project is available.


One more thing I've just noticed: In HealthManager.Update() you call objectDestroyed(gameObject) if health < 0but health will be less than zero for many frames. So, in the first frame the gameObject is removed from the lists and is then requested to be removed from the list for 10 seconds until the gameObject is destroyed. At 60 FPS there will be 600 calls to objectDestroyed(gameObject), and only the first one will succeed in removing the gameObject from the lists. In fact Destroy(gameObject, 10f) will also be called 600 times at 60 FPS.

Apparently, you have one healthmanager to each destroyable GameObject, so it's not that different healthmanagers call objectDestroyed(gameObject) with the same gameObject (per my comment), it's the same healthmanager calling it for 10 seconds.

In that case, use a boolean variable:

 private bool hasBeenDestroyed;
 
      void Update () {
  
          health -= damage;
  
          if (health < 0 && !hasBeenDestroyed)
          {
              gameManagerScript.objectDestroyed(gameObject);
  
              if (explosionCode != null)
              {
                  explosionCode.ExplosionEnter(rigidbody.velocity);
              }
  
              Destroy(gameObject, 10f); //10 seconds to make sure everything gets executed right.
 
              hasBeenDestroyed = true; // this will make sure that the "Destroy" code gets called only once
          }
      }


















Comment
Add comment · Show 8 · 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 VozzeTheCreator · Apr 01, 2018 at 02:30 PM 0
Share

Thanks for the feedback! This is the current situation, breakpoints and unity debugger used and I changed my health$$anonymous$$anager (Thanks!): https://ibb.co/m8V2kS https://ibb.co/iCtoQS https://ibb.co/eWC4zn The item gets removed out of the list in the "objectDestroyed" class but this doesn't seem to affect the list in the game$$anonymous$$anager because unity happily keeps printing the gameObject in the debugger (See "Update"). I am completely lost now.

avatar image pako VozzeTheCreator · Apr 01, 2018 at 03:28 PM 0
Share

I can see the following from your screenshots:

  1. Before: when Game$$anonymous$$anagerScript starts there are 2 targets, namely redTarget6 & 7, and a Player (apparently also a target), inside the targetableObjectsList. Fine...

  2. While: at some point during the execution of the objectDestroyed(GameObject destroyedObject) method, and apparently when list = targetableObjects, the redTarget6 GameObject has been removed from the List, but it still appears in the Hierarchy. This is expected, since Destroy(gameObject, 10f) doesn't get called until objectDestroyed(gameObject) finishes, and it hasn't finished yet. Fine again.

  3. After: No breakpoints hit, meaning that objectDestroyed(GameObject destroyedObject) has finished executing, and Destroy(gameObject, 10f) called. However, redTarget6 is still in the hierarchy. Apparently the 10 seconds haven't yet passed. Also, as you say, redTarget6 is still showing in the console.


    doesn't seem to affect the list in the game$$anonymous$$anager because unity happily keeps printing the gameObject in the debugger (See Update)**

It seems to me that you mean (See **Fixed**Update), because as far as I can tell that's where you call:

          foreach (GameObject obj in targetableObjects) {
              Debug.Log(obj.name);
                  }
 

...and apparently that's how you "verify" that the destroyed object hasn't been removed from the list.

Well, you shouldn't be using FixedUpdate() for this purpose, because it's meant to be used for smooth physics motion, and it may be called several times per frame. You'd better use LateUpdate(), which is called after Update(), to make sure that all your code in Update() has finished running, before you verify the contents of the list.

Try that and see what happens.

avatar image VozzeTheCreator pako · Apr 01, 2018 at 04:40 PM 0
Share

Okay so I tried that. This are the results: https://ibb.co/kkq227 https://ibb.co/f07h27 https://ibb.co/dqUvN7 I also removed the 10 seconds from the health$$anonymous$$anager and it produced the exact same results (images are without).

Show more comments
avatar image VozzeTheCreator · Apr 02, 2018 at 04:19 PM 2
Share

Thanks you so much! I gave you all the points I had left.

avatar image pako VozzeTheCreator · Apr 02, 2018 at 04:35 PM 0
Share

You are welcome!

avatar image
1

Answer by meat5000 · Apr 01, 2018 at 05:10 PM

https://msdn.microsoft.com/en-us/library/bwabdf9z(v=vs.110).aspx

Looks like List has a built in ForEach method. Also I do believe that a List of Lists could be better structured as a Dictionary? Not sure, dont use em much. list.Remove(T) has a boolean return that lets you know if the item was found(and removed) or not present.

Comment
Add comment · Show 1 · 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 Harinezumi · Apr 02, 2018 at 01:35 PM 1
Share

No, a Dictionary would not help a lot with a List of Lists (the key would be the reference to the List, or its index, but List is already more efficient with that). Or maybe you are thinking of a more exotic use of Dictionaries? Or a HashSet?
However, you are right about using foreach with List, and also List.Remove(), which is safe to call even if the item is not present in the list; that is no need to use List.Contains().

avatar image
0

Answer by EpicAsMe · Mar 31, 2018 at 08:18 PM

I think you should remove list at index where it's contain/equal to that destroyedObject, I usualy using for loop for this kind of task. Maybe try this.

 public void objectDestroyed(GameObject destroyedObject)
     {
         foreach (List<GameObject> list in objectContainerLists)
         {
             for (int i = 0; i < list.Count; i++)
             {
                 // Check if list at index of i is equal to destroyedObject / list is containing destroyedObject
                 if (list[i] == destroyedObject)
                 {
                     list.Remove(list[i]);
                     Debug.Log("Removed" + destroyedObject.name + " from " + list);
                 }
             }
         }
     }




Comment
Add comment · Show 3 · 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 VozzeTheCreator · Mar 31, 2018 at 09:11 PM 0
Share

Thanks for the reply but sadly this code produces exactly the same result.

avatar image EpicAsMe VozzeTheCreator · Apr 01, 2018 at 02:03 AM 0
Share

I'm sorry if that didn't help, but in case I get your question wrong, did you want to remove the destroyed object from the list or from the scene?

avatar image VozzeTheCreator EpicAsMe · Apr 01, 2018 at 02:29 PM 0
Share

No you got it right it is my question and the code should do it only it doesn't.

avatar image
0

Answer by whisp · Apr 01, 2018 at 09:02 PM

The destroyed GameObject is possibly NULL. Try to remove it from the list right before you destroy it, instead afterwards.

Also "objectContainerLists" probably doesn't get serialized (because it's a generic list of generic lists and private)

Check with the debugger if the GameObject is null and if objectContainerLists contains any values.

If objectContainerLists is not serialized you can create a wrapper like this:

 [System.Serializable]
 public class ObjectContainer
 {
     public List<GameObject> Objects = new List<GameObject>();
 }

and then use it in your script instead of the List>:

 [SerializeField] List<ObjectContainer> objectContainerLists;


Comment
Add comment · 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

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

83 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 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 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 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 avatar image avatar image avatar image

Related Questions

A node in a childnode? 1 Answer

can add to a list but not remove 1 Answer

How to remove project from the Project Wizard 6 Answers

When removing something from a list it says missing 1 Answer

How do I make a list of lists? 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