Search Unity

Request for World.AddManager

Discussion in 'Entity Component System' started by tertle, Jul 6, 2018.

Thread Status:
Not open for further replies.
  1. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I'd like to be able to do my own injections when creating managers to better integrate with existing solutions.

    Basically just need the back end of the internal creation code to be split like this

    World.cs
    Code (CSharp):
    1.      
    2. public void AddManager<T>(T manager) where T : ScriptBehaviourManager
    3. {
    4.     var type = typeof(T);
    5.     var capacity = GetCapacityForType(type);
    6.    
    7.     m_BehaviourManagers.Add(manager);
    8.     AddTypeLookup(type, manager);
    9.  
    10.     try
    11.     {
    12.         manager.CreateInstance(this, capacity);
    13.     }
    14.     catch
    15.     {
    16.         RemoveManagerInteral(manager);
    17.         throw;
    18.     }
    19.  
    20.     ++Version;
    21. }
    22.  
     
  2. capyvara

    capyvara

    Joined:
    Mar 11, 2010
    Posts:
    80
  3. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    If you noticed I was the one who first posted in that thread how to create systems.

    The whole point is I want to be able to do my own injections without knowing what I want to inject until execution (and no not the inject attribute that only works for other systems.)

    The code that I need added is literally only the code in the original post. It's taken from the actual ecs source code.
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I wrote an extension method for this (using reflection) until this is (hopefully) supported
    It converts the reflection into delegates so after initial setup it's basically as fast as just calling a method.

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Reflection;
    4. using BovineLabs.Toolkit.Reflection;
    5. using Unity.Entities;
    6.  
    7. namespace BovineLabs.Toolkit.ECS
    8. {
    9.     public static class WorldExtensions
    10.     {
    11.         private static bool _setup;
    12.         private static Func<World, List<ScriptBehaviourManager>> _behaviourManagers;
    13.         private static Action<World, Type, ScriptBehaviourManager> _addTypeLookup;
    14.         private static Action<World, ScriptBehaviourManager> _removeManagerInteral;
    15.         private static Action<ScriptBehaviourManager, World, int> _createInstance;
    16.         private static Action<World, int> _setVersion;
    17.  
    18.         private static void Setup()
    19.         {
    20.             _behaviourManagers = CreateBehaviourManagerGet();
    21.             _addTypeLookup = CreateAddTypeLookup();
    22.             _removeManagerInteral = CreateRemoveManagerInteral();
    23.             _createInstance = CreateCreateInstance();
    24.             _setVersion = CreateVersionSetter();
    25.      
    26.             _setup = true;
    27.         }
    28.  
    29.         private static Func<World, List<ScriptBehaviourManager>> CreateBehaviourManagerGet()
    30.         {
    31.             var addTypeLookupMethodInfo = typeof(World).GetField("m_BehaviourManagers", BindingFlags.NonPublic | BindingFlags.Instance);
    32.  
    33.             if (addTypeLookupMethodInfo == null)
    34.                 throw new NullReferenceException("AddTypeLookup changed");
    35.  
    36.             return addTypeLookupMethodInfo.CreateGetter<World, List<ScriptBehaviourManager>>();
    37.         }
    38.  
    39.         private static Action<World, Type, ScriptBehaviourManager> CreateAddTypeLookup()
    40.         {
    41.             var addTypeLookupMethodInfo = typeof(World).GetMethod("AddTypeLookup",
    42.                 BindingFlags.NonPublic | BindingFlags.Instance);
    43.  
    44.             if (addTypeLookupMethodInfo == null)
    45.                 throw new NullReferenceException("AddTypeLookup changed");
    46.  
    47.             return (Action<World, Type, ScriptBehaviourManager>) Delegate.CreateDelegate(typeof(Action<World, Type, ScriptBehaviourManager>), null, addTypeLookupMethodInfo);
    48.         }
    49.  
    50.         private static Action<World, ScriptBehaviourManager> CreateRemoveManagerInteral()
    51.         {
    52.             var removeManagerInteralMethodInfo = typeof(World).GetMethod("RemoveManagerInteral",
    53.                 BindingFlags.NonPublic | BindingFlags.Instance);
    54.          
    55.             if (removeManagerInteralMethodInfo == null)
    56.                 throw new NullReferenceException("RemoveManagerInteral changed");
    57.  
    58.             return (Action<World, ScriptBehaviourManager>) Delegate.CreateDelegate(typeof(Action<World, ScriptBehaviourManager>), null, removeManagerInteralMethodInfo);
    59.         }
    60.  
    61.         private static Action<ScriptBehaviourManager, World, int> CreateCreateInstance()
    62.         {
    63.             var createInstanceMethodInfo = typeof(ScriptBehaviourManager).GetMethod("CreateInstance",
    64.                 BindingFlags.NonPublic | BindingFlags.Instance);
    65.      
    66.             if (createInstanceMethodInfo == null)
    67.                 throw new NullReferenceException("CreateInstance changed");
    68.          
    69.             return (Action<ScriptBehaviourManager, World, int>) Delegate.CreateDelegate(typeof(Action<ScriptBehaviourManager, World, int>), null, createInstanceMethodInfo);
    70.         }
    71.  
    72.         private static Action<World, int> CreateVersionSetter()
    73.         {
    74.             var versionFieldInfo = typeof(World).GetProperty("Version",
    75.                 BindingFlags.Public | BindingFlags.Instance);
    76.      
    77.             if (versionFieldInfo == null)
    78.                 throw new NullReferenceException("Version changed");
    79.  
    80.             MethodInfo methodInfo = versionFieldInfo.SetMethod;
    81.      
    82.             return (Action<World, int>)Delegate.CreateDelegate(typeof(Action<World, int>), null, methodInfo);
    83.         }
    84.  
    85.         public static void AddManager(this World world, ScriptBehaviourManager manager)
    86.         {
    87.             if (!_setup)
    88.                 Setup();
    89.      
    90.             _behaviourManagers(world).Add(manager);
    91.             _addTypeLookup(world, manager.GetType(), manager);
    92.  
    93.             try
    94.             {
    95.                 _createInstance(manager, world, 10);
    96.             }
    97.             catch
    98.             {
    99.                 _removeManagerInteral(world, manager);
    100.                 throw;
    101.             }
    102.  
    103.             _setVersion(world, world.Version + 1);
    104.         }
    105.     }
    106. }
    To use, just call

    Code (CSharp):
    1. var system = new MyCustomSystem();
    2. Unity.Entities.World.Active.AddManager(system);
    I personally use it so I can setup my systems using Zenject
    I have a simple extension to BindSystems

    Code (CSharp):
    1.         public static ScopeConditionCopyNonLazyBinder BindSystem<T>(this DiContainer container) where T : ComponentSystemBase
    2.         {
    3.             return container.BindInterfacesAndSelfTo<T>().FromResolveGetter<World, T>(w =>
    4.             {
    5.                 var system = container.Instantiate<T>();
    6.                 w.AddManager(system);
    7.                 return system;
    8.             });
    9.         }
    And I bind my systems like this

    Code (CSharp):
    1.             Container.Bind<Unity.Entities.World>().FromInstance(Unity.Entities.World.Active).AsSingle();
    2.  
    3.             // ...
    4.  
    5.             Container.BindSystem<FogSystem>().AsSingle().NonLazy();
    6.             Container.BindSystem<FogVisionSystem>().AsSingle().NonLazy();
    7.             Container.BindSystem<FogSetupSystem>().AsSingle().NonLazy();
    8.             Container.BindSystem<RevealedSystem>().AsSingle().NonLazy();
    9.             Container.BindSystem<ObservationSystem>().AsSingle().NonLazy();    
    And all my dependency will resolve themselves

    Remember to call ScriptBehaviourUpdateOrder.UpdatePlayerLoop(World);

    Also Unity could break at anytime
     
  5. Kasax

    Kasax

    Joined:
    Mar 2, 2019
    Posts:
    2
    As of `Entities 0.1.1` (Maybe earlier) which has been out for a while now, you can now just use AddSystem().
    No need for reflection anymore.

    See the extension below which also changes syntax to latest Extenject:

    Code (CSharp):
    1.    
    2. public static ScopeConcreteIdArgConditionCopyNonLazyBinder BindSystem<T>(this DiContainer container) where T : ComponentSystemBase {
    3.   return container.BindInterfacesAndSelfTo<T>().FromResolveGetter<World, T>(world => {
    4.     var system = container.Instantiate<T>();
    5.     world.AddSystem(system);
    6.     var simulationSystemGroup = world.GetExistingSystem<SimulationSystemGroup>();
    7.     simulationSystemGroup.AddSystemToUpdateList(system);
    8.      
    9.     return system;
    10.   });
    11. }
    12.  
    In your installer you can now do:

    Code (CSharp):
    1.   public class ECSInstaller : Installer {
    2.     public override void InstallBindings() {
    3.       var world = World.Active;
    4.       Container.BindInstance(world);
    5.       Container.BindSystem<MySystem>().AsSingle().NonLazy();
    6.      
    7.       ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);
    8.     }
    9.   }
    Note that if you use UniRx you will need to reinitalize the player loop (see // https://github.com/neuecc/UniRx/issues/354)
     
    oakus, koshelev_unity and bobbaluba like this.
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Not sure why you needed to bump a more than 2 year old post.

    I would advice against doing anything that was suggested in the original posts by me, it was written at a time when entities used inject attributes.
     
  7. Kasax

    Kasax

    Joined:
    Mar 2, 2019
    Posts:
    2
    I bumped the post because right now (still) this post seems to be the only reference easily found online on how to combine Zenject + ECS.

    Note that Zenject's README points to this post: https://github.com/modesttree/Zenject#ecs-integration.

    I was hoping to save other people some time if they do follow the documentation route I followed.
     
Thread Status:
Not open for further replies.