Object construction with factory method

factory method in unity3d

How to structure your code to produce loads of objects? Learn the Factory Method and how to implement it in Unity3d!

One of the most common things to do in making a game is instantiating objects at runtime. Be it bullets from a gun, enemies rushing forward or instances of software classes, sooner or later you’ll need to do it.

This is one of those classic problems of computer science that have been solved over and over again. The solution to it I’m going to talk about today is the Factory Method (not to be confused with the Abstract Factory, which is way more general in its application).

What’s a Factory Method?

factory-method-pattern

Basically it’s when you use an inherited method (either from a superclass or an interface) to create for you an instance of a Product. The Product will be set up just as you specified with the arguments that are given to the Factory Method.

For instance if you have an EnemyFactory superclass, you could have a OgreFactory implement the FactoryMethod() for it. Then you could have an Ogre orc=datOgreFactory.FactoryMethod(100) to have orc‘s hit points set to 100.
Of course the GoblinFactory is another subclass of EnemyFactory but will still give you a 100 hp hulkling of a goblin if you ask for it with a Goblin hobGoblin=datGoblinFactory.FactoryMethod(100). It’s important to keep in mind that both Ogre and Goblin are one same kind of superclass: Enemy. This means they will share a common usage in their DieForLoot() function so that the kill procedure doesn’t need to know their specifics.

If you want a more technical definition, the internet is filled with it but I’d rather recommend to cut to the source and read it straight from the book written by the Gang of Four.

When to factory?

That’s more of a critical issue worth discussing. My personal answer would be to use it every single time you have more than exactly one thing to instantiate and most of the times you actually instantiate just one thing.

If you want dem clones, make a factory!
If you want dem clones, make a factory!

When you construct an instance of something twice, you are duplicating code and sure as hell it’s better not to write the same thing twice.

As for the reason I’d recommend to write one even if you are doing an instantiation just once:

  • if it’s something that will be a part of gameplay are you really sure you won’t be putting another one somewhere else?
  • Are you really sure you don’t need to test the instantiation itself indipendently?

Chances are, you will, and copy-pasting a bit of boilerplate code upfront is way better than refactoring after.

Also, it helps with respecting the single responsibility principle in the code: if instantiating that object was the only job of the class it’d be a factory, wouldn’t it?

But be careful: in some cases you might need to move even further and go full Abstract Factory. You might ask yourself:

  • Do I need to abstract everything or is it ok to work on concrete instances?
  • Do I need to hide my concrete code (i.e.: with a .dll)?
  • Do I need a factory of factories?

If you answered no to that, there’s no need for too much abstraction and you can just use the good old Factory Method.

blueprints-1837238_1920

How to build one in Unity3d

When working with Unity we have the usual problem of handling the references, which entails serious design decisions over how the factory should be realized. Plus in this case we have an additional issue: interface references won’t show up in the inspector, not even with a SerializeField .

Why? because just as generics (with the exception of the hardcoded List<T> ) interface references don’t get serialized in Unity, unless you write a workaround or pay sixty bucks …or at least five.

So this code:

public class FactoryUser : MonoBehaviour
{
    [SerializeField]
    IFactory thisWontSerialize;
    [SerializeField]
    MonoBehaviour factory;

will be rendered in the inspector like this:

immagine

So how to do a factory? Let’s begin with the interface (of course you can also make a superclass and skip half of the issues).

public interface IFactory 
{
    GameObject FactoryMethod(object[] parameters);
}

Why an array of objects? because we don’t know what we’ll need, so better have the thing that can do everything. A more type-safe approach would be to define a dedicated struct or class.

To have a working example we’ll implement a concrete factory that instantiates a number of childs of a main prefab:

public class ConcreteFactory : MonoBehaviour, IFactory
{
    [SerializeField]
    GameObject mainPrefab;
    [SerializeField]
    GameObject childPrefab;

First of all,  we add the references so that they can be seen in the inspector. Of course we’re going to make this class a MonoBehaviour, because to use the inspector is either that or ScriptableObjects and this is not a ScriptableObject tutorial. And I love the inspector.

inspector unity
He obviously prefers a platonic love 🙁
    public GameObject FactoryMethod(object[] parameters)
    {
        GameObject mainObject = Instantiate(mainPrefab);
        int amount = (int)parameters[0];
        for (int i = 0; i < amount; i++)
        {
            GameObject childObject = Instantiate(childPrefab);
            childObject.transform.parent = mainObject.transform;
        }
        return mainObject;
    }

This is the method that implements the IFactory interface. What we now do is:

  1. Instantiate a copy of the main prefab.
  2. Dumbly take for granted that the first parameter really IS an int
  3. proceed to a runtime error to instantiate and reparent his child objects
  4. witness in horror as performance sinks since we didn’t use pooling here

Now that the concrete factory is done let’s see it in action:

public class FactoryUser : MonoBehaviour
{
    [SerializeField]
    MonoBehaviour factory;

Wait, why a MonoBehaviour reference? We don’t want any runtime checks, do we? This should be type-safe and checked at compile-time!

    public void OnValidate()
    {
        if (!(factory is IFactory))
        {
            Debug.LogError("Wrong reference type");
            factory = null;
        }
    }

Well, not really at compile time, we just need it to be done while the game isn’t running. So here’s a check that runs every time the field is modified in the inspector. Of course if you want to inject the factory instance or have it passed some other way, you can use an IFactory variable instead.

This is just a workaround to have both type-safety and serialization after all, here’s another one if you don’t like it this way.

workaround unity interface serialization

But we’ll also avoid having to write a cast every time we use it:

    IFactory Factory { get { return factory as IFactory; } }

So we now can write:

    public void Start()
    {
        object[] parameters = new object[1];
        parameters[0] = 2;
        Factory.FactoryMethod(parameters);
    }

And finally, here it’s at work. Easy as pie. So easy it will be a pleasure to test and spam everywhere (provided you pooled everything).

Where’s the advantage of using this approach? Say your level designer wants to change the enemy type that is spawned in front of the castle gates, he can do it in the inspector simply by changing the factory that is referenced in the castle gate spawn component. No coding required, just a drag&drop.

That’s all folks!

Here’s a Unity project where you can grab al the code and see it in action, plus scroll down for a copy-pasteable version of it.

I hope you liked this tutorial, please tell me if you find this kind of guide interesting or not, either here or on my Twitter. If you don’t want to miss my next thing consider subscribing to my newsletter.

P.S.: I’m currently looking for a job, if you are interested here’s my portfolio.

public class ConcreteFactory : MonoBehaviour, IFactory
{
    [SerializeField]
    GameObject mainPrefab;
    [SerializeField]
    GameObject childPrefab;

    public GameObject FactoryMethod(object[] parameters)
    {
        GameObject mainObject = Instantiate(mainPrefab);
        int amount = (int)parameters[0];
        for (int i = 0; i < amount; i++)
        {
            GameObject childObject = Instantiate(childPrefab);
            childObject.transform.parent = mainObject.transform;
        }
        return mainObject;
    }
}
public interface IFactory 
{
    GameObject FactoryMethod(object[] parameters);
}
public class FactoryUser : MonoBehaviour
{
    [SerializeField]
    MonoBehaviour factory;
    IFactory Factory { get { return factory as IFactory; } }

    public void OnValidate()
    {
        if (!(factory is IFactory))
        {
            Debug.LogError("Wrong reference type");
            factory = null;
        }
    }

    public void Start()
    {
        object[] parameters = new object[1];
        parameters[0] = 2;
        Factory.FactoryMethod(parameters);
    }
}
Share
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Leave a Reply

Your email address will not be published.