Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Setting a tile to null results in an error "Destroying object multiple times"

Discussion in 'Scripting' started by Dowlgrett, Sep 10, 2023.

  1. Dowlgrett

    Dowlgrett

    Joined:
    Feb 27, 2022
    Posts:
    4
    I spent quite a while on this problem and it's starting to bug me out. It's not crashing the game or anything, just a little annoying, but the basic gist of it is that I have a custom tile with associated game object:

    Code (CSharp):
    1. public class GrassTile : TileBase
    2. {
    3.     public GameObject tileGameObject;
    4.     public Sprite sprite;
    5.  
    6.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    7.     {
    8.         tileData.gameObject = tileGameObject;
    9.         tileData.sprite = sprite;
    10.     }
    11. }

    So far so good, and here's the script for the tile object itself:

    Code (CSharp):
    1. public class GrassTileObject : MonoBehaviour
    2. {
    3.     private GameObject _objectOnTile;
    4.     private Tilemap _tilemap;
    5.  
    6.     private void Start()
    7.     {
    8.         _tilemap = FindObjectOfType<Tilemap>();
    9.     }
    10.     public GameObject ObjectOnTile => _objectOnTile;
    11.     public void SetObjectOnTile(GameObject objectOnTile)
    12.     {
    13.         _objectOnTile = objectOnTile;
    14.     }
    15.     private void OnDestroy()
    16.     {  
    17.         _tilemap.SetTile(_tilemap.WorldToCell(transform.position), null);    
    18.     }
    19. }
    OnDestroy() is triggered when the field _health is <= 0:

    Code (CSharp):
    1. public class Health : MonoBehaviour
    2. {
    3.     [SerializeField] private int _health;
    4.     public int HealthPoints => _health;
    5.  
    6.     public void TakeDamage(int damage)
    7.     {
    8.         _health -= damage;
    9.         if (_health <= 0)
    10.         {
    11.             EventManager.Instance.TriggerEntityDiedEvent(GetComponent<Entity>());
    12.             Destroy(gameObject);
    13.         }
    14.     }
    15. }

    Now the problem is that for some reason whenever the associated tile object is destroyed, Unity is insisting that I'm destroying the object multiple times:

    Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.
    UnityEngine.Tilemaps.Tilemap:SetTile (UnityEngine.Vector3Int,UnityEngine.Tilemaps.TileBase)
    GrassTileObject:OnDestroy () (at Assets/Scripts/GrassTileObject.cs:27)

    If I comment out the "_tilemap.SetTile(_tilemap.WorldToCell(transform.position), null);" line, the error is gone. I'm thinking the reason could be that when the tile is set to null, the associated game object is destroyed, but I couldn't find any information on that, so I'm just blind guessing.

    Any ideas on how to fix this?
     
  2. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    This is a declaration for the script(component) of "Tilemap".

    here you only ask for the gameObject component, not it's script.

    I would assume this should fix your error:
    _tilemap = FindObjectOfType<Tilemap>().GetComponent<TileMap>();


    However you should have a better way of getting this object, than to use find object of type, but first things first, just fix your error. :)
     
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,160
    Uh, no,
    FindObjectOfType<T>
    returns the first instance of a component found in the scene, and has nothing to do with this error.

    @Dowlgrett I would check if the event is being raised multiple times.
     
  4. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,526
    Destroy doesn't destroy the object immediately, it puts it in a queue to be destroyed at the end of the frame. In Unity, that means that managed pointers/references to the object don't get set to null straight away, other objects can still find it via GetComponent() et all, and so on.

    My first guess would be that it's taking damage multiple times at once, resulting in Destroy getting called multiple times. If so, an easy way to get around it could be to early exit if health is already <= 0.
     
    spiney199 likes this.
  5. Dowlgrett

    Dowlgrett

    Joined:
    Feb 27, 2022
    Posts:
    4
    I tried changing Health component a bit like so, but still it gives me the same error:
    Code (CSharp):
    1.  
    2. private bool isDestroyed;
    3.  
    4.     public void TakeDamage(int damage)
    5.     {
    6.         _health -= damage;
    7.  
    8.         if (_health <= 0 && !isDestroyed)
    9.         {
    10.             EventManager.Instance.TriggerEntityDiedEvent(GetComponent<Entity>());
    11.             Destroy(gameObject);
    12.             isDestroyed = true;
    13.         }
    14.     }
     
    Last edited: Sep 10, 2023
  6. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,526
    That was just my first guess, we don't have any details about what's going on.

    Do you have any other calls to Destroy() in your code? I'd put break points on them and see when they're getting called for what.