I’m back once again, with more news on Office Mayhem. As I’ve said before, in this game you compete with local players completing office tasks, such as delivering a report to the outbox.

I use that specific example because, very early in the project, the team ran into a problem. When you make a report and put it in the outbox, you have two fewer pieces of paper. After a few of these tasks, if you didn’t create new paper with the copier, you couldn’t complete the task anymore.

This problem was fixed temporarily before I was added to the team: new paper (and any other necessary objects) occasionally spawn in on desks. They just unceremoniously appeared, which was quite jarring if you happen to see it spawn.

The full fix for this problem was proposed to be an elevator, which would open every once in a while, spitting out whatever items you needed. The idea is that some other office worker was delivering them, but simply tossed them into the room from the elevator.

Well, the time had come to implement this item spawner, and the job fell onto me.


In creating the spawner, the first problem to overcome was determining how many of each object we need. To do that, I need to know how many copies of the object are in the scene. This was also encountered in the temporary fix, of course, and the solution was Unity’s FindAllObjectsWithTag function.

This is fine, but the problem is that it was being run in Update, meaning it was being run every frame. Because Unity has to step through every game object that exists when this is called, that is very inefficient.

We fix this by calling it every several seconds, rather than once per frame.

But that didn’t fix the whole problem.

You see, before, the spawning system was explicitly searching for objects with the Paper or Outbox tags, because those were those were the only things that could be thrown out in the trash can. Our goal was to allow anything to be thrown out, and checking for each tag is just insane, adding much more code every time we created a new item.

And not only that, but different items needed different minimum acceptable amount in the scene: there should be a lot of paper, so it’s minimum is 15; but there do not need to be 15 computers. So this not only needed to be extendable for future items for searching purposes, but also allowing different minimums.

So, my solution: create a struct that defines the object’s type and the minimum number of them, and have a list of those structs:

// Enum for items spawnable by the elevator
[System.Serializable]
public enum ElevatorSpawnableItem
{
    Computer,
    Garbage,
    Outbox,
    Paper,
    Phone,
    Stapler,
    CoffeeMaker
}

// Private struct for the list
[System.Serializable]
public struct ObjectEntry
{
    public ElevatorSpawnableItem type;
    public int min;
}

// List of tags of objects that need to stay around
public List<ObjectEntry> requiredObjectTags;

// Will hold objects that are needed; key is the item type,
// and value is the number to spawn
Dictionary<ElevatorSpawnableItem, int> bucket;

When it is time to check for missing items, these ObjectEntries are stepped through one by one, and FindAllObjectsWithTag is called, the size of that list is compared to the minimum number for that object.

You might be thinking, though, “But don’t you still need to do a

huge, gross switch statement on that enum and input each tag by hand? And worse, the name of the object to create from the resources!”

But this is where the best part of this system comes in — the tags and the names of the prefabs were both formatted identically to the names of those enum values above; this means, because of a wonderful feature of C#, calling ToString on this enum is all you need for both the tag and the prefab of the item to create… no massive switch statement required.

This brings down the Check() function down to one, fairly simple for-each loop:

// Check for objects of the required types
void Check()
{
    foreach (ObjectEntry oe in requiredObjectTags)
    {
        // Get the number of the object in the scene
        int numSpawned = GameObject.FindGameObjectsWithTag(oe.type.ToString()).Length;

        // If that number is less than the minimum, add the difference to the bucket
        if (numSpawned < oe.min)
        {
            bucket[oe.type] = oe.min - numSpawned;
        }
    }
}

Now it’s just a matter of looping through the bucket and spawning all the objects. That, unfortunately, is a rather busy and large function, due to spawn force, spawn direction variance, playing sound effects, and animations for opening and closing the elevator doors, so I won’t recount the whole thing here; but the spawning system is now done.

But… the required objects need to be added in the script manually. Because the requiredObjectTags list is of a struct, it couldn’t be exposed to the editor by default. [System.Serializable] allows it to be exposed, but clumsily. My next task: create an editor interface so the designers can edit it as much as they want. But for now, it works. And it can do stuff like this.

spitting_paper.gif
Advertisements