ECS Prefabs

Here is my attempt at setting up a prefab system for ECS. It properly sets up archetypes for all your prefabs and allows you to store any instance data you want for each prefab. It also allows you to easily add batches of components or have prefabs inherit from each other. There is an extra cache miss for each component you add which is not default initialized but in most cases this should hopefully be ok. I thought this might be useful to other people so I’m sharing it here. The full source code can be found here: https://github.com/jseidenz/ecsprefabs

// Setup prefabs
public static class Prefabs
{
    public static Prefab Asteroid = new Prefab()
        .AddPhysics()      
        .Add(new Asteroid{})
        .Add(new SceneLayer{})
        .Add(new CircleSprite{});      

    public const long SHIP_RADIUS = 4000;
    public static Prefab Ship = new Prefab()
        .AddPhysics(radius:SHIP_RADIUS)
        .Add(new Thruster{})
        .Add(new Health{})
        .Add(new Gun{})
        .Add(new CircleSprite { _Radius = SHIP_RADIUS, _Color = Color.green });      

    public static Prefab AIShip = new Prefab(Ship)
        .Add(new AI{});

    public static Prefab PlayerShip = new Prefab(Ship)
        .Add(new PlayerInput{});

    public const long BULLET_RADIUS = 400;
    public static Prefab Bullet = new Prefab()
        .AddPhysics(radius:BULLET_RADIUS)
        .Add(new SceneLayer{})
        .Add(new Bullet{})
        .Add(new CircleSprite { _Radius = BULLET_RADIUS, _Color = Color.yellow });      

    public static Prefab AddPhysics(this Prefab prefab, long radius = 1000)
    {
        return prefab
                .Add(new Position{})
                .Add(new Rotation{})
                .Add(new RigidBody{})
                .Add(new CircleCollider{_Radius = radius});
    } 
}
 // Spawn prefabs
public class Example : ComponentSystem
{
    struct Data
    {
        public int Length;
        public ComponentDataArray<Gun> _Guns;
        public ComponentDataArray<Position> _Positions;       
    }

    [Inject] Data _Data;

    protected override void OnCreateManager(int capacity)
    {
        InitializePrefabManager.Initialize();

        Prefabs.PlayerShip.Spawn()
            .Set(new Position{_X = 100, _Y = 100});       
    }

    protected override void OnUpdate()
    {
        for(int i = 0; i < _Data.Length; ++i)
        {
            PostUpdateCommands.Spawn(Prefabs.Bullet)
                .Set(_Data._Positions[i]);
        }   
    }
}
4 Likes

Looks useful! Thanks for sharing.

Thanks for sharing !
To be fully useful, I think you should add some way to spawn prefabs during system Update, for instance using PostUpdateCommands., etc.

Thank you for the feedback. I added support for spawning prefabs from EntityCommandBuffers and updated the example code.

Here is my version also using fluent interface
Instead of using extension methods i’m inheriting ComponentSystem
and also i’m using PostUpdateCommands

Usage is almost the same
in OnCreateManager: (define archetype and set defaults)

DefineEntity( "car" )
  .Add<Rotation>()
  .Add( new Position(){ Value = new float3(0, 1, 0) } )
  .Add<Speed>();

in OnUpdate: (spawn and overwrite defaults)

Spawn("car").Set(new Speed{Value = 10});

System code

  public abstract class EcsSystem : ComponentSystem{
    protected abstract override void OnUpdate();

    private static Dictionary<string, EntityDefinition> defs
      = new Dictionary<string, EntityDefinition>();

    protected EntityDefinition DefineEntity( String name ){
      var def = new EntityDefinition();
      defs.Add( name, def );
      return def;
    }
 
    public EcsSystem Spawn(string name){
      EntityDefinition def = defs[name];
      PostUpdateCommands.CreateEntity( def.GetArchetype( EntityManager ) );
      def.Components.ForEach( cpi => cpi.PostUpdateSet( this ) );
      return this;
    }

    public void Set<T>(T cp)where T: struct, IComponentData{
      PostUpdateCommands.SetComponent( cp );
    }
 
  }
 
 
  public class EntityDefinition{
    private EntityArchetype _archetype;
    public List<ComponentType> Types = new List<ComponentType>();
    public readonly List<IComponentDataWrapper> Components = new List<IComponentDataWrapper>();

    public EntityArchetype GetArchetype(EntityManager em){
      if( _archetype == default(EntityArchetype) )  _archetype = em.CreateArchetype(Types.ToArray());
      return _archetype;
    }
 
    public EntityDefinition Add<T>() where T : struct, IComponentData{
      Types.Add( ComponentType.Create<T>() );
      return this;
    }

    public EntityDefinition Add<T>( T cd ) where T : struct, IComponentData{
      Types.Add(ComponentType.Create<T>() );
      Components.Add( new ComponentDataWrapper<T>( cd ) );
      return this;
    }
  }
 
  public interface IComponentDataWrapper{
    void PostUpdateSet(EcsSystem system);
  }
 
  class ComponentDataWrapper<T> : IComponentDataWrapper where T : struct, IComponentData{
    public T _Component;
    public ComponentDataWrapper(T component){_Component = component;}
    public void PostUpdateSet( EcsSystem sys ){ sys.Set(_Component); }
}
1 Like

Hi,

Are you aware that you can instantiate a gameobject (prefab) and have it automatically create all the IComponentData-derived components on the ECS side without creating any actual game objects?

See EntityManagerExtensions.Instantiate(this EntityManager entityManager, GameObject srcGameObject)

5 Likes

Also, once an entity has been created, it can be used to instantiate clones in batch, which is vastly more efficient and should be the default for most simulations.

See EntityManager.Instantiate(Entity, NativeArray<Entity>)

2 Likes

@deplinenoise - is there a way for that to work across worlds? IE: Let’s say we have a world with no systems that we use to store prefabs entities in so they go untouched by normal worlds with systems. Or is there a universal way to freeze/exclude an entity in a world from normal processing?

Right now the only reasonably well working way of using prefabs is to instantiate the entities from the game object prefab directly.

2 Likes

this tread isn’t about game object prefabs this is about pure entities

it is useful to be able to construct and configure entity and store it in some form
and then to instantiate it several times

for example let’s take a bullet in a shooter game, when you spawn it, it’ll fly forward until hit something, and after hitting anything is removed, and when you have a machine gun you have to spawn lots of this bullets, also you bullets may have some default configuration options, like damage, size, speed depending on weapon used to spawn it

great way to do it is to clone a hidden pre-constructed and pre-configured single bullet entity many times (separate for each kind of weapon)

EntityManager.Instantiate is not good for this because original is not hidden (all systems affect original)
EntityArchetype is not good because it do not store default component values not pre-configured (only list of component types)
GameObject is not good because it is not an entity

this is what we are talking about,
not about using game object prefabs

The approach we are probably going to take is to have some kind of default “Inactive” component which is default disabled by all ComponentGroups. You could probably do exactly this manually for the time being.

4 Likes

In my case performance is not a key factor, so i’ve took a different approach:

I’m constricting EntityDefinition objects at startup using chained commands
this object is a container for 1) entity archetype and 2) list of components with defaults

and at runtime i’m constructing entities from this object with one method Spawn()
which adds several PostUpdateCommand-s

( thinking now about custom gui editor for it )

Did you manage to achieve your goal?
I am also interested in this, doing everything in code is not really flexible :frowning:

no news - custom editor for EntityDefinition is on hold
the rest is here ECS Prefabs

Hi,
I am not so good in programming, but I have been working for games using unity 3d,
tried to find a way to translate the below piece of code,
Please help me to translate the below piece of code in ECS

public GameObject pref;

public void CreateObjs()
{

for (int i = 0; i < 10000;i++)
{
Instantiate(pref);
}

}

also there are 2 more components attached with the prefab, Rigidbody and BoxCollider.
Thanks

Is there a way to use it with hybrid ECS? E.g. System that inherited from ComponentSystem is going to create objects on scene from prefab using EntityManagerExtensions.Instantiate. Or it is impossible right now?

1 Like

I am also interested in the answer to this.

this is a very old post, there is a prefab component now to instantiate “pure” entities

if you need a system that creates gemeObject each time you create an entity, you need to write a system for that yourself, there is no ready solution, but there is a way to convert gameobject to entity see New SubScene & ConvertToEntity workflows

Could you tell me how you would write the prefabs on the 1st post using the prefab component?
From what I see it doesn’t allow you to set default values for IComponentDatas

when your entity has a prefab component on it, it is ignored by all systems, but you still can clone it using
EntityManager.Instantiate(), all clones receive the exact same set of components as prefab had excluding prefab component which is removed on clone during instantiation.