Selecting Prefabs in Prefab Brush.

For reference, this is what a prefab brush is in Unity 2017.3:

I’m making a top-down styled 2D game. I need to be able to spawn in prefabs using a brush. I made a prefab brush and added two prefabs to it, unfortunately, it doesn’t let me select which prefab I want to paint with. Instead, it randomly selects one and places it down. So my question is how do I select which prefab to place down, I don’t want to have to make 100 different brushes for each prefab. Is there a way to add prefabs to your tile pallet?

3 Likes

I’m also looking for a solution to this.

@sean244 As far as I know there isn’t a solution for this, at least not without some heavy customization of the tilemap system.

I’ve made that exact comment on this Unity video (

) and I even got lectured by some random person about how I shouldn’t complain about it. ¯_(ツ)_/¯

Any how, my recommendation is to look elsewhere. I recommend the Super Tilemap Editor. It has everything you need and with a much better workflow.

Same problem

Yet another example of Unity “seems” like feature is there to make real game, but in real practice usage, it falls short.

Hi everyone,
I’m relative new in Unity and I’m trying de Tilemap 'cos I find is a very powerful tool. In some point I found myself in the same problem you describe, but I discover that in the script of Prefab brush that came with the tool, is the way the Brush “paint randomly”, but I made a few changes to the script and I found IT CAN BE POSSIBLE, I don’t know if there’s a problem changin it or what and I know probably is not the best solution of there’s a solution in another blog or site but I wanna post my solution here.

I post the script’s code here, ready to “Copy-Paste” in the file in the folder TileMap => Brushes => Prefab Brushes:

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace UnityEditor
{
    [CreateAssetMenu(fileName = "Prefab brush", menuName = "Brushes/Prefab brush")]
    [CustomGridBrush(false, true, false, "Prefab Brush")]
    public class PrefabBrush : UnityEditor.Tilemaps.GridBrush
    {
        private const float k_PerlinOffset = 100000f;
        public GameObject[] m_Prefabs;
        public float m_PerlinScale = 0.5f;
        public int m_Z;
        private GameObject prev_brushTarget;
        private Vector3Int prev_position;


        public int prefabToTPrint = 0;
        public bool randomPrefab = true;

        public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
        {
            Transform itemInCell = GetObjectInCell(grid, brushTarget.transform, new Vector3Int(position.x, position.y, m_Z));
            if (itemInCell != null) return;
            if (position == prev_position)
            {
                return;
            }
            prev_position = position;
            if (brushTarget) {
                prev_brushTarget = brushTarget;
            }
            brushTarget = prev_brushTarget;

            // Do not allow editing palettes
            if (brushTarget.layer == 31)
                return;

            int index;

            if (!randomPrefab) //If there is a position that you want to print
            {
                if (m_Prefabs.Length <= prefabToTPrint) //If there nos prefab in that position
                {
                    Debug.Log("No item in array of Prefabs, setting to Random");
                    index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, m_PerlinScale, k_PerlinOffset) * m_Prefabs.Length), 0, m_Prefabs.Length - 1);
                    randomPrefab = true; //setting to Random
                }
                else
                    index = prefabToTPrint;
            }
            else
                index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, m_PerlinScale, k_PerlinOffset)*m_Prefabs.Length), 0, m_Prefabs.Length - 1);

            GameObject prefab = m_Prefabs[index];
            GameObject instance = (GameObject) PrefabUtility.InstantiatePrefab(prefab);
            if (instance != null)
            {
                Undo.MoveGameObjectToScene(instance, brushTarget.scene, "Paint Prefabs");
                Undo.RegisterCreatedObjectUndo((Object)instance, "Paint Prefabs");
                instance.transform.SetParent(brushTarget.transform);
                instance.transform.position = grid.LocalToWorld(grid.CellToLocalInterpolated(new Vector3Int(position.x, position.y, m_Z) + new Vector3(.5f, .5f, .5f)));
            }
        }

        public override void Erase(GridLayout grid, GameObject brushTarget, Vector3Int position)
        {
            if (brushTarget)
            {
                prev_brushTarget = brushTarget;
            }
            brushTarget = prev_brushTarget;
            // Do not allow editing palettes
            if (brushTarget.layer == 31)
                return;

            Transform erased = GetObjectInCell(grid, brushTarget.transform, new Vector3Int(position.x, position.y, m_Z));
            if (erased != null)
                Undo.DestroyObjectImmediate(erased.gameObject);
        }

        private static Transform GetObjectInCell(GridLayout grid, Transform parent, Vector3Int position)
        {
            int childCount = parent.childCount;
            Vector3 min = grid.LocalToWorld(grid.CellToLocalInterpolated(position));
            Vector3 max = grid.LocalToWorld(grid.CellToLocalInterpolated(position + Vector3Int.one));
            Bounds bounds = new Bounds((max + min)*.5f, max - min);

            for (int i = 0; i < childCount; i++)
            {
                Transform child = parent.GetChild(i);
                if (bounds.Contains(child.position))
                    return child;
            }
            return null;
        }

        private static float GetPerlinValue(Vector3Int position, float scale, float offset)
        {
            return Mathf.PerlinNoise((position.x + offset)*scale, (position.y + offset)*scale);
        }
    }

    [CustomEditor(typeof(PrefabBrush))]
    public class PrefabBrushEditor : UnityEditor.Tilemaps.GridBrushEditor
    {
        private PrefabBrush prefabBrush { get { return target as PrefabBrush; } }

        private SerializedProperty m_Prefabs;
        private SerializedObject m_SerializedObject;

        protected override void OnEnable()
        {
            base.OnEnable();
            m_SerializedObject = new SerializedObject(target);
            m_Prefabs = m_SerializedObject.FindProperty("m_Prefabs");
        }

        public override void OnPaintInspectorGUI()
        {
            m_SerializedObject.UpdateIfRequiredOrScript();
            prefabBrush.m_PerlinScale = EditorGUILayout.Slider("Perlin Scale", prefabBrush.m_PerlinScale, 0.001f, 0.999f);
            prefabBrush.m_Z = EditorGUILayout.IntField("Position Z", prefabBrush.m_Z);
            EditorGUILayout.PropertyField(m_Prefabs, true);
           
            prefabBrush.randomPrefab = EditorGUILayout.Toggle("Random Prefab", prefabBrush.randomPrefab);
            prefabBrush.prefabToTPrint = EditorGUILayout.IntField("Prefab to Print", prefabBrush.prefabToTPrint);
           

            m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
        }
    }
}

I added two new option:

  • Random Prefab: If it’s active, the Brush will act as always will paint a random prefab, if it’s not active It will take the prefab in the position that we indicate in the next parameter.
  • Prefab to Print: the position in the array of the prefab that we wanna print.

5974091--641279--upload_2020-6-12_15-41-50.png

It’s not pretty but for me it work.

I hope this help you. Sorry for my English but I don’t very good at.

1 Like