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.

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.