Prefab tile

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:

using UnityEngine;
using UnityEngine.Tilemaps;

[CreateAssetMenu]
public class PrefabTile : UnityEngine.Tilemaps.TileBase
{
    public Sprite Sprite; //The sprite of tile in a palette and in a scene
    public GameObject Prefab; //The gameobject to spawn

    public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    {
        if (Sprite) tileData.sprite = Sprite; // Asigning sprite
        tileData.gameObject = Prefab; // Assigning prefab
    }

    public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    {
        // Streangly the position of gameobject starts at Left Bottom point of cell and not at it center
        go.transform.position += Vector3.up * 0.5f + Vector3.right * 0.5f;
                   
        return base.StartUp(position, tilemap, go);
    }

}

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.

10000000929492ā€“473726ā€“RuntimePrefabTile.unitypackage (1.96 KB)

1 Like

Maybe try

if(Sprite && !Application.isPlaying)
{
    tileData.sprite = Sprite;
}
else
{
    tileData.sprite = null;
}

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)

I have made tile invisible by setting it animation data:

   public override bool GetTileAnimationData(Vector3Int location, ITilemap tileMap, ref TileAnimationData tileAnimationData)
    {
        tileAnimationData.animatedSprites = new Sprite[] { null};
        tileAnimationData.animationSpeed = 0;
        tileAnimationData.animationStartTime = 0;
        return true;
    }

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.

Here is final code.

using UnityEngine;
using UnityEngine.Tilemaps;

[CreateAssetMenu]
public class PrefabTile : UnityEngine.Tilemaps.TileBase
{
    public Sprite Sprite; //The sprite of tile in the palette
    public GameObject Prefab; //The gameobject to spawn

 
    public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    {
        // Assign variables
               if (!Application.isPlaying) tileData.sprite = Sprite;
        else tileData.sprite = null;

        if (Prefab) tileData.gameObject = Prefab;
    }

    public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    {
        // Streangly the position of gameobject starts at Left Bottom point of cell and not at it center
       // TODO need to add anchor points  (vertical and horisontal (left,centre,right)(top,centre,bottom))
        go.transform.position += Vector3.up * 0.5f + Vector3.right * 0.5f;
        return true;
    }

    public override bool GetTileAnimationData(Vector3Int location, ITilemap tileMap, ref TileAnimationData tileAnimationData)
    {
        // Make sprite of tile invisiable
        tileAnimationData.animatedSprites = new Sprite[] { null};
        tileAnimationData.animationSpeed = 0;
        tileAnimationData.animationStartTime = 0;
        return true;
    }

}

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!?

4 Likes

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.

2 Likes

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.

easier Is better

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

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?

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.

2 Likes

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:

Updating to the latest version of unity fixed the error message for me

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.

  • 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.
  • 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.
  • 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:

using System;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace UnityEngine.Tilemaps
{
    /// <summary>
    /// A Tile that creates a GameObject at runtime.
    /// </summary>
    [Serializable]
    public class PrefabTile : TileBase
    {
        /// <summary>
        /// The GameObject that will be created by the Tile.
        /// </summary>
        [SerializeField] public GameObject Prefab;

        /// <summary>
        /// The Sprite that will be displayed in the Editor.
        /// </summary>
        private Sprite Sprite => Prefab.GetComponent<SpriteRenderer>().sprite;

        /// <summary>
        /// Allows the TileData for this Tile to be modified.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="tilemap"></param>
        /// <param name="tileData"></param>
        public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
        {
            tileData.gameObject = Prefab;
            tileData.sprite = Sprite;
        }
 
        /// <summary>
        /// Allows the GameObject for this Tile to be modified when the game starts.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="tilemap"></param>
        /// <param name="gameObject"></param>
        /// <returns></returns>
        public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject gameObject)
        {
            gameObject.transform.position += new Vector3(0.5f, 0.5f, 0);
                   
            return base.StartUp(position, tilemap, gameObject);
        }
    
#if UNITY_EDITOR
        /// <summary>
        /// Registers a menu item that creates a new PrefabTile asset.
        /// </summary>
        [MenuItem("Assets/Create/Prefab Tile")]
        public static void CreatePrefabTile()
        {
            string path = EditorUtility.SaveFilePanelInProject(
                "Save Prefab Tile",
                "New Prefab Tile",
                "asset",
                "Save Prefab Tile",
                "Assets"
            );

            if (path == "")
            {
                return;
            }

            AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<PrefabTile>(), path);
        }
#endif
    }
}

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?

This is what Iā€™m wondering right now. Have you found an easy way to do this?

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.

You could also get all tiles from ā€˜ITilemap tilemapā€™ argument in GetTileData to call RefreshAllTiles(). Then have this:

if(Sprite && !Application.isPlaying)
{
    tileData.sprite = Sprite;
}
else
{
    tileData.sprite = null;
}

In GetTileData.

Still not the most efficient way perhaps.

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!

2 Likes

Stumbled here looking for a good solution. Prefab tile would be most welcome. :slight_smile: