U
tab 04World & Game Loop7 lessons — tap to open
03 / 07 · 9 min
World & Game Loop

Modular props and grid snapping

your project so far

Assets/

Scenes/

Arena.unity

Terrain

Scripts/

PropScatter.cs

Prefabs/

module kit (6 pieces)prefab variants

Project settings

baked lightmaps

you already havethis lesson adds

You already built a kit like this once, in tab 01: P_Cube, P_Sphere, P_Capsule. Same idea, bigger pieces, and this time they have to fit against each other.

Professional levels are not modelled as one giant mesh. They are assembled from a small kit of pieces that snap together on a grid.

animated diagram

Every piece is 1, 2 or 4 metres, so anything fits against anything.

Set the grid before you model anything

  1. 1

    Open Edit▸Grid and Snap Settings and set Move to 1, 1, 1 and Rotate to 90.

  2. 2

    Hold Ctrl/Cmd while dragging a handle to snap in those steps.

  3. 3

    Model every module so its pivot sits at a corner, not the centre. This is the same trap as the cube at Y = 0.5 in tab 01 — pivot placement decides everything.

  4. 4

    Every module faces the same way (+Z forward) so rotating by 90° always works.

A starter kit

  • Floor 4×4, wall 4×3, wall with doorway, wall with window, corner pillar, stair 2×3.

  • Six pieces build an entire building. Ten pieces build a village.

  • ProBuilder (free from Package Manager) lets you model these inside Unity without leaving the editor.

Prefab variants — one kit, many themes

Make the grey blockout first. Then create variants that only change the material. Fix a bug in the base prefab and every variant inherits the fix.

  1. 1

    Right-click P_Wall → Create → Prefab Variant → name it P_Wall_Stone.

  2. 2

    Open the variant, change only the material. Everything else stays linked to the parent.

  3. 3

    Later, adding a collider to P_Wall adds it to P_Wall_Stone and P_Wall_Wood at the same time.

Scatter props without hand-placing 300 rocks

PropScatter.cs — run it from the Inspector
using UnityEngine;

public class PropScatter : MonoBehaviour
{
    [SerializeField] private GameObject[] prefabs;
    [SerializeField] private int count = 120;
    [SerializeField] private float radius = 40f;
    [SerializeField] private LayerMask groundMask;
    [SerializeField] private Vector2 scaleRange = new Vector2(0.8f, 1.4f);

    [ContextMenu("Scatter")]            // right-click the component → Scatter
    public void Scatter()
    {
        Clear();

        for (int i = 0; i < count; i++)
        {
            Vector2 flat = Random.insideUnitCircle * radius;
            Vector3 from = transform.position + new Vector3(flat.x, 50f, flat.y);

            // drop a ray straight down to find the ground
            if (!Physics.Raycast(from, Vector3.down, out RaycastHit hit, 200f, groundMask))
                continue;

            // skip cliffs — props on a vertical wall look broken
            if (Vector3.Angle(hit.normal, Vector3.up) > 30f) continue;

            GameObject prefab = prefabs[Random.Range(0, prefabs.Length)];
            GameObject go = Instantiate(prefab, hit.point, Quaternion.identity, transform);

            go.transform.rotation = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
            go.transform.localScale = Vector3.one * Random.Range(scaleRange.x, scaleRange.y);
        }
    }

    [ContextMenu("Clear")]
    public void Clear()
    {
        for (int i = transform.childCount - 1; i >= 0; i--)
            DestroyImmediate(transform.GetChild(i).gameObject);
    }
}
  • [ContextMenu] adds the method to the component's right-click menu, so you can run it in the editor without pressing Play.

  • Raycasting down onto the ground means props follow the terrain exactly, including after you resculpt.

  • Random rotation and scale is what stops 120 identical rocks from looking like 120 identical rocks.

  • DestroyImmediate, not Destroy — Destroy does nothing outside Play mode.