Search Unity

How to add behaviors to a TileAsset

Discussion in '2D' started by TheLurkingDev, Apr 10, 2018.

  1. TheLurkingDev

    TheLurkingDev

    Joined:
    Nov 16, 2009
    Posts:
    91
    I need to add additional logic to TileAssets, mainly to track the terrain type. I can add a TileMapCollider2D to a TileMap and prevent a sprite from moving through the tile, but if I set the TileMapCollider2D to IsTriggerr and add a script component that handles OnTriggerEnter2D, I am getting nothing.

    Of course, I can accomplish this by creating a prefab and painting with the prefab brush, but this is hardly ideal as I can't just click within the TilePalette to select a new prefab where a preview of what it looks like would be.

    How can I do this?
     
  2. furroy

    furroy

    Joined:
    Feb 24, 2017
    Posts:
    93
    You can make your own subclass of TileBase if you want to add additional functionality to tiles. I too wanted to store the terrain type to later know what sounds to play when a character walked over each tile. However, I totally cheated and just set tile.name = "grass" to avoid making my own class for now. :)
     
    TheLurkingDev likes this.
  3. TheLurkingDev

    TheLurkingDev

    Joined:
    Nov 16, 2009
    Posts:
    91
    Thank you - I will try to wire that up at some point tomorrow during working hours and see if it will work. Another alternative that I found while poking around the 2D Extras GitHub package is to change the PrefabBrush in that repository. Currently it basically takes an array of gameobjects and randomly selects a prefab to paint into the grid. This would be easy enough to change and have it place the desired prefabs.
     
  4. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    I use a Prefab tile object. You can simply assign the prefab to the tile object

    This should work by inheriting from Tile or TileBase instead of AdvancedTile.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3.  
    4. // ReSharper disable once CheckNamespace
    5. public class PrefabTile : AdvancedTile
    6. {
    7.     public Sprite TileSprite;
    8.     public GameObject TileAssociatedPrefab;
    9.  
    10.     public float PrefabLocalOffset = 0.5f;
    11.     public float prefabZOffset = -1f;
    12.  
    13.     public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    14.     {
    15.  
    16. //This prevents rogue prefab objects from appearing when the Tile palette is present
    17. #if UNITY_EDITOR
    18.         if (go != null)
    19.         {
    20.             if (go.scene.name == null)
    21.             {
    22.                 DestroyImmediate(go);
    23.             }
    24.         }
    25. #endif
    26.  
    27.         if (go != null)
    28.         {
    29.             //Modify position of GO to match middle of Tile sprite
    30.             go.transform.position = new Vector3(position.x + PrefabLocalOffset
    31.                 , position.y + PrefabLocalOffset
    32.                 , prefabZOffset);
    33.  
    34.         }
    35.  
    36.         return true;
    37.     }
    38.  
    39.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    40.     {
    41.         tileData.sprite = TileSprite;
    42.  
    43.         if (TileAssociatedPrefab && tileData.gameObject==null)
    44.         {
    45.             tileData.gameObject = TileAssociatedPrefab;
    46.         }
    47.     }
    48. }
     
  5. TheLurkingDev

    TheLurkingDev

    Joined:
    Nov 16, 2009
    Posts:
    91
    Thank you thank you thank you!

    Yes, this works perfectly! I did modify it slightly so that it can be placed under the Assets menu in the Editor. It also inherits from TileBase as you suggested.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3. #if UNITY_EDITOR
    4. using UnityEditor;
    5. #endif
    6.  
    7. public class PrefabTile : UnityEngine.Tilemaps.TileBase
    8. {
    9.     public Sprite TileSprite;
    10.     public GameObject TileAssociatedPrefab;
    11.  
    12.     public float PrefabLocalOffset = 0.5f;
    13.     public float prefabZOffset = -1f;
    14.  
    15.     public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    16.     {
    17.  
    18.         //This prevents rogue prefab objects from appearing when the Tile palette is present
    19. #if UNITY_EDITOR
    20.         if (go != null)
    21.         {
    22.             if (go.scene.name == null)
    23.             {
    24.                 DestroyImmediate(go);
    25.             }
    26.         }
    27. #endif
    28.  
    29.         if (go != null)
    30.         {
    31.             //Modify position of GO to match middle of Tile sprite
    32.             go.transform.position = new Vector3(position.x + PrefabLocalOffset
    33.                 , position.y + PrefabLocalOffset
    34.                 , prefabZOffset);
    35.  
    36.         }
    37.  
    38.         return true;
    39.     }
    40.  
    41. #if UNITY_EDITOR
    42.     [MenuItem("Assets/Create/Prefab Tile")]
    43.     public static void CreatePrefabTiles()
    44.     {
    45.         string path = EditorUtility.SaveFilePanelInProject("Save Prefab Tile", "New Prefab Tile", "asset", "Save Prefab Tile", "Assets");
    46.  
    47.         if (path == "")
    48.             return;
    49.  
    50.         AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<PrefabTile>(), path);
    51.     }
    52. #endif
    53.  
    54.  
    55. public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    56.     {
    57.         tileData.sprite = TileSprite;
    58.  
    59.         if (TileAssociatedPrefab && tileData.gameObject == null)
    60.         {
    61.             tileData.gameObject = TileAssociatedPrefab;
    62.         }
    63.     }
    64. }
     
  6. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    No problem. Glad I could help.

    Edit: Found a way to get RenderStaticPreview to work. This is a small modification of the existing RuleTileEditor code in 2D Extras.

    IEditorPreviewTile.cs (Should be in an Editor folder)
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public interface IEditorPreviewTile
    4. {
    5.     Sprite GetEditorPreviewSprite();
    6. }
    Sample editor using this code:
    Code (CSharp):
    1. using System;
    2. using System.Reflection;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using Object = UnityEngine.Object;
    6.  
    7. [CustomEditor(typeof(PrefabTile))]
    8. public class PrefabTileEditor : UnityEditor.Editor, IEditorPreviewTile
    9. {
    10.     private PrefabTile Tile { get { return (target as PrefabTile); } }
    11.  
    12.     public override void OnInspectorGUI()
    13.     {
    14.         AdvancedTileEditor.OnCustomInspectorGUI(Tile);
    15.  
    16.         EditorGUI.BeginChangeCheck();
    17.  
    18.         Tile.PrefabLocalXYOffset = EditorGUILayout.FloatField("Prefab Local XY Offset", Tile.PrefabLocalXYOffset);
    19.         Tile.PrefabLocalZOffset = EditorGUILayout.FloatField("Prefab Local Z Offset", Tile.PrefabLocalZOffset);
    20.         Tile.UseAbsoluteZOffset = EditorGUILayout.Toggle("Use Absolute Z Offset", Tile.UseAbsoluteZOffset);
    21.         Tile.PrefabAbsoluteZOffset = EditorGUILayout.FloatField("Prefab Absolute Z Offset", Tile.PrefabAbsoluteZOffset);
    22.  
    23.         Tile.TileSprite = (Sprite) EditorGUILayout.ObjectField("Sprite", Tile.TileSprite, typeof(Sprite), false, null);
    24.  
    25.         Tile.TileAssociatedPrefab =
    26.             (GameObject) EditorGUILayout.ObjectField("Prefab", Tile.TileAssociatedPrefab, typeof(GameObject), false);
    27.  
    28.         if (EditorGUI.EndChangeCheck())
    29.             EditorUtility.SetDirty(Tile);
    30.     }
    31.  
    32.     //For Static Preview - Start Here
    33.     public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
    34.     {
    35.         if (GetEditorPreviewSprite() != null)
    36.         {
    37.             Type t = GetType("UnityEditor.SpriteUtility");
    38.             if (t != null)
    39.             {
    40.                 MethodInfo method = t.GetMethod("RenderStaticPreview", new[] { typeof(Sprite), typeof(Color), typeof(int), typeof(int) });
    41.                 if (method != null)
    42.                 {
    43.                     object ret = method.Invoke("RenderStaticPreview", new object[] { GetEditorPreviewSprite(), Color.white, width, height });
    44.                     if (ret is Texture2D)
    45.                     {
    46.                         return ret as Texture2D;
    47.                     }
    48.                 }
    49.             }
    50.         }
    51.         return base.RenderStaticPreview(assetPath, subAssets, width, height);
    52.     }
    53.  
    54.     private static Type GetType(string typeName)
    55.     {
    56.         var type = Type.GetType(typeName);
    57.         if (type != null)
    58.             return type;
    59.  
    60.         if (typeName.Contains("."))
    61.         {
    62.             var assemblyName = typeName.Substring(0, typeName.IndexOf('.'));
    63.             var assembly = Assembly.Load(assemblyName);
    64.             if (assembly == null)
    65.                 return null;
    66.             type = assembly.GetType(typeName);
    67.             if (type != null)
    68.                 return type;
    69.         }
    70.  
    71.         var currentAssembly = Assembly.GetExecutingAssembly();
    72.         var referencedAssemblies = currentAssembly.GetReferencedAssemblies();
    73.         foreach (var assemblyName in referencedAssemblies)
    74.         {
    75.             var assembly = Assembly.Load(assemblyName);
    76.             if (assembly != null)
    77.             {
    78.                 type = assembly.GetType(typeName);
    79.                 if (type != null)
    80.                     return type;
    81.             }
    82.         }
    83.         return null;
    84.     }
    85.     //Static Preview - End Here
    86.  
    87.     public Sprite GetEditorPreviewSprite()
    88.     {
    89.         return Tile.TileSprite;
    90.     }
    91. }
     
    Last edited: Apr 29, 2018
  7. MatthiasStuetzer

    MatthiasStuetzer

    Joined:
    Oct 5, 2016
    Posts:
    17
    Thank you !!
     
    spryx likes this.
  8. luispedrofonseca

    luispedrofonseca

    Joined:
    Aug 29, 2012
    Posts:
    945
    @spryx Thanks for your code. I've just started looking at the Tilemap system and it seems incredible to me that this functionality is not built-in.

    Any how, your code works great but the sprite also gets rendered together with the prefab. Is there a way to render the sprite only on the Tile Palette window and not on the actual game (without disabling the TilemapRenderer)?

    Also, I'd like to be able to see and edit the prefab on the scene view once it's added. Currently it stays hidden on the hierarchy under the Tilemap GameObject.

    Thanks in advance.
     
    spryx likes this.
  9. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    The following is an updated version of the prefabTile. This is kind of a hacky workaround as the tile sprite is removed at runtime, but it works. If you want to separate the prefab instance (you may be required to do this - having a prefab tile effectively makes the prefab dependant on the tile "instance".), I suggest doing this in a custom tile brush (See included custom brush as an example of this - this is not required for the prefab tile).

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3. using UnityEngine.Tilemaps;
    4.  
    5. // ReSharper disable once CheckNamespace
    6. [CreateAssetMenu]
    7. public class PrefabTile : AdvancedTile
    8. {
    9.     public Sprite TileSprite;
    10.     public GameObject TileAssociatedPrefab;
    11.  
    12.     public float PrefabLocalZOffset = 0f;
    13.  
    14.     public bool UseAbsoluteZOffset = false;
    15.     public float PrefabAbsoluteZOffset = 0f;
    16.  
    17.     public bool TileIsEditorObjectOnly = false;
    18.  
    19.     public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    20.     {
    21.  
    22.         //This prevents rogue prefab objects from appearing when the Tile palette is present
    23. #if UNITY_EDITOR
    24.         if (go != null)
    25.         {
    26.             if (go.scene.name == null || go.scene.name == "Preview Scene")
    27.             {
    28.                 DestroyImmediate(go);
    29.             }
    30.         }
    31. #endif
    32.  
    33.         if (go != null)
    34.         {
    35.             if (UseAbsoluteZOffset)
    36.             {
    37.                 //Modify position of GO to match middle of Tile sprite
    38.                 go.transform.position = new Vector3(position.x + Globals.PrefabXyOffset
    39.                     , position.y + Globals.PrefabXyOffset
    40.                     , PrefabAbsoluteZOffset);
    41.  
    42.                 return true;
    43.             }
    44.  
    45.             //Modify position of GO to match middle of Tile sprite
    46.             go.transform.position = new Vector3(position.x + Globals.PrefabXyOffset
    47.                 , position.y + Globals.PrefabXyOffset
    48.                 , position.z);
    49.  
    50.             //Set Z
    51.             go.transform.localPosition =
    52.                 new Vector3(go.transform.localPosition.x, go.transform.localPosition.y, PrefabLocalZOffset);
    53.  
    54.         }
    55.  
    56.         return true;
    57.     }
    58.  
    59. #if UNITY_EDITOR
    60.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    61.     {
    62.         //Always show sprite when we are not running
    63.         if (!Application.isPlaying)
    64.             tileData.sprite = TileSprite;
    65.         else
    66.         {
    67.             //Remove sprite if we intend the sprite to be in-editor only!
    68.             tileData.sprite = TileIsEditorObjectOnly ? tileData.sprite : TileSprite;
    69.         }
    70.  
    71.         if (TileAssociatedPrefab && tileData.gameObject == null)
    72.         {
    73.             tileData.gameObject = TileAssociatedPrefab;
    74.         }
    75.  
    76.         tileData.flags = TileFlags.InstantiateGameObjectRuntimeOnly;
    77.     }
    78. #endif
    79.  
    80. #if !UNITY_EDITOR
    81. public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    82.     {
    83.         //Remove sprite if we intend the sprite to be in-editor only!
    84.         tileData.sprite = TileIsEditorObjectOnly ? tileData.sprite : TileSprite;
    85.  
    86.         if (TileAssociatedPrefab && tileData.gameObject == null)
    87.         {
    88.             tileData.gameObject = TileAssociatedPrefab;
    89.         }
    90.     }
    91. #endif
    92. }
    Code (CSharp):
    1. using System;
    2. using System.Reflection;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using Object = UnityEngine.Object;
    6.  
    7. [CustomEditor(typeof(PrefabTile))]
    8. public class PrefabTileEditor : UnityEditor.Editor, IEditorPreviewsTiles
    9. {
    10.     private PrefabTile Tile { get { return (target as PrefabTile); } }
    11.  
    12.     public override void OnInspectorGUI()
    13.     {
    14.         AdvancedTileEditor.OnCustomInspectorGUI(Tile);
    15.  
    16.         EditorGUI.BeginChangeCheck();
    17.  
    18.         Tile.TileIsEditorObjectOnly = EditorGUILayout.Toggle("Tile Used In Editor Only", Tile.TileIsEditorObjectOnly);
    19.         Tile.PrefabLocalZOffset = EditorGUILayout.FloatField("Prefab Local Z Offset", Tile.PrefabLocalZOffset);
    20.         Tile.UseAbsoluteZOffset = EditorGUILayout.Toggle("Use Absolute Z Offset", Tile.UseAbsoluteZOffset);
    21.         Tile.PrefabAbsoluteZOffset = EditorGUILayout.FloatField("Prefab Absolute Z Offset", Tile.PrefabAbsoluteZOffset);
    22.  
    23.         Tile.TileSprite = (Sprite) EditorGUILayout.ObjectField("Sprite", Tile.TileSprite, typeof(Sprite), false, null);
    24.  
    25.         Tile.TileAssociatedPrefab =
    26.             (GameObject) EditorGUILayout.ObjectField("Prefab", Tile.TileAssociatedPrefab, typeof(GameObject), false);
    27.  
    28.         if (EditorGUI.EndChangeCheck())
    29.             EditorUtility.SetDirty(Tile);
    30.     }
    31.  
    32.     //For Static Preview - Start Here
    33.     public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
    34.     {
    35.         if (GetEditorPreviewSprite() != null)
    36.         {
    37.             Type t = GetType("UnityEditor.SpriteUtility");
    38.             if (t != null)
    39.             {
    40.                 MethodInfo method = t.GetMethod("RenderStaticPreview", new[] { typeof(Sprite), typeof(Color), typeof(int), typeof(int) });
    41.                 if (method != null)
    42.                 {
    43.                     object ret = method.Invoke("RenderStaticPreview", new object[] { GetEditorPreviewSprite(), Color.white, width, height });
    44.                     if (ret is Texture2D)
    45.                     {
    46.                         return ret as Texture2D;
    47.                     }
    48.                 }
    49.             }
    50.         }
    51.         return base.RenderStaticPreview(assetPath, subAssets, width, height);
    52.     }
    53.  
    54.     public Type GetType(string typeName)
    55.     {
    56.         var type = Type.GetType(typeName);
    57.         if (type != null)
    58.             return type;
    59.  
    60.         if (typeName.Contains("."))
    61.         {
    62.             var assemblyName = typeName.Substring(0, typeName.IndexOf('.'));
    63.             var assembly = Assembly.Load(assemblyName);
    64.             if (assembly == null)
    65.                 return null;
    66.             type = assembly.GetType(typeName);
    67.             if (type != null)
    68.                 return type;
    69.         }
    70.  
    71.         var currentAssembly = Assembly.GetExecutingAssembly();
    72.         var referencedAssemblies = currentAssembly.GetReferencedAssemblies();
    73.         foreach (var assemblyName in referencedAssemblies)
    74.         {
    75.             var assembly = Assembly.Load(assemblyName);
    76.             if (assembly != null)
    77.             {
    78.                 type = assembly.GetType(typeName);
    79.                 if (type != null)
    80.                     return type;
    81.             }
    82.         }
    83.         return null;
    84.     }
    85.     //Static Preview - End Here
    86.  
    87.     public Sprite GetEditorPreviewSprite()
    88.     {
    89.         return Tile.TileSprite;
    90.     }
    91. }
    Code (CSharp):
    1. using System;
    2. using System.Reflection;
    3. using UnityEngine.Tilemaps;
    4. using Object = UnityEngine.Object;
    5.  
    6. public class AdvancedTile : AdvancedTileBase {
    7. }
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3.  
    4. public abstract class AdvancedTileBase : TileBase
    5. {
    6.     public bool hasCollider = false;
    7.     public bool colliderDoesNotCastShadows = false;
    8. }
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomEditor(typeof(AdvancedTile), true)]
    5. public class AdvancedTileEditor : UnityEditor.Editor
    6. {
    7.     public static void OnCustomInspectorGUI(AdvancedTile advancedTileObject)
    8.     {
    9.         EditorGUI.BeginChangeCheck();
    10.         advancedTileObject.hasCollider = EditorGUILayout.Toggle("Has Collider", advancedTileObject.hasCollider);
    11.         advancedTileObject.colliderDoesNotCastShadows = EditorGUILayout.Toggle("Doesn't Cast Shadows", advancedTileObject.colliderDoesNotCastShadows);
    12.  
    13.         // (Sprite)EditorGUILayout.ObjectField("Sprite " + (i + 1), tile.m_AnimatedSprites[i], typeof(Sprite), false, null);
    14.         //advancedTileObject.editorSprite = (Sprite)EditorGUILayout.ObjectField("Editor Sprite", advancedTileObject.editorSprite, typeof(Sprite), false, null);
    15.  
    16.         if (EditorGUI.EndChangeCheck())
    17.         {
    18.             Debug.Log("Modified advanced tile");
    19.             EditorUtility.SetDirty(advancedTileObject);
    20.         }
    21.     }
    22. }
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using Object = UnityEngine.Object;
    4.  
    5. public interface IEditorPreviewsTiles
    6. {
    7.     Sprite GetEditorPreviewSprite();
    8.     Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height);
    9.     Type GetType(string typeName);
    10. }
    Code (CSharp):
    1. #if UNITY_EDITOR
    2.  
    3. using System;
    4. using Extensions;
    5. using Tiles;
    6. using UnityEditor;
    7. using UnityEngine;
    8. using UnityEngine.Tilemaps;
    9.  
    10.  
    11. [CustomGridBrush(true, true, true, "DataTile Brush")]
    12. public class DataTileBrush : GridBrush
    13. {
    14.     public override void Paint(GridLayout gridLayout, GameObject brushTarget, Vector3Int position)
    15.     {
    16.         //Determine if the tile is editor only
    17.         //PrefabTile prefabTileRef = cells[0].tile as PrefabTile;
    18.         //bool tileIsEditorOnly = prefabTileRef != null && prefabTileRef.TileIsEditorObjectOnly;
    19.  
    20.         //Paint the TileBase first if it is not an editor only tile
    21.         //if (!tileIsEditorOnly)
    22.         //{
    23.             base.Paint(gridLayout, brushTarget, position);
    24.         //}
    25.  
    26.         //Determine if the grid selection is in the palette or is part of the scene.
    27.         bool tileWasPaintedInScene = brushTarget.gameObject.scene.name != null;
    28.  
    29.         //Get Selection Tilemap
    30.         Tilemap paintTilemap = brushTarget.GetComponent<Tilemap>();
    31.  
    32.         //Get GridInformation
    33.         GridInformation paintGridInfo = paintTilemap.gameObject.GetComponent<GridInformation>();
    34.  
    35.         //Get the TileBase associated with the painted object
    36.         TileBase paintTile = paintTilemap.GetTile(position);
    37.  
    38.         //If Painting occurs in scene, Erase all current TileBase properties
    39.         if (tileWasPaintedInScene)
    40.         {
    41.             //Erase all properties at position
    42.             paintGridInfo.EraseAllPositionPropertiesAtPosition(position);
    43.  
    44.             //Check if TileBase contains properties
    45.             var tileInfo = paintTile as IDataTile;
    46.  
    47.             //If TileBase contains properties, copy properties to the TileBase
    48.             if (tileInfo != null)
    49.             {
    50.                 foreach (var prop in tileInfo.GetDataTileProperties())
    51.                 {
    52.                     switch (Globals.TypeCodes[prop.PropertyType])
    53.                     {
    54.                         case 0:
    55.                             paintGridInfo.SetPositionProperty(position, prop.Name, (string)prop.GetValue(tileInfo, null));
    56.                             break;
    57.                         case 1:
    58.                             paintGridInfo.SetPositionProperty(position, prop.Name, (int)prop.GetValue(tileInfo, null));
    59.                             break;
    60.                         case 2:
    61.                             paintGridInfo.SetPositionProperty(position, prop.Name, (float)prop.GetValue(tileInfo, null));
    62.                             break;
    63.                         case 3:
    64.                             paintGridInfo.SetPositionProperty(position, prop.Name, (double)prop.GetValue(tileInfo, null));
    65.                             break;
    66.                         case 4:
    67.                             paintGridInfo.SetPositionProperty(position, prop.Name, (Color)prop.GetValue(tileInfo, null));
    68.                             break;
    69.                         case 5:
    70.                             paintGridInfo.SetPositionProperty(position, prop.Name,
    71.                                 (UnityEngine.Object)prop.GetValue(tileInfo, null));
    72.                             break;
    73.                         case 6:
    74.                             paintGridInfo.SetPositionProperty(position, prop.Name, (bool)prop.GetValue(tileInfo, null));
    75.                             break;
    76.                     }
    77.                 }
    78.             }
    79.  
    80.             ////Instantiate Objects on tilemap
    81.             //Debug.Log("PFtilref: " + prefabTileRef?.name + " isEonly: " + tileIsEditorOnly);
    82.             //if (prefabTileRef != null && tileIsEditorOnly)
    83.             //{
    84.             //    if (prefabTileRef.TileAssociatedPrefab != null)
    85.             //    {
    86.             //        Debug.Log("Trying to instantiate prefab");
    87.             //        Instantiate(prefabTileRef.TileAssociatedPrefab, position.Fixed3DPosition(),Quaternion.identity,paintTilemap.transform);
    88.             //    }
    89.             //}
    90.         }
    91.     }
    92.  
    93.     public override void Erase(GridLayout gridLayout, GameObject brushTarget, Vector3Int position)
    94.     {
    95.         //Get Selection Tilemap
    96.         Tilemap paintTilemap = brushTarget.GetComponent<Tilemap>();
    97.  
    98.         //Get GridInformation
    99.         GridInformation paintGridInfo = paintTilemap.gameObject.GetComponent<GridInformation>();
    100.  
    101.         if (paintGridInfo)
    102.         {
    103.             //Erase all properties at position
    104.             paintGridInfo.EraseAllPositionPropertiesAtPosition(position);
    105.         }
    106.  
    107.         //Erase Tile
    108.         base.Erase(gridLayout, brushTarget, position);
    109.     }
    110.  
    111.     //Prevent moving tiles until we can find a good way to do it. (i.e. I'm too lazy to move IDataTile Properties)
    112.     public override void Move(GridLayout gridLayout, GameObject brushTarget, BoundsInt from, BoundsInt to)
    113.     {
    114.         throw new NotImplementedException();
    115.     }
    116.  
    117.     public override void MoveStart(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
    118.     {
    119.         throw new NotImplementedException();
    120.     }
    121.  
    122.     public override void MoveEnd(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
    123.     {
    124.         throw new NotImplementedException();
    125.     }
    126. }
    127.  
    128. [CustomEditor(typeof(DataTileBrush))]
    129. public class DataTileBrushEditor : GridBrushEditor
    130. {
    131.     public override void OnSelectionInspectorGUI()
    132.     {
    133.         //Prevents errors when leaving VS
    134.         if (!GridSelection.active || target == null)
    135.         {
    136.             return;
    137.         }
    138.  
    139.         //Guistyle for informational text
    140.         GUIStyle infoGuiStyle = new GUIStyle
    141.         {
    142.             normal = new GUIStyleState
    143.             {
    144.                 textColor = Color.white
    145.             },
    146.             alignment = TextAnchor.UpperCenter
    147.         };
    148.  
    149.         //Get selection position
    150.         Vector3Int selectionPosition = GridSelection.position.position;
    151.  
    152.         //Get Selection Tilemap
    153.         Tilemap selectionTilemap = GridSelection.target.GetComponent<Tilemap>();
    154.  
    155.         //Get GridInformation
    156.         GridInformation gridInfo = selectionTilemap.gameObject.GetComponent<GridInformation>();
    157.  
    158.         //Determine if the grid selection is in the palette or is part of the scene.
    159.         bool selectionIsInScene = GridSelection.grid.gameObject.scene.name != null;
    160.  
    161.         #region Tile Palette
    162.  
    163.         //Stop and do other things when selection is part of TileBase palette
    164.         if (!selectionIsInScene)
    165.         {
    166.             GUILayout.Label("Palette Options:");
    167.  
    168.             TileBase selectedPaletteTile = selectionTilemap.GetTile(selectionPosition);
    169.  
    170.             if (selectedPaletteTile == null)
    171.             {
    172.                 GUILayout.Label("Selection: - N/A -");
    173.             }
    174.             else
    175.             {
    176.                 //Tile has been selected
    177.                 GUILayout.Label("Selection: " + selectedPaletteTile.name);
    178.  
    179.                 //Check for TileInfo
    180.                 var tileInfo = selectedPaletteTile as IDataTile;
    181.                 if (tileInfo != null)
    182.                 {
    183.                     GUILayout.Label("-------------------- Properties --------------------", infoGuiStyle);
    184.  
    185.                     var propIndex = 1;
    186.                     foreach (var prop in tileInfo.GetDataTileProperties())
    187.                     {
    188.                         //Handle each TileBase of property
    189.                         switch (Globals.TypeCodes[prop.PropertyType])
    190.                         {
    191.                             //String
    192.                             case 0:
    193.                                 prop.SetValue(
    194.                                     tileInfo,
    195.                                     EditorGUILayout.TextField(
    196.                                         propIndex + ". " + prop.Name,
    197.                                         (string)prop.GetValue(tileInfo, null)),
    198.                                     null
    199.                                 );
    200.                                 break;
    201.                             //Int
    202.                             case 1:
    203.                                 prop.SetValue(
    204.                                     tileInfo,
    205.                                     EditorGUILayout.IntField(
    206.                                         propIndex + ". " + prop.Name,
    207.                                         (int)prop.GetValue(tileInfo, null)),
    208.                                     null
    209.                                 );
    210.                                 break;
    211.                             //Float
    212.                             case 2:
    213.                                 prop.SetValue(
    214.                                     tileInfo,
    215.                                     EditorGUILayout.FloatField(
    216.                                         propIndex + ". " + prop.Name,
    217.                                         (float)prop.GetValue(tileInfo, null)),
    218.                                     null
    219.                                 );
    220.                                 break;
    221.                             //Double
    222.                             case 3:
    223.                                 prop.SetValue(
    224.                                     tileInfo,
    225.                                     EditorGUILayout.DoubleField(
    226.                                         propIndex + ". " + prop.Name,
    227.                                         (double)prop.GetValue(tileInfo, null)),
    228.                                     null
    229.                                 );
    230.                                 break;
    231.                             //Color
    232.                             case 4:
    233.                                 prop.SetValue(
    234.                                     tileInfo,
    235.                                     EditorGUILayout.ColorField(
    236.                                         propIndex + ". " + prop.Name,
    237.                                         (Color)prop.GetValue(tileInfo, null)),
    238.                                     null
    239.                                 );
    240.                                 break;
    241.                             //Object
    242.                             case 5:
    243.                                 prop.SetValue(
    244.                                     tileInfo,
    245.                                     EditorGUILayout.ObjectField(
    246.                                         propIndex + ". " + prop.Name,
    247.                                         (UnityEngine.Object)prop.GetValue(tileInfo, null),
    248.                                         prop.PropertyType,
    249.                                         true
    250.                                         ),
    251.                                     null
    252.                                 );
    253.                                 break;
    254.                             //Bool
    255.                             case 6:
    256.                                 prop.SetValue(
    257.                                     tileInfo,
    258.                                     EditorGUILayout.Toggle(
    259.                                         propIndex + ". " + prop.Name,
    260.                                         (bool)prop.GetValue(tileInfo, null)),
    261.                                     null
    262.                                 );
    263.                                 break;
    264.                             default:
    265.                                 GUILayout.Label(propIndex + ". " + "Unknown Property Type");
    266.                                 GUILayout.Label("\tName: " + prop.Name);
    267.                                 GUILayout.Label("\tType: " + prop.PropertyType);
    268.                                 break;
    269.                         }
    270.                         //Increment Property Index
    271.                         propIndex++;
    272.                     }
    273.                 }
    274.                 else
    275.                 {
    276.                     GUILayout.Label("Not an info TileBase");
    277.                 }
    278.  
    279.             }
    280.             return;
    281.         }
    282.  
    283.         #endregion Tile Palette
    284.  
    285.         #region Scene
    286.  
    287.         //Selection is in the scene
    288.         GUILayout.Label("Scene Options:");
    289.  
    290.         //Get TileBase associated with selection
    291.         TileBase selectedTile = selectionTilemap.GetTile(selectionPosition);
    292.  
    293.         if (selectedTile == null)
    294.         {
    295.             GUILayout.Label("Selection: - N/A -");
    296.         }
    297.         else
    298.         {
    299.             //Selected TileBase is in a scene tilemap and is not null
    300.             GUILayout.Label("Selection: " + selectedTile.name);
    301.         }
    302.  
    303.         //Check if position has properties
    304.         if (gridInfo.PositionHasProperties(selectionPosition))
    305.         {
    306.             //Check if selected TileBase is a dataTile
    307.             IDataTile tileTemplateData = selectedTile as IDataTile;
    308.  
    309.             if (tileTemplateData != null)
    310.             {
    311.                 var propIndex = 1;
    312.                 foreach (var prop in tileTemplateData.GetDataTileProperties())
    313.                 {
    314.                     int propertyCode = Globals.TypeCodes[prop.PropertyType];
    315.                     switch (propertyCode)
    316.                     {
    317.                         //String
    318.                         case 0:
    319.                             EditorGUILayout.LabelField(propIndex + ". " + prop.Name + " : " + gridInfo.GetPositionProperty(selectionPosition, prop.Name, "-1"));
    320.                             break;
    321.                         //Int
    322.                         case 1:
    323.                             EditorGUILayout.LabelField(propIndex + ". " + prop.Name + " : " + gridInfo.GetPositionProperty(selectionPosition, prop.Name, -1));
    324.                             break;
    325.                         //Float
    326.                         case 2:
    327.                             EditorGUILayout.LabelField(propIndex + ". " + prop.Name + " : " + gridInfo.GetPositionProperty(selectionPosition, prop.Name, -1f));
    328.                             break;
    329.                         //Double
    330.                         case 3:
    331.                             EditorGUILayout.LabelField(propIndex + ". " + prop.Name + " : " + gridInfo.GetPositionProperty(selectionPosition, prop.Name, -1.0d));
    332.                             break;
    333.                         //Color
    334.                         case 4:
    335.                             EditorGUILayout.LabelField(propIndex + ". " + prop.Name + " : " + gridInfo.GetPositionProperty(selectionPosition, prop.Name, new Color(-1f, -1f, -1f)));
    336.                             break;
    337.                         //Object
    338.                         case 5:
    339.                             EditorGUILayout.LabelField(propIndex + ". " + prop.Name + " : " + gridInfo.GetPositionProperty(selectionPosition, prop.Name, new UnityEngine.Object()));
    340.                             break;
    341.                         //Bool
    342.                         case 6:
    343.                             EditorGUILayout.LabelField(propIndex + ". " + prop.Name + " : " + gridInfo.GetPositionProperty(selectionPosition, prop.Name, false));
    344.                             break;
    345.                         default:
    346.                             GUILayout.Label(propIndex + ". " + "Unknown Property Type");
    347.                             GUILayout.Label("\tName: " + prop.Name);
    348.                             GUILayout.Label("\tType: " + prop.PropertyType);
    349.                             GUILayout.Label(propIndex + ". " + "Cannot retrieve a value for this property");
    350.                             break;
    351.                     }
    352.                     propIndex++;
    353.                 }
    354.             }
    355.  
    356.             //GUILayout.Label("Properties found here:");
    357.  
    358.             ////Show current properties
    359.             //foreach (var property in gridInfo.GetAllPositionPropertiesAtPosition(selectionPosition))
    360.             //{
    361.             //    GUILayout.Label(property.Key + ":" + property.Value);
    362.             //}
    363.  
    364.  
    365.             if (GUILayout.Button("Remove"))
    366.             {
    367.                 gridInfo.EraseAllPositionPropertiesAtPosition(selectionPosition);
    368.             }
    369.             if (GUILayout.Button("Clear All"))
    370.             {
    371.                 gridInfo.Reset();
    372.             }
    373.         }
    374.         else
    375.         {
    376.             GUILayout.Label("No Properties Found Here");
    377.         }
    378.  
    379.         //if (GUILayout.Button("Create String Data 1"))
    380.         //{
    381.         //    gridInfo.SetPositionProperty(selectionPosition, "test", "1");
    382.         //}
    383.  
    384.         //if (GUILayout.Button("Create String Data 2"))
    385.         //{
    386.         //    gridInfo.SetPositionProperty(selectionPosition, "test2", "2");
    387.         //}
    388.  
    389.         #endregion Scene
    390.     }
    391. }
    392. #endif
    Code (CSharp):
    1. using System.Reflection;
    2.  
    3. namespace Tiles
    4. {
    5.     public interface IDataTile
    6.     {
    7.         PropertyInfo[] GetDataTileProperties();
    8.     }
    9. }
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using UnityEngine;
    6.  
    7. namespace UnityEngine.Tilemaps
    8. {
    9.     [Serializable]
    10.     public enum GridInformationType
    11.     {
    12.         Bool,
    13.         Integer,
    14.         String,
    15.         Float,
    16.         Double,
    17.         UnityObject,
    18.         Color
    19.     }
    20.  
    21.     [Serializable]
    22.     [AddComponentMenu("Tilemap/Grid Information")]
    23.     public class GridInformation : MonoBehaviour, ISerializationCallbackReceiver
    24.     {
    25.         internal struct GridInformationValue
    26.         {
    27.             public GridInformationType type;
    28.             public object data;
    29.         }
    30.  
    31.         [Serializable]
    32.         internal struct GridInformationKey
    33.         {
    34.             public Vector3Int position;
    35.             public String name;
    36.         }
    37.  
    38.         private Dictionary<GridInformationKey, GridInformationValue> m_PositionProperties = new Dictionary<GridInformationKey, GridInformationValue>();
    39.         internal Dictionary<GridInformationKey, GridInformationValue> PositionProperties
    40.         {
    41.             get { return m_PositionProperties; }
    42.         }
    43.  
    44.         [SerializeField]
    45.         [HideInInspector]
    46.         private List<GridInformationKey> m_PositionIntKeys = new List<GridInformationKey>();
    47.  
    48.         [SerializeField]
    49.         [HideInInspector]
    50.         private List<int> m_PositionIntValues = new List<int>();
    51.  
    52.         [SerializeField]
    53.         [HideInInspector]
    54.         private List<GridInformationKey> m_PositionBoolKeys = new List<GridInformationKey>();
    55.  
    56.         [SerializeField]
    57.         [HideInInspector]
    58.         private List<bool> m_PositionBoolValues = new List<bool>();
    59.  
    60.         [SerializeField]
    61.         [HideInInspector]
    62.         private List<GridInformationKey> m_PositionStringKeys = new List<GridInformationKey>();
    63.  
    64.         [SerializeField]
    65.         [HideInInspector]
    66.         private List<String> m_PositionStringValues = new List<String>();
    67.  
    68.         [SerializeField]
    69.         [HideInInspector]
    70.         private List<GridInformationKey> m_PositionFloatKeys = new List<GridInformationKey>();
    71.  
    72.         [SerializeField]
    73.         [HideInInspector]
    74.         private List<float> m_PositionFloatValues = new List<float>();
    75.  
    76.         [SerializeField]
    77.         [HideInInspector]
    78.         private List<GridInformationKey> m_PositionDoubleKeys = new List<GridInformationKey>();
    79.  
    80.         [SerializeField]
    81.         [HideInInspector]
    82.         private List<Double> m_PositionDoubleValues = new List<Double>();
    83.  
    84.         [SerializeField]
    85.         [HideInInspector]
    86.         private List<GridInformationKey> m_PositionObjectKeys = new List<GridInformationKey>();
    87.  
    88.         [SerializeField]
    89.         [HideInInspector]
    90.         private List<Object> m_PositionObjectValues = new List<Object>();
    91.  
    92.         [SerializeField]
    93.         [HideInInspector]
    94.         private List<GridInformationKey> m_PositionColorKeys = new List<GridInformationKey>();
    95.  
    96.         [SerializeField]
    97.         [HideInInspector]
    98.         private List<Color> m_PositionColorValues = new List<Color>();
    99.  
    100.         void ISerializationCallbackReceiver.OnBeforeSerialize()
    101.         {
    102.             Grid grid = GetComponentInParent<Grid>();
    103.             if (grid == null)
    104.                 return;
    105.  
    106.             m_PositionIntKeys.Clear();
    107.             m_PositionIntValues.Clear();
    108.             m_PositionBoolKeys.Clear();
    109.             m_PositionBoolValues.Clear();
    110.             m_PositionStringKeys.Clear();
    111.             m_PositionStringValues.Clear();
    112.             m_PositionFloatKeys.Clear();
    113.             m_PositionFloatValues.Clear();
    114.             m_PositionDoubleKeys.Clear();
    115.             m_PositionDoubleValues.Clear();
    116.             m_PositionObjectKeys.Clear();
    117.             m_PositionObjectValues.Clear();
    118.             m_PositionColorKeys.Clear();
    119.             m_PositionColorValues.Clear();
    120.  
    121.             foreach (var kvp in m_PositionProperties)
    122.             {
    123.                 switch (kvp.Value.type)
    124.                 {
    125.                     case GridInformationType.Integer:
    126.                         m_PositionIntKeys.Add(kvp.Key);
    127.                         m_PositionIntValues.Add((int)kvp.Value.data);
    128.                         break;
    129.                     case GridInformationType.Bool:
    130.                         m_PositionBoolKeys.Add(kvp.Key);
    131.                         m_PositionBoolValues.Add((bool)kvp.Value.data);
    132.                         break;
    133.                     case GridInformationType.String:
    134.                         m_PositionStringKeys.Add(kvp.Key);
    135.                         m_PositionStringValues.Add(kvp.Value.data as String);
    136.                         break;
    137.                     case GridInformationType.Float:
    138.                         m_PositionFloatKeys.Add(kvp.Key);
    139.                         m_PositionFloatValues.Add((float)kvp.Value.data);
    140.                         break;
    141.                     case GridInformationType.Double:
    142.                         m_PositionDoubleKeys.Add(kvp.Key);
    143.                         m_PositionDoubleValues.Add((double)kvp.Value.data);
    144.                         break;
    145.                     case GridInformationType.Color:
    146.                         m_PositionColorKeys.Add(kvp.Key);
    147.                         m_PositionColorValues.Add((Color)kvp.Value.data);
    148.                         break;
    149.                     default:
    150.                         m_PositionObjectKeys.Add(kvp.Key);
    151.                         m_PositionObjectValues.Add(kvp.Value.data as Object);
    152.                         break;
    153.                 }
    154.             }
    155.         }
    156.  
    157.         void ISerializationCallbackReceiver.OnAfterDeserialize()
    158.         {
    159.             m_PositionProperties.Clear();
    160.             for (int i = 0; i != Math.Min(m_PositionIntKeys.Count, m_PositionIntValues.Count); i++)
    161.             {
    162.                 GridInformationValue positionValue;
    163.                 positionValue.type = GridInformationType.Integer;
    164.                 positionValue.data = m_PositionIntValues[i];
    165.                 m_PositionProperties.Add(m_PositionIntKeys[i], positionValue);
    166.             }
    167.             for (int i = 0; i != Math.Min(m_PositionBoolKeys.Count, m_PositionBoolValues.Count); i++)
    168.             {
    169.                 GridInformationValue positionValue;
    170.                 positionValue.type = GridInformationType.Bool;
    171.                 positionValue.data = m_PositionBoolValues[i];
    172.                 m_PositionProperties.Add(m_PositionBoolKeys[i], positionValue);
    173.             }
    174.             for (int i = 0; i != Math.Min(m_PositionStringKeys.Count, m_PositionStringValues.Count); i++)
    175.             {
    176.                 GridInformationValue positionValue;
    177.                 positionValue.type = GridInformationType.String;
    178.                 positionValue.data = m_PositionStringValues[i];
    179.                 m_PositionProperties.Add(m_PositionStringKeys[i], positionValue);
    180.             }
    181.             for (int i = 0; i != Math.Min(m_PositionFloatKeys.Count, m_PositionFloatValues.Count); i++)
    182.             {
    183.                 GridInformationValue positionValue;
    184.                 positionValue.type = GridInformationType.Float;
    185.                 positionValue.data = m_PositionFloatValues[i];
    186.                 m_PositionProperties.Add(m_PositionFloatKeys[i], positionValue);
    187.             }
    188.             for (int i = 0; i != Math.Min(m_PositionDoubleKeys.Count, m_PositionDoubleValues.Count); i++)
    189.             {
    190.                 GridInformationValue positionValue;
    191.                 positionValue.type = GridInformationType.Double;
    192.                 positionValue.data = m_PositionDoubleValues[i];
    193.                 m_PositionProperties.Add(m_PositionDoubleKeys[i], positionValue);
    194.             }
    195.             for (int i = 0; i != Math.Min(m_PositionObjectKeys.Count, m_PositionObjectValues.Count); i++)
    196.             {
    197.                 GridInformationValue positionValue;
    198.                 positionValue.type = GridInformationType.UnityObject;
    199.                 positionValue.data = m_PositionObjectValues[i];
    200.                 m_PositionProperties.Add(m_PositionObjectKeys[i], positionValue);
    201.             }
    202.             for (int i = 0; i != Math.Min(m_PositionColorKeys.Count, m_PositionColorValues.Count); i++)
    203.             {
    204.                 GridInformationValue positionValue;
    205.                 positionValue.type = GridInformationType.Color;
    206.                 positionValue.data = m_PositionColorValues[i];
    207.                 m_PositionProperties.Add(m_PositionColorKeys[i], positionValue);
    208.             }
    209.         }
    210.  
    211.         public bool SetPositionProperty(Vector3Int position, String name, int positionProperty)
    212.         {
    213.             return SetPositionProperty(position, name, GridInformationType.Integer, positionProperty);
    214.         }
    215.  
    216.         public bool SetPositionProperty(Vector3Int position, String name, string positionProperty)
    217.         {
    218.             return SetPositionProperty(position, name, GridInformationType.String, positionProperty);
    219.         }
    220.  
    221.         public bool SetPositionProperty(Vector3Int position, String name, float positionProperty)
    222.         {
    223.             return SetPositionProperty(position, name, GridInformationType.Float, positionProperty);
    224.         }
    225.  
    226.         public bool SetPositionProperty(Vector3Int position, String name, double positionProperty)
    227.         {
    228.             return SetPositionProperty(position, name, GridInformationType.Double, positionProperty);
    229.         }
    230.  
    231.         public bool SetPositionProperty(Vector3Int position, String name, bool positionProperty)
    232.         {
    233.             return SetPositionProperty(position, name, GridInformationType.Bool, positionProperty);
    234.         }
    235.  
    236.         public bool SetPositionProperty(Vector3Int position, String name, UnityEngine.Object positionProperty)
    237.         {
    238.             return SetPositionProperty(position, name, GridInformationType.UnityObject, positionProperty);
    239.         }
    240.  
    241.         public bool SetPositionProperty(Vector3Int position, String name, Color positionProperty)
    242.         {
    243.             return SetPositionProperty(position, name, GridInformationType.Color, positionProperty);
    244.         }
    245.  
    246.         public bool SetPositionProperty<T>(Vector3Int position, String name, T positionProperty)
    247.         {
    248.             Debug.Log(name + " - Called generic set position property");
    249.             throw new NotImplementedException("Storing this type is not accepted in GridInformation");
    250.         }
    251.  
    252.         private bool SetPositionProperty(Vector3Int position, String name, GridInformationType dataType, System.Object positionProperty)
    253.         {
    254.             Grid grid = GetComponentInParent<Grid>();
    255.             if (grid != null && positionProperty != null)
    256.             {
    257.                 GridInformationKey positionKey;
    258.                 positionKey.position = position;
    259.                 positionKey.name = name;
    260.  
    261.                 GridInformationValue positionValue;
    262.                 positionValue.type = dataType;
    263.                 positionValue.data = positionProperty;
    264.  
    265.                 m_PositionProperties[positionKey] = positionValue;
    266.                 return true;
    267.             }
    268.             return false;
    269.         }
    270.  
    271.         public T GetPositionProperty<T>(Vector3Int position, String name, T defaultValue) where T : UnityEngine.Object
    272.         {
    273.             GridInformationKey positionKey;
    274.             positionKey.position = position;
    275.             positionKey.name = name;
    276.  
    277.             GridInformationValue positionValue;
    278.             if (m_PositionProperties.TryGetValue(positionKey, out positionValue))
    279.             {
    280.                 if (positionValue.type != GridInformationType.UnityObject)
    281.                     throw new InvalidCastException("Value stored in GridInformation is not of the right type");
    282.                 return positionValue.data as T;
    283.             }
    284.             return defaultValue;
    285.         }
    286.  
    287.         public int GetPositionProperty(Vector3Int position, String name, int defaultValue)
    288.         {
    289.             GridInformationKey positionKey;
    290.             positionKey.position = position;
    291.             positionKey.name = name;
    292.  
    293.             GridInformationValue positionValue;
    294.             if (m_PositionProperties.TryGetValue(positionKey, out positionValue))
    295.             {
    296.                 if (positionValue.type != GridInformationType.Integer)
    297.                     throw new InvalidCastException("Value stored in GridInformation is not of the right type");
    298.                 return (int)positionValue.data;
    299.             }
    300.             return defaultValue;
    301.         }
    302.  
    303.         public bool GetPositionProperty(Vector3Int position, String name, bool defaultValue)
    304.         {
    305.             GridInformationKey positionKey;
    306.             positionKey.position = position;
    307.             positionKey.name = name;
    308.  
    309.             GridInformationValue positionValue;
    310.             if (m_PositionProperties.TryGetValue(positionKey, out positionValue))
    311.             {
    312.                 if (positionValue.type != GridInformationType.Bool)
    313.                     throw new InvalidCastException("Value stored in GridInformation is not of the right type");
    314.                 return (bool)positionValue.data;
    315.             }
    316.             return defaultValue;
    317.         }
    318.  
    319.         public string GetPositionProperty(Vector3Int position, String name, string defaultValue)
    320.         {
    321.             GridInformationKey positionKey;
    322.             positionKey.position = position;
    323.             positionKey.name = name;
    324.  
    325.             GridInformationValue positionValue;
    326.             if (m_PositionProperties.TryGetValue(positionKey, out positionValue))
    327.             {
    328.                 if (positionValue.type != GridInformationType.String)
    329.                     throw new InvalidCastException("Value stored in GridInformation is not of the right type");
    330.                 return (string)positionValue.data;
    331.             }
    332.             return defaultValue;
    333.         }
    334.  
    335.         public float GetPositionProperty(Vector3Int position, String name, float defaultValue)
    336.         {
    337.             GridInformationKey positionKey;
    338.             positionKey.position = position;
    339.             positionKey.name = name;
    340.  
    341.             GridInformationValue positionValue;
    342.             if (m_PositionProperties.TryGetValue(positionKey, out positionValue))
    343.             {
    344.                 if (positionValue.type != GridInformationType.Float)
    345.                     throw new InvalidCastException("Value stored in GridInformation is not of the right type");
    346.                 return (float)positionValue.data;
    347.             }
    348.             return defaultValue;
    349.         }
    350.  
    351.         public double GetPositionProperty(Vector3Int position, String name, double defaultValue)
    352.         {
    353.             GridInformationKey positionKey;
    354.             positionKey.position = position;
    355.             positionKey.name = name;
    356.  
    357.             GridInformationValue positionValue;
    358.             if (m_PositionProperties.TryGetValue(positionKey, out positionValue))
    359.             {
    360.                 if (positionValue.type != GridInformationType.Double)
    361.                     throw new InvalidCastException("Value stored in GridInformation is not of the right type");
    362.                 return (double)positionValue.data;
    363.             }
    364.             return defaultValue;
    365.         }
    366.  
    367.         public Color GetPositionProperty(Vector3Int position, String name, Color defaultValue)
    368.         {
    369.             GridInformationKey positionKey;
    370.             positionKey.position = position;
    371.             positionKey.name = name;
    372.  
    373.             GridInformationValue positionValue;
    374.             if (m_PositionProperties.TryGetValue(positionKey, out positionValue))
    375.             {
    376.                 if (positionValue.type != GridInformationType.Color)
    377.                     throw new InvalidCastException("Value stored in GridInformation is not of the right type");
    378.                 return (Color)positionValue.data;
    379.             }
    380.             return defaultValue;
    381.         }
    382.  
    383.         public bool ErasePositionProperty(Vector3Int position, String name)
    384.         {
    385.             GridInformationKey positionKey;
    386.             positionKey.position = position;
    387.             positionKey.name = name;
    388.             return m_PositionProperties.Remove(positionKey);
    389.         }
    390.  
    391.         public virtual void Reset()
    392.         {
    393.             m_PositionProperties.Clear();
    394.         }
    395.  
    396.         public Vector3Int[] GetAllPositions(string propertyName)
    397.         {
    398.             return m_PositionProperties.Keys.ToList().FindAll(x => x.name == propertyName).Select(x => x.position).ToArray();
    399.         }
    400.  
    401.         // Added for Delve
    402.  
    403.         public bool PositionHasProperties(Vector3Int position)
    404.         {
    405.             return m_PositionProperties.Keys.Any(k => k.position.Equals(position));
    406.         }
    407.  
    408.         public Dictionary<string, string> GetAllPositionPropertiesAtPosition(Vector3Int position)
    409.         {
    410.             Dictionary<string, string> resultList = new Dictionary<string, string>();
    411.             foreach (var key in m_PositionProperties.Keys.Where(k => k.position.Equals(position)).ToList())
    412.             {
    413.                 //TODO: Refactor method to pull correct types
    414.                 resultList.Add(key.name, GetPositionProperty(key.position, key.name, null));
    415.             }
    416.             return resultList;
    417.         }
    418.  
    419.         public void EraseAllPositionPropertiesAtPosition(Vector3Int position)
    420.         {
    421.             foreach (var key in m_PositionProperties.Keys.Where(k => k.position.Equals(position)).ToList())
    422.             {
    423.                 ErasePositionProperty(position, key.name);
    424.             }
    425.         }
    426.     }
    427. }
     
    Last edited: Feb 5, 2019
  10. leisim

    leisim

    Joined:
    Dec 13, 2016
    Posts:
    5
    This looks really interesting but the IDataTile seems to be missing. What is its purpose?
     
  11. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    The method defined in that interface is used to get properties about the tile and copy them to the tile on the tilemap. It requires a modified version of GridInformation. (Included above). It is rather difficult to store information per tile, so this has to be done in a helper class (i.e. GridInformation) that lives on the tilemap.

    Object refs are a bit odd and don't seem to work well.
     
    CheekySparrow78 likes this.
  12. ShervinVR

    ShervinVR

    Joined:
    May 8, 2018
    Posts:
    7
    Can I ask what the Extensions namespace is? I'm not really sure and can't seem to find anything online :(
     
  13. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    Extension methods are a way to add functionality to objects without having to modify the class declaration itself. You can use them to add behavior to predefined unity objects. I will share some of mine as examples.

    In this example, I add the method DirectlyMoveNonTileMapPosition to the GameObject class.
    Code (CSharp):
    1. using General;
    2. using UnityEngine;
    3.  
    4. namespace Extensions
    5. {
    6.     public static class GameObjectExtensions
    7.     {
    8.         //public static void NudgeNonTileMapObject(this GameObject go, Vector2Int delta)
    9.         //{
    10.         //    MapMovement.NudgeNonTileMapObject(go, delta.x, delta.y);
    11.         //}
    12.  
    13.         //public static void AdjustNonTileMapPosition(this GameObject go)
    14.         //{
    15.         //    MapMovement.AdjustNonTileMapObjectPosition(go);
    16.         //}
    17.  
    18.         public static void DirectlyMoveNonTileMapPosition(this GameObject go, Vector2Int pos)
    19.         {
    20.             MapMovement.DirectMoveNonTileMapObject(go, pos);
    21.         }
    22.     }
    23.  
    24.     internal static class MapMovement
    25.     {
    26.         //internal static void NudgeNonTileMapObject(GameObject go, int dX, int dY)
    27.         //{
    28.         //    var goPos = go.transform.position;
    29.         //    go.transform.position = new Vector3(dX * (goPos.x + dX + Globals.PrefabXyOffset),
    30.         //        dY * (goPos.y + dY + Globals.PrefabXyOffset), goPos.z);
    31.         //}
    32.  
    33.         //internal static void AdjustNonTileMapObjectPosition(GameObject go)
    34.         //{
    35.         //    var goPos = go.transform.position;
    36.         //    go.transform.position = new Vector3(goPos.x + Globals.PrefabXyOffset,
    37.         //        goPos.y + Globals.PrefabXyOffset, goPos.z);
    38.         //}
    39.  
    40.         internal static void DirectMoveNonTileMapObject(GameObject go, Vector2Int position)
    41.         {
    42.             go.transform.position = new Vector3(position.x + Globals.PrefabXyOffset,
    43.                 position.y + Globals.PrefabXyOffset, go.transform.position.z);
    44.         }
    45.     }
    46. }
    To call the method, I need a GameObject reference. Then I can simple use reference.DirectlyMoveNonTileMapPosition(new Vector2Int(x,y);

    Here is another example:
    Code (CSharp):
    1. using Elements;
    2. using UnityEngine;
    3. using UnityEngine.Tilemaps;
    4. using Object = UnityEngine.Object;
    5.  
    6. namespace Extensions
    7. {
    8.     public static class TileBaseExtensions
    9.     {
    10.         public static void InstantiateMapObject(this TileBase baseTile, Tilemap instanceTileMap,
    11.             Vector2Int instancePosition)
    12.         {
    13.             InstantiateTileOrMapObject(baseTile, instanceTileMap, instancePosition);
    14.         }
    15.  
    16.         private static void InstantiateTileOrMapObject(TileBase baseTile, Tilemap instanceTileMap,
    17.             Vector2Int instancePosition)
    18.         {
    19.             NonTileMapObjectTileBase nonMapTile = baseTile as NonTileMapObjectTileBase;
    20.             if (nonMapTile)
    21.             {
    22.                 //Instantiate GO
    23.                 GameObject nonTileMapObject = Object.Instantiate(nonMapTile.TileAssociatedPrefab,
    24.                     instanceTileMap.gameObject.transform);
    25.  
    26.                 //Move GO
    27.                 nonTileMapObject.gameObject.DirectlyMoveNonTileMapPosition(instancePosition);
    28.  
    29.                 //Attach non tilemap object script
    30.                 NonTileMapObject objectProperties =
    31.                     (NonTileMapObject)nonTileMapObject.AddComponent(typeof(NonTileMapObject));
    32.  
    33.                 //Get tile association to set properties
    34.                 AdvancedTile advTileVersion = (AdvancedTile)baseTile;
    35.  
    36.                 //Set properties
    37.                 objectProperties.colliderDoesNotCastShadows = advTileVersion.colliderDoesNotCastShadows;
    38.                 objectProperties.hasCollider = advTileVersion.hasCollider;
    39.             }
    40.             else
    41.             {
    42.                 //Instantiate TileBase if found
    43.                 instanceTileMap.SetTile(new Vector3Int(instancePosition.x, instancePosition.y, 0), baseTile);
    44.             }
    45.         }
    46.     }
    47. }
    This is an example of calling one of these methods from another script:
    Code (CSharp):
    1. VisualTileDefinition first = mapVisualInfo.tileDefinitions.FirstOrDefault(g => g.name == (CellTypeMatrix.First(v => v.Value == cell.CellType).Key));
    2. VisualTileDefinition second = hasCompanionCell ? mapVisualInfo.tileDefinitions.FirstOrDefault(g => g.name == (CellTypeMatrix.First(v => v.Value == companionCell.CellType).Key)) : null;
    3.  
    4. TileBase cellTranslationTile = first != null ? first.translationTile : mapVisualInfo.defaultTile;
    5. Tilemap instanceLocation = first != null ? first.tilemap : mapVisualInfo.defaultTileMap;
    6. TileBase companionCellTranslationTile = hasCompanionCell ? (second != null ? second.translationTile : mapVisualInfo.defaultTile) : null;
    7.  
    8. Tilemap companionInstanceLocation = hasCompanionCell ? (second != null ? second.tilemap : mapVisualInfo.defaultTileMap) : null;
    9.  
    10. //Instantiate tile if found here. Remember that Karcero is stupid and swaps x and y coords
    11. if (cellTranslationTile != null)
    12. {
    13.     cellTranslationTile.InstantiateMapObject(instanceLocation, new Vector2Int(y, x));
    14. }
     
    Last edited: Aug 2, 2019
  14. ra6e

    ra6e

    Joined:
    May 24, 2019
    Posts:
    5
    Sorry to bring up this necro but I can't seem to find any information on what I want to achieve and this is the closest there is I was able to find.

    I'm using prefab tile:

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using UnityEditor;
    7. using UnityEngine;
    8. using UnityEngine.Tilemaps;
    9.  
    10. namespace Assets.Scripts.Tiles
    11. {
    12.     public class PrefabTile : TileBase
    13.     {
    14.         public GameObject TilePrefab;
    15.         public Sprite TileSprite => this.TilePrefab.GetComponent<SpriteRenderer>()?.sprite;
    16.  
    17.         public float TileHeight = 2.56f;
    18.         public float prefabZOffset = -1f;
    19.  
    20.         public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    21.         {
    22.             var actualTilemap = tilemap.GetComponent<Tilemap>();
    23.             var worldPosition = actualTilemap.layoutGrid.CellToWorld(position);
    24.  
    25. #if UNITY_EDITOR
    26.             if (go != null)
    27.             {
    28.                 if (go.scene.name == null)
    29.                 {
    30.                     DestroyImmediate(go);
    31.                 }
    32.             }
    33. #endif
    34.             if (go != null)
    35.             {
    36.                 // set cell position
    37.                 go.GetComponent<GameTileBase>().CellPosition = position;
    38.  
    39.                 //Modify position of GO to match middle of Tile sprite
    40.                 go.transform.position = new Vector3(
    41.                     worldPosition.x,
    42.                     worldPosition.y - this.TileHeight / 2,
    43.                     prefabZOffset);
    44.             }
    45.  
    46.             return true;
    47.         }
    48.  
    49.         public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    50.         {
    51.             tileData.sprite = this.TileSprite;
    52.  
    53.             if (this.TilePrefab && tileData.gameObject == null)
    54.             {
    55.                 tileData.gameObject =  this.TilePrefab;
    56.             }
    57.         }
    58.  
    59. #if UNITY_EDITOR
    60.         [MenuItem("Assets/Create/Custom/Prefab Tile")]
    61.         public static void CreatePrefabTiles()
    62.         {
    63.             string path = EditorUtility.SaveFilePanelInProject("Save Prefab Tile", "New Prefab Tile", "asset", "Save Prefab Tile", "Assets");
    64.  
    65.             if (path == "")
    66.             {
    67.                 return;
    68.             }
    69.  
    70.             AssetDatabase.CreateAsset(CreateInstance<PrefabTile>(), path);
    71.         }
    72. #endif
    73.     }
    74. }
    75.  
    the base class
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using UnityEngine;
    7. using UnityEngine.Tilemaps;
    8. using Random = UnityEngine.Random;
    9.  
    10. namespace Assets.Scripts.Tiles
    11. {
    12.     public class GameTileBase : MonoBehaviour, IGameTile
    13.     {
    14.         public MapManager MapManager;
    15.         public CameraControl CameraControl;
    16.         public Sprite[] PossibleStartingSprites;
    17.         public Sprite CurrentSprite;
    18.         public SpriteRenderer SpriteRenderer;
    19.         public GameObject LinkedCity { get; set; }
    20.         public GameObject GameObject => this.gameObject;
    21.         public int Income { get; set; }
    22.         public Vector3Int CellPosition;
    23.  
    24.         public virtual void UpgradeTile()
    25.         {
    26.         }
    27.  
    28.         public virtual void Start()
    29.         {
    30.             this.MapManager = GameObject.Find("MapManager").GetComponent<MapManager>();
    31.             this.CameraControl = Camera.main.GetComponent<CameraControl>();
    32.             this.CurrentSprite = this.PossibleStartingSprites[Random.Range(0, this.PossibleStartingSprites.Length)];
    33.             this.SpriteRenderer.sprite = this.CurrentSprite;
    34.         }
    35.     }
    36. }
    37.  
    The Tile Asset



    The prefab:



    What I'm trying to do is when I click a tile it "gets upgraded" as in I remove a "PlainPrefabTile" and create a new "FarmPrefabTile" at it's place.

    I've tried to make a custom brush:
    Code (CSharp):
    1. using Assets.Scripts.Tiles;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using UnityEditor.Tilemaps;
    7. using UnityEngine;
    8. using UnityEngine.Tilemaps;
    9.  
    10. namespace Assets.Scripts.Extensions
    11. {
    12.     public class PrefabTileBrush : PrefabBrush
    13.     {
    14.         public float TileHeight = 2.56f;
    15.         public float prefabZOffset = -1f;
    16.  
    17.         public void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position, Object prefab)
    18.         {
    19.             Debug.Log(prefab);
    20.             base.Paint(grid, brushTarget, position);
    21.  
    22.             var tilemap = brushTarget.GetComponent<Tilemap>();
    23.             var tile = tilemap.GetTile(position) as PrefabTile;
    24.             var worldPosition = tilemap.layoutGrid.CellToWorld(position);
    25.  
    26.             if (tile != null)
    27.             {
    28.                 var go = Instantiate(prefab);
    29.  
    30.                 Debug.Log(go);
    31.  
    32.                 //go.transform.position = new Vector3(
    33.                 //        worldPosition.x,
    34.                 //        worldPosition.y - this.TileHeight / 2,
    35.                 //        prefabZOffset
    36.                 //    );
    37.  
    38.                 //go.transform.rotation = Quaternion.identity;
    39.  
    40.                 //tile.TilePrefab = go;
    41.             }
    42.         }
    43.  
    44.         public override void Erase(GridLayout grid, GameObject brushTarget, Vector3Int position)
    45.         {
    46.             var tilemap = brushTarget.GetComponent<Tilemap>();
    47.             var tile = tilemap.GetTile(position) as PrefabTile;
    48.  
    49.             if (tile.TilePrefab != null)
    50.             {
    51.                 DestroyImmediate(tile.TilePrefab, false);
    52.             }
    53.  
    54.             base.Erase(grid, brushTarget, position);
    55.         }
    56.     }
    57. }
    58.  
    and try to create it like so:

    Code (CSharp):
    1.     public Object CreateTile(TileTypes type, Vector3 position, Quaternion quaternion, Vector3Int cellPosition)
    2.     {
    3.         var originalTile = this.GetTile(cellPosition);
    4.  
    5.         Debug.Log(originalTile);
    6.  
    7.         if (originalTile != null)
    8.         {
    9.             this.Brush.Erase(this.GameMap, this.GameMap.gameObject, cellPosition);
    10.         }
    11.  
    12.         Object result = null;
    13.  
    14.         if (type == TileTypes.Farm)
    15.         {
    16.             this.Brush.Paint(this.GameMap, this.GameMap.gameObject, cellPosition, this.farmPrefabTile);
    17.             //result = Instantiate(this.farmPrefabTile, position, quaternion, this.GameMap.gameObject.transform);
    18.         }
    19.         else if (type == TileTypes.Forester)
    20.         {
    21.             result = Instantiate(this.foresterPrefabTile, position, quaternion, this.GameMap.gameObject.transform);
    22.         }
    23.  
    24.         Debug.Log(result);
    25.  
    26.         return result;
    27.     }
    It won't let me erase it (seems like the original prefab isn't an instance, but the prefab itself) and it won't allow me to destroy assets, if I change the tile to instantiate the prefab there are then 2 objects on the grid, and the instantiation of the farmPrefabTile returns null (inside the PrefabTileBrush.Paint() method). Any ideas or clues how to achieve this

    EDIT: I painted the game with a Tile Palette (dragged the PrefabTileAssets (one for each tile type):
     
  15. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,514
    I haven't messed with gameobjects on tiles, but it appears that you're missing modifying the tileData of the tile. There's a gameobject field there, which should give you the instance of the prefab on that tile. If you were to destroy that gameobject, and assign your own new "upgrade" gameobject to that field with your brush, you may find that's sufficient to fix the problem. To do that, it might be easiest to swap the tile w/ .SetTile(..) with the brush to a different tile associated with the "upgrade" prefab.
     
    Last edited: Jun 15, 2020
  16. ra6e

    ra6e

    Joined:
    May 24, 2019
    Posts:
    5
    The thing is I created the asset for each tile type, so when upgrading plainTile it's of that type and I don't want to just change the gameobject but the whole thing. I could revert it to have one asset with different prefabs attached but I couldnt then have each in the tile palette to easily create levels. I'm missing something and its hard to find similar solutions to this :/
     
  17. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    Have you tried Tilemap.GetTile.GetTileData
    Then gameObject from the TileData?

    This should be enough to get the go ref on the tile and you can destroy it or do whatever. I'm pretty sure though if you set the tile again Unity will destroy it, assuming you are allowing Unity to handle the instantiation.

    Also, are you manually instantiating the prefabs? You shouldn't have to do that if you assign the
    tileData.gameObject, Unity will instantiate them for you (most likely why you are seeing two).
    You seem to already be doing what you need to for Unity to instantiate it (PrefabTile, Line 55).

    EDIT: You are instantiating the prefabs twice.
    Unity is handling one for you and you are also doing it in
    brush.paint() (Line 28) var go = Instantiate(prefab);

    Also, why are you using so many GameObjects? Fair enough if they need behavior, but that is going to create a lot of overhead. Tiles and GameObjects are two separate things - if you can get away with just tiles, you should try to do that.
     
    Last edited: Jun 15, 2020
  18. ra6e

    ra6e

    Joined:
    May 24, 2019
    Posts:
    5
    Sorry, I'm rather new. What would the paint method look like please? I'm passing an Object, but it's null (the instantiation), if I pass it from the create method, I don't know how to properly instantiate it there :/

    EDIT: yeah most of them need the behavior as most of them can be level/changed (that's the part I can't seem to get to work)
     
    Last edited: Jun 16, 2020
  19. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    I'd really need to see more about where
    came from.

    My point is that you don't need to worry about instantiating the prefabs at all or even using a prefab brush if you are already assigning TilePrefab to the PrefabTile asset. Unity will handle this for you. (See TileData.gameObject).

    If you use a prefab tile brush (like you are currently), you are just instantiating the gameobjects again. (unneeded).
    My advice: try using the standard GridBrush (or whatever is default) to paint the tiles. Any prefabs you have assigned to the tile asset should be instantiated for you when Unity creates the tile instance (after you click play of course).

    You can still have your game logic scripts on the prefab that is instantiated.

    Hope this helps/makes sense.
     
    ra6e likes this.
  20. ra6e

    ra6e

    Joined:
    May 24, 2019
    Posts:
    5
    Yes when I play the scene it correctly instantiates the proper tiles and everything works fine, but I can't seem to make it work when I want to "repaint" a tile.

    How can I paint a tile in runtime, using just the grid brush? How do I pass in the asset?

    I passed the asset as object to my script:


    How do I pass it to the paint though?

    Code (CSharp):
    1.     public Object farmPrefabTile;
    2.     public Object foresterPrefabTile;
    3.  
    4.     public GridBrush Brush => ScriptableObject.CreateInstance<GridBrush>();
    5.  
    6.     private List<IGameTile> AllGameTiles { get; set; }
    7.  
    8.     private void Awake()
    9.     {
    10.         if (Instance != null && Instance != this)
    11.         {
    12.             Destroy(this.gameObject);
    13.         }
    14.         else
    15.         {
    16.             Instance = this;
    17.         }
    18.     }
    19.  
    20.     public void Start()
    21.     {
    22.         this.AllGameTiles = FindObjectsOfType<MonoBehaviour>()
    23.             .OfType<IGameTile>()
    24.             .ToList();
    25.     }
    26.  
    27.     public void RemoveTile(Object tile)
    28.     {
    29.         Destroy(tile);
    30.     }
    31.  
    32.     public void CreateTile(TileTypes type, Vector3 position, Quaternion quaternion, Vector3Int cellPosition)
    33.     {
    34.         var originalTile = this.GetTile(cellPosition);
    35.  
    36.         if (type == TileTypes.Farm)
    37.         {
    38.             this.Brush.Paint(this.GameMap, this.GameMap.gameObject, cellPosition); // farmPrefabTile ???
    39.         }
    40.         else if (type == TileTypes.Forester)
    41.         {
    42.         }
    43.     }
    Thanks for your input mate, I'm so lost in here for days :/
     
  21. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    Give me just a few and I'll see if I can come up with a small project for you
     
    The_Chef and ra6e like this.