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

Tilemap.GetTile Returns Asset and Not Instance

Discussion in 'Scripting' started by TuckerFlynn, Feb 16, 2020.

  1. TuckerFlynn

    TuckerFlynn

    Joined:
    May 18, 2015
    Posts:
    10
    I'm working on the building system for a 2D game using Unity's tilemap system. I use my own class of tiles which inherit from the TileBase class and have attached gameobjects, just like the built-in Tile class. All the tiles used in the game are created at runtime from a Json file, while the gameobjects are saved as prefabs and attached to tiles using Resources.Load

    The most common gameobjects are for resources that have a collider and a script that tracks durability. These work as expected, using OnTriggerEnter2D when the player hits the resource (ie. chopping a tree) to decrease the durability until the resource is destroyed.

    My problem is when I try to access the gameobject via its tile instead of through the script on the gameobject, the original prefab is accessed and not the instance. I've tested this by logging the instance ID of the script to confirm that when using GetTile, GetInstanceID returns the instance ID of the script on the prefab while from within OnTriggerEnter2D, GetInstanceID returns the instance ID of the script on the instantiated gameobject.

    Is this the expected behaviour? And if so, are there any recommendations for accessing the gameobject & script instance via the GetTile method?

    (I can add script excerpts if this needs extra clarification!)
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,971
    Pretty sure this is the problem: you just have to Instantiate those prefabs before assigning them to your tile class.

    The reason is that Resources.Load() does not "load" it in the usual sense of the word, as in read it into memory as a new instance. Resources.Load() actually just gets a reference to the on-disk (and in the editor, in-editor) asset. If you want a fresh instance, you must call Instantiate<>() on it before writing down the new reference.
     
  3. TuckerFlynn

    TuckerFlynn

    Joined:
    May 18, 2015
    Posts:
    10
    I had the impression from the documents that gameobjects attached to tiles were individually instantiated when the tiles are added to a tilemap, but that may not be the case. I came up with a functioning workaround that just uses a raycast under the mouse which gives me the gameobject instance and not asset, but I'll have to check if your suggestion will work better!
     
  4. TuckerFlynn

    TuckerFlynn

    Joined:
    May 18, 2015
    Posts:
    10
    Two months later the best I have come up with is a situation-specific workaround involving raycasts, which seems crude so I'm going to bump this post and add some code to see if I can get any further. Hopefully there's an unrelated error in my programming that someone else will spot! The issue isn't so much that using Resources.Load() points to the on-disk asset, but that this.gameobject and GetTile().gameobject are pointing to different instances.

    All tiles used in the game are generated from Json files & spritesheets, colliderType and sprites are also set but the important part is adding gameobjects.

    Code (CSharp):
    1. public static class TileLoader
    2. {
    3.      public static List<myTile> Tiles = new List<myTile>();
    4.  
    5.      // Original method of adding gameobjects:
    6.      myTile toAdd = ScriptableObject.CreateInstance<myTile>();
    7.      toAdd.gameobject = Resources.Load<GameObject>("someGameobject");
    8.      Tiles.Add(toAdd);
    9.    
    10.      // Have also tried:
    11.      myTile toAdd2 = ScriptableObject.CreateInstance<myTile>();
    12.      GameObject go = Object.Instantiate(Resources.Load<GameObject>("someGameobject"));
    13.      toAdd2.gameobject = go;
    14.      Tiles.Add(toAdd2);
    15. }
    Filling tilemaps with the generated tiles is straightforward, something like:

    Code (CSharp):
    1. public class MapCreator : Monobehaviour
    2. {
    3.      void Start ()
    4.      {
    5.           myTilemap.SetTile(Vector3Int.zero, TileLoader.Tiles[0]);
    6.      }
    7. }
    This is where I find my issue, if the gameobject "someGameobject" has the following simple script attached:

    Code (CSharp):
    1. public class TileScript : Monobehaviour
    2. {
    3.      void Start ()
    4.      {
    5.           int thisInstanceID = this.gameObject.GetInstanceID();
    6.           int tileInstanceID = myTilemap.GetTile<myTile>(Vector3Int.zero).gameobject.GetInstanceID();
    7.      }
    8. }
    According to the docs for the GetTile() method it "Gets the tile of type T at the given XYZ coordinates of a cell in the tile map." I had assumed the values of thisInstanceID and tileInstanceID would be the same because GetTile() should be returning the tile instantiated in MapCreator and therefore referring to the same gameobject. That is not the case though, GetTile() always gets the original asset and not the tile as it is in the tilemap. Neither of the two methods of setting the gameobject shown in the TileLoader code produce different results.

    Again maybe this is just the way it is meant to be, but to me it seems like a limitation if GetTile() can't be used to alter an individual tile, or the attached gameobject.
     
  5. netizen539

    netizen539

    Joined:
    Jul 28, 2018
    Posts:
    1