About Me 👈

Spawning Prefabs with Unity ECS

How to instantiate copies of a prefab as entities, at runtime, with Unity ECS.

unitycsharptutorial

Created on November 7, 2019. Last updated on February 25, 2020.

Video of spawning prefabs with Unity ECS.

Spawning entity-associated prefabs with Unity ECS is a bit complicated, especially if it can occur at any point during your game. Moreover, initiating spawning from other (ECS) systems and even MonoBehaviors complicates matters when it comes to achieving thread safety and conventional usage with the tools provided. Thankfully, none of these challenges will prevent us from achieving our goal.

Note: If at any point you find that this tutorial uses too much jargon, don't worry! Try my Getting Started with Unity DOTS tutorial first.

Now, I'll refer to the to-be-spawned subjects as people. Feel free to call them characters, NPCs, agents, or whatever. And by the way, when thinking in terms of ECS, really there are two modes. There's "authoring" and "runtime." Authoring means to, well, author stuff. That's when we're in the editor, moving things around and adjusting parameters. It also includes conversion of GameObjects into entities upon startup, which are then used for the remainder of runtime.

We can easily convert prefabs with this code:

[GenerateAuthoringComponent]
struct PersonPrefab : IComponentData
{
    public Entity Value;
}

Next, you want to create an empty GameObject in your scene. You should add two scripts to it:

  1. This PersonPrefab script, defined above. Drag and drop your prefab onto its Value field.
  2. The built-in Convert To Entity script. For its Conversion Mode, elect for Convert And Inject Game Object if there are other MonoBehaviours attached to it that you want to keep running after conversion; otherwise, select Convert And Destroy.

With a means to convert prefabs, we can use them when enqueuing spawning. Yes, enqueue, meaning we will use a thread-safe, queue data structure. But I already coded that for you, so all you have to do is install my DOTS spawning package—here's how:

The spawning code is a stand-alone UPM package, meaning you can import it directly into your project as long as you're using >=2019.3. To take advantage of this, just copy one of the below Git URLs:

  • HTTPS: https://github.com/reeseschultz/ReeseUnityDemos.git#spawning
  • SSH: git@github.com:reeseschultz/ReeseUnityDemos.git#spawning

Then go to Window ⇒ Package Manager in the editor. Press the + symbol in the top-left corner, and then click on Add package from git URL. Paste the text you copied and finally click Add.

It'll take a little while for the import to work. It should install the required dependencies and appropriate versions for you.

Afterward, here's an example of what you can do:

using Reese.Spawning;
using Unity.Entities;
using UnityEngine;

namespace SomeNamespace {
    class PersonSpawner : MonoBehaviour
    {
        // Get the default world containing all entities:
        EntityManager entityManager => World
            .DefaultGameObjectInjectionWorld
            .EntityManager;

        void Start()
        {
            // Get the entity associated with the prefab:
            var prefabEntity = entityManager
                .CreateEntityQuery(typeof(PersonPrefab))
                .GetSingleton<PersonPrefab>()
                .Value;

            // Enqueue spawning (SpawnSystem and Spawn are from Reese.Spawning):
            SpawnSystem.Enqueue(new Spawn()
                .WithPrefab(prefabEntity) //  Optional prefab entity.
                .WithComponentList( // Optional comma-delimited list of IComponentData.
                    new SomeComponent
                    {
                        Charisma = 6,
                        Intelligence = 3,
                        Luck = 14,
                        Strength = 16,
                        Wisdom = 9
                    }
                )
                .WithBufferList( // Optional comma-delimited list of IBufferElementData.
                    new SomeBufferElement { }
                ),
                50 // Optional number of times to spawn (default is once).
            );
        }
    }
}

Your choice namespace, and how you populate the component and buffer lists, is entirely up to you.

And no doubt, you may be wondering how to spawn in the Update method via button click, and furthermore how to randomize positions with the built-in Translation component and Unity.Mathematics.Random. To see how that works, see the spawning demo scene in my Unity demo repo on GitHub. Its location is Assets/Scenes/SpawnDemo.unity. That demo specifically relies on the redundantly-named SpawnDemoSpawner code. I left all that out of this tutorial since there are a number of ways you may want to enqueue spawning at runtime.

You should know that this tutorial used to be much more complicated, as the spawning was specific to certain component types before I generalized it as a package. While I normally would avoid abstraction at such a high level with data-oriented design, I have found that creating and maintaining a bunch of specific spawning code is tedious, time-consuming, and error-prone. For the development of commercial games at least, centralizing spawning into one package seems to be most effective. To see how I implemented it, just take a look at the SpawnSystem code. Feel free to roll your own implementation based on mine, or submit a PR if you would like to add something.

I hope this helps!