Search Unity

ECS Prefabs

Discussion in 'Entity Component System' started by Cheerio, May 19, 2018.

  1. Cheerio

    Cheerio

    Joined:
    Aug 3, 2013
    Posts:
    19
    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

    Code (CSharp):
    1. // Setup prefabs
    2. public static class Prefabs
    3. {
    4.     public static Prefab Asteroid = new Prefab()
    5.         .AddPhysics()      
    6.         .Add(new Asteroid{})
    7.         .Add(new SceneLayer{})
    8.         .Add(new CircleSprite{});      
    9.  
    10.     public const long SHIP_RADIUS = 4000;
    11.     public static Prefab Ship = new Prefab()
    12.         .AddPhysics(radius:SHIP_RADIUS)
    13.         .Add(new Thruster{})
    14.         .Add(new Health{})
    15.         .Add(new Gun{})
    16.         .Add(new CircleSprite { _Radius = SHIP_RADIUS, _Color = Color.green });      
    17.  
    18.     public static Prefab AIShip = new Prefab(Ship)
    19.         .Add(new AI{});
    20.  
    21.     public static Prefab PlayerShip = new Prefab(Ship)
    22.         .Add(new PlayerInput{});
    23.  
    24.     public const long BULLET_RADIUS = 400;
    25.     public static Prefab Bullet = new Prefab()
    26.         .AddPhysics(radius:BULLET_RADIUS)
    27.         .Add(new SceneLayer{})
    28.         .Add(new Bullet{})
    29.         .Add(new CircleSprite { _Radius = BULLET_RADIUS, _Color = Color.yellow });      
    30.  
    31.     public static Prefab AddPhysics(this Prefab prefab, long radius = 1000)
    32.     {
    33.         return prefab
    34.                 .Add(new Position{})
    35.                 .Add(new Rotation{})
    36.                 .Add(new RigidBody{})
    37.                 .Add(new CircleCollider{_Radius = radius});
    38.     }
    39. }
    Code (CSharp):
    1.  // Spawn prefabs
    2. public class Example : ComponentSystem
    3. {
    4.     struct Data
    5.     {
    6.         public int Length;
    7.         public ComponentDataArray<Gun> _Guns;
    8.         public ComponentDataArray<Position> _Positions;      
    9.     }
    10.  
    11.     [Inject] Data _Data;
    12.  
    13.     protected override void OnCreateManager(int capacity)
    14.     {
    15.         InitializePrefabManager.Initialize();
    16.  
    17.         Prefabs.PlayerShip.Spawn()
    18.             .Set(new Position{_X = 100, _Y = 100});      
    19.     }
    20.  
    21.     protected override void OnUpdate()
    22.     {
    23.         for(int i = 0; i < _Data.Length; ++i)
    24.         {
    25.             PostUpdateCommands.Spawn(Prefabs.Bullet)
    26.                 .Set(_Data._Positions[i]);
    27.         }  
    28.     }
    29. }
     
    Last edited: May 22, 2018
  2. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    340
    Looks useful! Thanks for sharing.
     
  3. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    91
    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.
     
  4. Cheerio

    Cheerio

    Joined:
    Aug 3, 2013
    Posts:
    19
    Thank you for the feedback. I added support for spawning prefabs from EntityCommandBuffers and updated the example code.
     
  5. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    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)
    Code (CSharp):
    1. DefineEntity( "car" )
    2.   .Add<Rotation>()
    3.   .Add( new Position(){ Value = new float3(0, 1, 0) } )
    4.   .Add<Speed>();
    in OnUpdate: (spawn and overwrite defaults)
    Code (CSharp):
    1. Spawn("car").Set(new Speed{Value = 10});
    System code
    Code (CSharp):
    1.  
    2.   public abstract class EcsSystem : ComponentSystem{
    3.     protected abstract override void OnUpdate();
    4.  
    5.     private static Dictionary<string, EntityDefinition> defs
    6.       = new Dictionary<string, EntityDefinition>();
    7.  
    8.     protected EntityDefinition DefineEntity( String name ){
    9.       var def = new EntityDefinition();
    10.       defs.Add( name, def );
    11.       return def;
    12.     }
    13.  
    14.     public EcsSystem Spawn(string name){
    15.       EntityDefinition def = defs[name];
    16.       PostUpdateCommands.CreateEntity( def.GetArchetype( EntityManager ) );
    17.       def.Components.ForEach( cpi => cpi.PostUpdateSet( this ) );
    18.       return this;
    19.     }
    20.  
    21.     public void Set<T>(T cp)where T: struct, IComponentData{
    22.       PostUpdateCommands.SetComponent( cp );
    23.     }
    24.  
    25.   }
    26.  
    27.  
    28.   public class EntityDefinition{
    29.     private EntityArchetype _archetype;
    30.     public List<ComponentType> Types = new List<ComponentType>();
    31.     public readonly List<IComponentDataWrapper> Components = new List<IComponentDataWrapper>();
    32.  
    33.     public EntityArchetype GetArchetype(EntityManager em){
    34.       if( _archetype == default(EntityArchetype) )  _archetype = em.CreateArchetype(Types.ToArray());
    35.       return _archetype;
    36.     }
    37.  
    38.     public EntityDefinition Add<T>() where T : struct, IComponentData{
    39.       Types.Add( ComponentType.Create<T>() );
    40.       return this;
    41.     }
    42.  
    43.     public EntityDefinition Add<T>( T cd ) where T : struct, IComponentData{
    44.       Types.Add(ComponentType.Create<T>() );
    45.       Components.Add( new ComponentDataWrapper<T>( cd ) );
    46.       return this;
    47.     }
    48.   }
    49.  
    50.   public interface IComponentDataWrapper{
    51.     void PostUpdateSet(EcsSystem system);
    52.   }
    53.  
    54.   class ComponentDataWrapper<T> : IComponentDataWrapper where T : struct, IComponentData{
    55.     public T _Component;
    56.     public ComponentDataWrapper(T component){_Component = component;}
    57.     public void PostUpdateSet( EcsSystem sys ){ sys.Set(_Component); }
    58. }
     
    Last edited: May 22, 2018
    IC_ likes this.
  6. deplinenoise

    deplinenoise

    Unity Technologies

    Joined:
    Dec 20, 2017
    Posts:
    33
    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)
     
    xKosta, DrSpritz, MikeMarcin and 2 others like this.
  7. deplinenoise

    deplinenoise

    Unity Technologies

    Joined:
    Dec 20, 2017
    Posts:
    33
    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>)
     
    MikeMarcin and starikcetin like this.
  8. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    @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?
     
  9. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Right now the only reasonably well working way of using prefabs is to instantiate the entities from the game object prefab directly.
     
    georgetowersassets and twobob like this.
  10. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    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
     
  11. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    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.
     
  12. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    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 )
     
  13. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    91
    Did you manage to achieve your goal?
    I am also interested in this, doing everything in code is not really flexible :(
     
  14. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
  15. kumar3087

    kumar3087

    Joined:
    May 12, 2014
    Posts:
    2
    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
     
  16. ujinjinjin

    ujinjinjin

    Joined:
    May 8, 2016
    Posts:
    1
    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?
     
    DreamersINC likes this.
  17. coldasfrost979

    coldasfrost979

    Joined:
    Jun 9, 2018
    Posts:
    25
    I am also interested in the answer to this.
     
  18. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    Last edited: Jun 16, 2019
  19. Suike

    Suike

    Joined:
    Jul 25, 2013
    Posts:
    16
    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
     
  20. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    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.
     
  21. ju_my

    ju_my

    Joined:
    Mar 7, 2017
    Posts:
    23
    I'm trying to use this function but all i get instantiated is an empty entity. What do i miss?
    scrGameObject is a prefab with multiple authoring components on it and a ConvertToEntity Component
     
    Last edited: Jan 15, 2020
  22. LuisEGV

    LuisEGV

    Joined:
    Jul 21, 2015
    Posts:
    36
    Same behavior. This method just creates an empty entity. Help Please!

    Did you solve it or created a thread about this?