What Is It?
A Factory centralises object creation. Clients ask for "a Goblin" through a method like Create(EnemyType.Goblin) and receive an IEnemy back; they never write new Goblin() or reference the Goblin prefab themselves.
Because the client only knows the interface, you can add a Dragon, swap the Orc prefab or route creation through an object pool without touching any spawner, wave controller or debug tool that calls the factory.
In Unity the factory usually owns a lookup of prefabs — often a ScriptableObject with a list of (EnemyType, prefab) entries — and calls Instantiate plus any post-setup (stats, team, difficulty scaling) in one place.
When Is It Used?
Use it when several systems create the same family of objects: wave spawners, a level editor, cheat menus and save-game loading all need "make me enemy X".
It is valuable when creation involves more than Instantiate — injecting dependencies, applying difficulty multipliers, registering with a manager — and you do not want that logic copy-pasted.
Do not bother for one-off objects only ever created in one spot. A serialized prefab reference and Instantiate is clearer.
Interactive Demo
Click the controls and watch the objects collaborate. The console mirrors what the C# code below would log.
Code
using System.Collections.Generic;
using UnityEngine;
namespace Patterns.Creational.Factory
{
/// <summary>
/// Client. It knows enemy *types* and the factory, but never
/// references Goblin, Orc or Dragon classes or prefabs.
/// </summary>
public class EnemySpawner : MonoBehaviour
{
[SerializeField] private EnemyFactory factory;
[SerializeField] private Transform[] spawnPoints;
private readonly List<IEnemy> alive = new();
private void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1)) Spawn(EnemyType.Goblin);
if (Input.GetKeyDown(KeyCode.Alpha2)) Spawn(EnemyType.Orc);
if (Input.GetKeyDown(KeyCode.Alpha3)) Spawn(EnemyType.Dragon);
if (Input.GetKeyDown(KeyCode.Space))
foreach (IEnemy enemy in alive)
enemy.Attack();
}
private void Spawn(EnemyType type)
{
Transform point = spawnPoints[Random.Range(0, spawnPoints.Length)];
IEnemy enemy = factory.Create(type, point.position, transform);
if (enemy != null)
alive.Add(enemy);
}
}
}using UnityEngine;
namespace Patterns.Creational.Factory
{
public enum EnemyType { Goblin, Orc, Dragon }
/// <summary>
/// What every client sees. Spawners, wave controllers and UI only
/// ever talk to this interface, never to Goblin or Dragon directly.
/// </summary>
public interface IEnemy
{
EnemyType Type { get; }
int Health { get; }
void Spawn(Vector3 position);
void Attack();
}
/// <summary>Shared MonoBehaviour base so prefabs can be referenced in a list.</summary>
public abstract class Enemy : MonoBehaviour, IEnemy
{
[SerializeField] private int baseHealth = 10;
public abstract EnemyType Type { get; }
public int Health { get; protected set; }
public virtual void Spawn(Vector3 position)
{
transform.position = position;
Health = baseHealth;
gameObject.SetActive(true);
}
public abstract void Attack();
}
}using UnityEngine;
namespace Patterns.Creational.Factory
{
/// <summary>Fast, weak melee enemy.</summary>
public class Goblin : Enemy
{
public override EnemyType Type => EnemyType.Goblin;
public override void Attack() => Debug.Log("Goblin stabs for 3.");
}
/// <summary>Slow, tough melee enemy.</summary>
public class Orc : Enemy
{
public override EnemyType Type => EnemyType.Orc;
public override void Attack() => Debug.Log("Orc smashes for 8.");
}
/// <summary>Flying boss that breathes fire.</summary>
public class Dragon : Enemy
{
[SerializeField] private ParticleSystem fireBreath;
public override EnemyType Type => EnemyType.Dragon;
public override void Spawn(Vector3 position)
{
base.Spawn(position + Vector3.up * 4f); // dragons arrive airborne
}
public override void Attack()
{
if (fireBreath != null) fireBreath.Play();
Debug.Log("Dragon breathes fire for 25.");
}
}
}using System;
using System.Collections.Generic;
using UnityEngine;
namespace Patterns.Creational.Factory
{
/// <summary>
/// Data-driven factory stored as an asset. Designers map each
/// EnemyType to a prefab; code only calls Create(type).
/// </summary>
[CreateAssetMenu(menuName = "Patterns/Enemy Factory")]
public class EnemyFactory : ScriptableObject
{
[Serializable]
private struct Entry
{
public EnemyType type;
public Enemy prefab;
}
[SerializeField] private List<Entry> prefabs = new();
private Dictionary<EnemyType, Enemy> lookup;
private void OnEnable() => lookup = null; // rebuild after domain reload / edits
public IEnemy Create(EnemyType type, Vector3 position, Transform parent = null)
{
lookup ??= BuildLookup();
if (!lookup.TryGetValue(type, out Enemy prefab))
{
Debug.LogError($"EnemyFactory: no prefab registered for {type}.");
return null;
}
Enemy instance = Instantiate(prefab, parent);
instance.Spawn(position);
return instance;
}
private Dictionary<EnemyType, Enemy> BuildLookup()
{
var map = new Dictionary<EnemyType, Enemy>();
foreach (Entry e in prefabs)
map[e.type] = e.prefab;
return map;
}
}
}Advantages & Disadvantages
+ Advantages
- Clients depend on
IEnemy, not concrete classes — low coupling. - One place to add new types, change prefabs or plug in pooling.
- Data-driven: designers can register new prefabs in a ScriptableObject without code changes.
− Disadvantages
- Adds an indirection layer; jumping to "where is this created?" takes an extra hop.
- A giant
switchin the factory can become its own maintenance hotspot if not data-driven. - Type keys (enums or strings) can drift out of sync with registered prefabs and fail at runtime.
Tips
- 01Back the factory with a
Dictionary<EnemyType, Enemy>built once inAwakefrom a serialized list — lookups stay O(1) and Inspector-editable. - 02Have
Createreturn the interface (IEnemy) so callers cannot reach into concrete-type details. - 03Combine with Object Pooling: the factory can fetch from a
UnityEngine.Pool.ObjectPool<T>instead of instantiating each time. - 04Log a clear error (and return
null) when a key has no prefab, rather than throwing deep inside a spawner. - 05For families of related objects (e.g. forest vs desert enemy sets) graduate to an Abstract Factory with one factory per theme.