Lots of Chairs

I recently attended the AWE live event where one of the speakers had placed prizes under a couple of the seats for the in-person attendees. Unfortunately for me (and many others) I was attending virtually but was inspired to create the Lots of Chairs world in Altspace.

The Unity store had a free chair model which I initially hand copied into rows and columns over a 100×100 meter ground plane. The first version wasn’t too bad but the chairs came in a little small. After scaling up the chair models it was clear that I would have recreate the world again to avoid overlapping geometry.

I took this opportunity to learn a bit about creating editor scripts in Unity. With this newly acquired knowledge I cobbled together a script that would place my chairs on a grid and allow me to randomize the offsets and rotation just a bit to make the chairs look hand-placed.

Here’s my ChairGridBuilder code that’s attached to an empty GameObject in the scene.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ChairGridBuilder : MonoBehaviour
{
    public GameObject obj;
    public int columns = 10;
    public int rows = 10;
    public float columnSpacing = 0.5f;
    public float rowSpacing = 1f;
    public float rowNoise = .01f;
    public float columnNoise = .01f;
    public float rotationNoise = 0.01f;
    public float chairScale = 1.2f;


    Vector3 spawnPoint;
    public Transform chairsParent;

    public void BuildChairGrid()
    {
        for (int c = 0; c < columns; ++c)
        {
            for (int r = 0; r < rows; ++r)
            {
                float colPos = c * columnSpacing + Random.Range(-columnNoise, columnNoise);
                float rowPos = r * rowSpacing + Random.Range(-rowNoise, rowNoise);
                spawnPoint = new Vector3(colPos, 0, rowPos);
                GameObject o = Instantiate(obj, spawnPoint, Quaternion.Euler(0, Random.Range(-rotationNoise, rotationNoise), 0));
                o.transform.localScale *= chairScale;
                o.transform.SetParent(chairsParent);
            }
        }

    }

    public void ClearChairs()
    {
        var children = new List<GameObject>();
        foreach (Transform child in chairsParent.transform) children.Add(child.gameObject);
        children.ForEach(child => MonoBehaviour.DestroyImmediate(child));
    }
}

And here’s the code for the companion editor script.

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(ChairGridBuilder))]
public class ChairGridBuilderEditor : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();
        ChairGridBuilder myScript = (ChairGridBuilder)target;
        if(GUILayout.Button("Build Grid"))
        {
            myScript.BuildChairGrid();
        }
        if(GUILayout.Button("Clear Chairs"))
        {
            myScript.ClearChairs();
        }
    }

}

The code above created an inspector panel that allowed me to iterate quickly on the settings.

Looking back I think the next time I create an editor like this I’ll use a ScriptableObject to hold the settings. When the AltspaceVR uploader strips out the scripts I have to re-add the script to the object and tweak the settings to make any changes to the template.

Once the chairs were in place, I darkened the world, added black fog and a couple of colliders so you can’t reach the end of the world – sort of giving the illusion that the chairs go on forever.

With 155 columns and 84 rows that makes for 13,020 instances of the chair model. Fortunately the chair model has a couple levels of detail with final level being fully culled from the world just out of sight beyond the fog – otherwise this would bring my Quest 2 headset to its knees very, very quickly.

So given that this world was created to provide a place to hide some secrets, I’ve placed a secret under one of the 13,000+ chairs in that world. Good hunting!