Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Prefab tile

Discussion in '2D Experimental Preview' started by TextusGames, Oct 12, 2017.

?

Would you like to have an ability to drag and drop gameobjects into tile palette

  1. Yes

    98.1%
  2. No

    1.9%
  1. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Hi.

    I am trying to make a TILE that creates prefab and removes sprite of itself.

    Basically, I want to add any (gameobject) into tile palette (through custom tile I guess).

    Here is code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3.  
    4. [CreateAssetMenu]
    5. public class PrefabTile : UnityEngine.Tilemaps.TileBase
    6. {
    7.     public Sprite Sprite; //The sprite of tile in a palette and in a scene
    8.     public GameObject Prefab; //The gameobject to spawn
    9.  
    10.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    11.     {
    12.         if (Sprite) tileData.sprite = Sprite; // Asigning sprite
    13.         tileData.gameObject = Prefab; // Assigning prefab
    14.     }
    15.  
    16.     public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    17.     {
    18.         // Streangly the position of gameobject starts at Left Bottom point of cell and not at it center
    19.         go.transform.position += Vector3.up * 0.5f + Vector3.right * 0.5f;
    20.                    
    21.         return base.StartUp(position, tilemap, go);
    22.     }
    23.  
    24. }
    It works but the sprite of tile is visible in a scene at runtime,
    so I need to hide the tile sprite in the scene.

    How can I do that (through custom tile's code)?

    (i don't want to use custom prefab brush because I want my prefabs to be on the palette and personally believe there should be the default way to just add prefabs to palette, cose it is kinda intuitive thing)

    EDITED (08.26.2019).
    Added RuntimePrefabTile unity package.
    Readme file is inside.
     

    Attached Files:

    Last edited: Aug 26, 2019
    megadavido likes this.
  2. robocup30

    robocup30

    Joined:
    May 2, 2017
    Posts:
    7
    Maybe try

    Code (CSharp):
    1.  
    2. if(Sprite && !Application.isPlaying)
    3. {
    4.     tileData.sprite = Sprite;
    5. }
    6. else
    7. {
    8.     tileData.sprite = null;
    9. }
    10.  
     
  3. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Ani
    Thank you for the reply, but its only works for tiles that I add in the editor at runtime, or than I point the cursor on tiles added during editor time. (At StartUp void I am not able to get actual tileData in order to set its sprite to null)
     
  4. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    I have made tile invisible by setting it animation data:
    Code (CSharp):
    1.    public override bool GetTileAnimationData(Vector3Int location, ITilemap tileMap, ref TileAnimationData tileAnimationData)
    2.     {
    3.         tileAnimationData.animatedSprites = new Sprite[] { null};
    4.         tileAnimationData.animationSpeed = 0;
    5.         tileAnimationData.animationStartTime = 0;
    6.         return true;
    7.     }
    I do not think this is the right way.
    But, it is working Then ever I need to add any prefab on palette, I just create a new instance of tile prefab assign its variables and drag and drop it into palette.
     
    Last edited: Oct 13, 2017
  5. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Here is final code.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3.  
    4. [CreateAssetMenu]
    5. public class PrefabTile : UnityEngine.Tilemaps.TileBase
    6. {
    7.     public Sprite Sprite; //The sprite of tile in the palette
    8.     public GameObject Prefab; //The gameobject to spawn
    9.  
    10.  
    11.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    12.     {
    13.         // Assign variables
    14.                if (!Application.isPlaying) tileData.sprite = Sprite;
    15.         else tileData.sprite = null;
    16.  
    17.         if (Prefab) tileData.gameObject = Prefab;
    18.     }
    19.  
    20.     public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    21.     {
    22.         // Streangly the position of gameobject starts at Left Bottom point of cell and not at it center
    23.        // TODO need to add anchor points  (vertical and horisontal (left,centre,right)(top,centre,bottom))
    24.         go.transform.position += Vector3.up * 0.5f + Vector3.right * 0.5f;
    25.         return true;
    26.     }
    27.  
    28.     public override bool GetTileAnimationData(Vector3Int location, ITilemap tileMap, ref TileAnimationData tileAnimationData)
    29.     {
    30.         // Make sprite of tile invisiable
    31.         tileAnimationData.animatedSprites = new Sprite[] { null};
    32.         tileAnimationData.animationSpeed = 0;
    33.         tileAnimationData.animationStartTime = 0;
    34.         return true;
    35.     }
    36.  
    37. }
    But I still want to hear from unity tech team 2D about the correct way of manipulating gameobjects in new tilemap system (preferably through palette because it seems a good thing to have all your level tools in one palette (tiles and game objects))

    For me it would be nice and intuitive just to drag and drop any prefab gameobject onto palette and unity do staff to generate preview of that prefab as an icon on palette cell and then you place that tile on tilemap the instance of that prefab will be instantiated (at runtime), but it seems not gonna happen.

    So unity tech team 2D and other users that is your way of manipulating prefabs through tilemap systems!?
     
    Last edited: Oct 13, 2017
  6. reuno

    reuno

    Joined:
    Sep 22, 2014
    Posts:
    4,923
    Same question here, I don't think the Prefab brush is user friendly. It's quite hidden, and having people just pick from the Palette seems more logical and practical.
    It'd be really nice to have a way to know if the tile is being painted in the palette window or in the scene. I too would love the tech team's opinion on this problem.
     
    Kiori and ChrisJohnson like this.
  7. ChrisJohnson

    ChrisJohnson

    Joined:
    Feb 20, 2013
    Posts:
    64
    I kind of wish there was a way to just add a prefab to a tile, like in the earlier betas as well. I think that for some situations it’s easier to do it this way instead of having to make a prefab brush.
     
  8. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,526
    easier Is better
     
    Last edited: Nov 12, 2017
  9. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    Can anyone get an actual object ref to go in TileBase.StartUp() ?
    The go ref is always null for me...

    EDIT: Nevermind...you actually have to set the object...per the script above
     
    Last edited: Nov 9, 2017
  10. amanda247

    amanda247

    Joined:
    Jan 20, 2015
    Posts:
    6
    I am using the same code as above but then I get this error for all of the prefabs and children of the prefabs whenever I stop the game:

    "Can't destroy RectTransform component of 'ChestTextWorthGroup'. If you want to destroy the game object, please call 'Destroy' on the game object instead. Destroying the RectTransform component is not allowed."


    Is anyone else getting that error?
     
  11. Kiori

    Kiori

    Joined:
    Jun 25, 2014
    Posts:
    161
    I was actually going to make a post saying exactly this. The current setup is very unfriendly and unintuitive.
    My view, is that either they should be in the pallete, as mentioned, or they should be in their own, prefab brushes category/place. and if so, it should be part of its own layer thing that isnt called tilemap, since it isn't, being that it's a prefab, not a gfx. What @ChrisJohnson said also sounds good.
    The major point is that there is room for improvement.

    Another cosmetic note, I'll use this as an example, in the tutorial there is a brown platform brush that in the pallete is represented by a huge brown square, yet when selected, only the selected grid block is highlighted(top left is "selected").
    It would be nice if when you clicked on it, being that the whole 9 tiles are part of a one brush, all of them would appear "selected", and not just the grid that you clicked on. Maybe this can be done and I'm not aware of it.
    But that's just cosmetic.
     
    Connorses and reuno like this.
  12. nopogo

    nopogo

    Joined:
    Sep 9, 2012
    Posts:
    25
    When using a prefab assignment like the above script I run into the following error when I try to build:

    Anybody else that has run into this issue?

    I also get the above mentioned error when stopping the scene:

     
    Last edited: Feb 5, 2018
  13. nopogo

    nopogo

    Joined:
    Sep 9, 2012
    Posts:
    25
    Updating to the latest version of unity fixed the error message for me
     
  14. thijsdaniels89

    thijsdaniels89

    Joined:
    Apr 21, 2013
    Posts:
    1
    Thanks for posting this prefab tile! I'm definitely with you guys on this, it would be great if prefabs could be used as tiles from the palette out-of-the-box. I would also love a RulePrefabTile as well, I think I might try and edit the existing RuleTile soon.

    In the meantime I've made some minor tweaks to the PrefabTile posted above.
    1. Instead of overriding the GetTileAnimationData, I removed the TilemapRenderer component from the tilemap. I think it makes sense to do this, because the prefabs already have their own sprite renderer with their own sorting layer etc., so having another renderer that *also* renders sprites for your prefabs doesn't seem right.
    2. Instead of assigning a separate sprite for your tile, I'm using the sprite that is already assigned to the prefab's sprite renderer. Even with the TilemapRenderer removed, the TileData.sprite field is still needed, because it is displayed in the palette window.
    3. I added a UnityEditor extension to add a menu item so that it's easy to create new prefab tiles.
    Here's the code I'm using:

    Code (CSharp):
    1. using System;
    2.  
    3. #if UNITY_EDITOR
    4. using UnityEditor;
    5. #endif
    6.  
    7. namespace UnityEngine.Tilemaps
    8. {
    9.     /// <summary>
    10.     /// A Tile that creates a GameObject at runtime.
    11.     /// </summary>
    12.     [Serializable]
    13.     public class PrefabTile : TileBase
    14.     {
    15.         /// <summary>
    16.         /// The GameObject that will be created by the Tile.
    17.         /// </summary>
    18.         [SerializeField] public GameObject Prefab;
    19.  
    20.         /// <summary>
    21.         /// The Sprite that will be displayed in the Editor.
    22.         /// </summary>
    23.         private Sprite Sprite => Prefab.GetComponent<SpriteRenderer>().sprite;
    24.  
    25.         /// <summary>
    26.         /// Allows the TileData for this Tile to be modified.
    27.         /// </summary>
    28.         /// <param name="position"></param>
    29.         /// <param name="tilemap"></param>
    30.         /// <param name="tileData"></param>
    31.         public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    32.         {
    33.             tileData.gameObject = Prefab;
    34.             tileData.sprite = Sprite;
    35.         }
    36.  
    37.         /// <summary>
    38.         /// Allows the GameObject for this Tile to be modified when the game starts.
    39.         /// </summary>
    40.         /// <param name="position"></param>
    41.         /// <param name="tilemap"></param>
    42.         /// <param name="gameObject"></param>
    43.         /// <returns></returns>
    44.         public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject gameObject)
    45.         {
    46.             gameObject.transform.position += new Vector3(0.5f, 0.5f, 0);
    47.                    
    48.             return base.StartUp(position, tilemap, gameObject);
    49.         }
    50.    
    51. #if UNITY_EDITOR
    52.         /// <summary>
    53.         /// Registers a menu item that creates a new PrefabTile asset.
    54.         /// </summary>
    55.         [MenuItem("Assets/Create/Prefab Tile")]
    56.         public static void CreatePrefabTile()
    57.         {
    58.             string path = EditorUtility.SaveFilePanelInProject(
    59.                 "Save Prefab Tile",
    60.                 "New Prefab Tile",
    61.                 "asset",
    62.                 "Save Prefab Tile",
    63.                 "Assets"
    64.             );
    65.  
    66.             if (path == "")
    67.             {
    68.                 return;
    69.             }
    70.  
    71.             AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<PrefabTile>(), path);
    72.         }
    73. #endif
    74.     }
    75. }
     
    Last edited: Feb 25, 2018
  15. nopogo

    nopogo

    Joined:
    Sep 9, 2012
    Posts:
    25
    I'm wondering, what would be the other way around. I.E. fetching the gameObject based on tile. Since tilemap.GetTile() gives you a TileBase object instead of a Tile. Is there a way to fetch the correct tile/gameobject pair without getting hacky?
     
  16. MonsterPlant

    MonsterPlant

    Joined:
    Aug 28, 2017
    Posts:
    2
    This is what I'm wondering right now. Have you found an easy way to do this?
     
  17. nopogo

    nopogo

    Joined:
    Sep 9, 2012
    Posts:
    25
    What I ended up doing is adding a script to the prefab object that makes it aware of its x/y position so it can fetch it's own tile from the tilemap.
     
  18. mattouBatou

    mattouBatou

    Joined:
    Sep 2, 2016
    Posts:
    20
    You could also get all tiles from 'ITilemap tilemap' argument in GetTileData to call RefreshAllTiles(). Then have this:

    Code (CSharp):
    1. if(Sprite && !Application.isPlaying)
    2. {
    3.     tileData.sprite = Sprite;
    4. }
    5. else
    6. {
    7.     tileData.sprite = null;
    8. }
    In GetTileData.

    Still not the most efficient way perhaps.
     
  19. soulareus

    soulareus

    Joined:
    Mar 2, 2014
    Posts:
    7
    Nobody wants to use the tilebrush. None of the solutions seem to work above properly for one reason or another. Please give us a prefabTile Unity!
     
    wenzhao and vzlomvl like this.
  20. MatthiasStuetzer

    MatthiasStuetzer

    Joined:
    Oct 5, 2016
    Posts:
    17
    Stumbled here looking for a good solution. Prefab tile would be most welcome. :)
     
  21. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
  22. bahaeddine666

    bahaeddine666

    Joined:
    Apr 3, 2019
    Posts:
    9
    Hey guys I have a simple question, maybe more simple than what you were discussing here , since I am very new to unity , can you initiate a prefab to a specific time !? If yes , how!?
     
  23. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    Unrelated to this thread, however, you can instantiate a prefab at any time using:
    Object.Instantiate
     
  24. Djayp

    Djayp

    Joined:
    Feb 16, 2015
    Posts:
    114
    "Unity 2019.3.0 Alpha 6

    • 2D: Add CreateTileFromPaletteAttribute to allow users to specify how and what Tiles are created when dragging and dropping assets to the Tile Palette Window
    • 2D: Add multi-texture support to SpriteShapeRenderer
    • 2D: Add multi-texture support to TilemapRenderer
    • 2D: Add toggle to allow changing of Z Position with GridBrush
    • 2D: Allow users to convert Prefabs to Tile Palettes by dragging and dropping a valid Prefab onto the Tile Palette Window
    • 2D: Expose GridPaintingState APIs to allow users to programatically change Tile Palette Painting states"
     
  25. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Finally :) Hope that will be working as expected.
     
  26. ShervinM

    ShervinM

    Joined:
    Sep 16, 2017
    Posts:
    67
    What makes a prefab a valid prefab? Downloaded the latest alpha, but cant seem to drag in prefabs to make tiles. Have you been able to make this work?
     
  27. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Have not tested it yet. I just hope they do that right way)
     
  28. ShervinM

    ShervinM

    Joined:
    Sep 16, 2017
    Posts:
    67
    Unfortunately it seem the new feature is for dragging in GameObject prefabs that have a Grid and Tilemap. In other words, its meant for painting onto a prefab grid. Not the otherway around :(
     
  29. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    557
    FYI, for what it is worth, I have moved away from using "prefab" tiles to spawn any prefab. The reason for this is the prefab instance becomes "tied" to the tile. So if you need to do something complex, like movement, you risk loosing the instance because SetTile will effectively spawn a new instance of the prefab. Conversely, clearing the tile will also destroy the instance.

    The way around this (And I imagine this new solution to be doing the same thing). Is to make a soft reference to gameobjects you need to instantiate. Anything that moves (or requires special behaviors that need to be modified on the fly) needs to be a standard gameobject, not a tile.

    For my purposes my "prefab" tiles merely store a reference to the object I need to instantiate. The tile as it exists is merely for editing purposes. I do this using a custom brush. The logic for this is follows:

    For all tiles:
    ON Paint:
    Determine if tile has a prefab reference
    YES: Instantiate the prefab reference where the tile would normally be painted. Don't paint the tile.
    NO: Paint the tile onto the tilemap.

    I would be glad to elaborate further.
     
    Last edited: Aug 2, 2019
  30. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Actually I asked for clarifying this, and got an answer
    Here you can read the thread.
    https://forum.unity.com/threads/exp...editor-package-in-2019-2.708848/#post-4870373

    Finally we will have the ability to drag and drop GO to tile pallete and draw them with prefab brush. (Almost like a tile)
    But then it will hapen i do not know)
     
    Last edited: Aug 26, 2019
    ShervinM likes this.
  31. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Meanwhile, anyone can use my package Called "RuntimePrefabTile".
    Readme file is inside.
     

    Attached Files:

    Last edited: Aug 26, 2019
    ShervinM likes this.
  32. unity_p1ZEBD9IvIhlOQ

    unity_p1ZEBD9IvIhlOQ

    Joined:
    Aug 24, 2018
    Posts:
    1
    I needed the same exact thing and here is what i came up with.

    I manually instantiate the prefab so that i can edit the properties of the gameobject in the scene. Deletion works by adding dummy tiles to the tilemap and then query it to see check if it has been deleted by the eraser tool.

    I did not test it very much but it seems to work.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEditor;
    4. using UnityEngine;
    5. using UnityEngine.Tilemaps;
    6.  
    7. [ExecuteInEditMode]
    8. [RequireComponent(typeof(Tilemap))]
    9. public class GOTileMap : MonoBehaviour
    10. {
    11.     [SerializeField, HideInInspector]
    12.     private List<GOTile> prefabs = new List<GOTile>();
    13.  
    14.     private Tilemap tilemap;
    15.     private Vector3 anchor = new Vector3(0.5f, 0.5f, 0);
    16.  
    17.     [System.Serializable]
    18.     public class GOTile
    19.     {
    20.         public GameObject Instance;
    21.         public Vector3Int Position;
    22.  
    23.         public GOTile(GameObject instance, Vector3Int position)
    24.         {
    25.             Instance = instance;
    26.             Position = position;
    27.         }
    28.     }
    29.     private class DummyTile : TileBase
    30.     {
    31.         public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    32.         {
    33.             return;
    34.         }
    35.     }
    36.  
    37.     void Start()
    38.     {
    39.         tilemap = GetComponent<Tilemap>();
    40.  
    41.         if (Application.isPlaying) {
    42.  
    43.             //Spawn all
    44.             foreach (GOTile tile in prefabs)
    45.             {
    46.                 GameObject clone = Instantiate(tile.Instance);
    47.                 clone.transform.parent = tile.Instance.transform.parent;
    48.                 clone.name = tile.Instance.name;
    49.                 tile.Instance.hideFlags = HideFlags.HideInHierarchy;
    50.                 tile.Instance.SetActive(false);
    51.             }
    52.         }
    53.         else
    54.         {
    55.             //Reset
    56.             foreach (GOTile tile in prefabs)
    57.             {
    58.                 tile.Instance.hideFlags = HideFlags.None;
    59.                 tile.Instance.SetActive(true);
    60.                 tilemap.SetTile(tile.Position, ScriptableObject.CreateInstance(typeof(DummyTile)) as DummyTile);
    61.             }
    62.         }
    63.     }
    64.  
    65.  
    66.     public void Spawn(GameObject prefab, Vector3Int position, Quaternion rotation)
    67.     {
    68.         if (Application.isPlaying) return;
    69.  
    70.         GOTile existing = prefabs.Find(cand => cand.Position == position);      
    71.         if (existing != null) {
    72.             DestroyImmediate(existing.Instance);
    73.             prefabs.Remove(existing);
    74.         }
    75.  
    76.         GameObject instance = Instantiate(prefab);
    77.         instance.transform.position = position + anchor;
    78.         instance.transform.rotation = rotation;
    79.         instance.transform.parent = transform;
    80.         prefabs.Add(new GOTile(instance, position));
    81.         tilemap.SetTile(position, ScriptableObject.CreateInstance(typeof(DummyTile)) as DummyTile);
    82.     }
    83.  
    84.  
    85.     void Update()
    86.     {
    87.         if (Application.isPlaying) return;
    88.  
    89.         prefabs.RemoveAll(
    90.             tile =>
    91.             {
    92.                 TileBase dummy = tilemap.GetTile(tile.Position);
    93.  
    94.                 if (!dummy)
    95.                 {
    96.                     DestroyImmediate(tile.Instance);
    97.                     return true;
    98.                 }
    99.                 return false;
    100.             }
    101.         );    
    102.     }
    103. }
    104.  
    105. public class PrefabTile : Tile
    106. {
    107.     [SerializeField] private GameObject prefab;
    108.  
    109.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    110.     {
    111.         color = prefab.GetComponentInChildren<SpriteRenderer>().color;
    112.         sprite = prefab.GetComponentInChildren<SpriteRenderer>().sprite;
    113.         base.GetTileData(position, tilemap, ref tileData);
    114.     }
    115.  
    116.     public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject tileGO)
    117.     {
    118.         if (tilemap.GetComponent<Transform>().gameObject.layer == 31) {
    119.             return base.StartUp(position, tilemap, tileGO);
    120.         }
    121.  
    122.         GOTileMap GOTileMap = tilemap.GetComponent<GOTileMap>();
    123.         if (!GOTileMap) {
    124.             GOTileMap = tilemap.GetComponent<Transform>().gameObject.AddComponent<GOTileMap>();
    125.         }
    126.  
    127.         GOTileMap.Spawn(prefab, position, tilemap.GetTransformMatrix(position).rotation);
    128.         return true;
    129.     }
    130.  
    131. #if UNITY_EDITOR
    132.     [MenuItem("Assets/Create/Tiles/Prefab Tile")]
    133.     public static void Create()
    134.     {
    135.         string path = EditorUtility.SaveFilePanelInProject("Save Prefab Tile", "New prefab tile", "asset", "Save prefab tile", "Assets");
    136.         if (path == "")
    137.             return;
    138.  
    139.         AssetDatabase.CreateAsset(CreateInstance<PrefabTile>(), path);
    140.     }
    141. #endif
    142. }
    143.  
    144. [CustomEditor(typeof(PrefabTile))]
    145. public class PrefabTileEditor : Editor
    146. {
    147.     SerializedProperty prefab;
    148.  
    149.     void OnEnable()
    150.     {
    151.         prefab = serializedObject.FindProperty("prefab");
    152.     }
    153.  
    154.     public override void OnInspectorGUI()
    155.     {
    156.         serializedObject.Update();
    157.         EditorGUILayout.PropertyField(prefab);
    158.         serializedObject.ApplyModifiedProperties();
    159.     }
    160. }
     
    Last edited: Feb 27, 2020
    ShervinM likes this.