Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Alternative for Start, OnTriggerEnter ...

Discussion in 'Entity Component System' started by DoYouRockBaby, Jul 29, 2018.

  1. DoYouRockBaby

    DoYouRockBaby

    Joined:
    May 21, 2018
    Posts:
    9
    Hello World,

    (sorry if my english is not perfect)

    I'm currently translating my code to an ecs code, but i'm facing a difficulty. I miss the old monobehaviour functions. Especialy Start(), the collision functions (CollisionEnter, ...) and the trigger functions (TriggerEnter, ...). I'm using the hybrid method with MonoBehaviour as component.

    For Start function, i overload OnStartRunning(), it work's well but the problem is that this function is only called once at the instanciation of the scene. So when i'm adding a new GameObject, it is not called wich can be disturbing. Is there any simple alternative ?

    For trigger, i made this temporary personnal solution but this is not very elegant, i use it same as a component in my system. I guess there is a better way to do :
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. namespace Physic
    5. {
    6.     [RequireComponent(typeof(Collider2D))]
    7.     public class Trigger2D : MonoBehaviour
    8.     {
    9.         public LayerMask collisionMask = ~0;
    10.  
    11.         public class TriggerEventArgs : EventArgs
    12.         {
    13.             public Collider2D thisCollider;
    14.             public Collider2D collider;
    15.         }
    16.  
    17.         public delegate void TriggerHandler(Trigger2D sender, TriggerEventArgs e);
    18.  
    19.         public event TriggerHandler Entered;
    20.         public event TriggerHandler Exited;
    21.         public event TriggerHandler Stayed;
    22.  
    23.         void OnTriggerEnter2D(Collider2D collider)
    24.         {
    25.             if(Entered != null && collisionMask.value == 1 << collider.gameObject.layer)
    26.             {
    27.                 Entered.Invoke(this, new TriggerEventArgs()
    28.                 {
    29.                     thisCollider = GetComponent<Collider2D>(),
    30.                     collider = collider
    31.                 });
    32.             }
    33.         }
    34.  
    35.         void OnTriggerExit2D(Collider2D collider)
    36.         {
    37.             if (Exited != null && collisionMask.value == 1 << collider.gameObject.layer)
    38.             {
    39.                 Exited.Invoke(this, new TriggerEventArgs()
    40.                 {
    41.                     thisCollider = GetComponent<Collider2D>(),
    42.                     collider = collider
    43.                 });
    44.             }
    45.         }
    46.  
    47.         void OnTriggerStay2D(Collider2D collider)
    48.         {
    49.             if (Stayed != null && collisionMask.value == 1 << collider.gameObject.layer)
    50.             {
    51.                 Stayed.Invoke(this, new TriggerEventArgs()
    52.                 {
    53.                     thisCollider = GetComponent<Collider2D>(),
    54.                     collider = collider
    55.                 });
    56.             }
    57.         }
    58.     }
    59. }
    And a exemple of use :
    Code (CSharp):
    1. namespace Gameplay.Miscelanous
    2. {
    3.     public class SelfDestroy : MonoBehaviour
    4.     {
    5.         [HideInInspector]
    6.         public Trigger2D.TriggerHandler handler;
    7.     }
    8.  
    9.     public class SelfDestroyTriggerSystem : ComponentSystem
    10.     {
    11.         public struct Components
    12.         {
    13.             public SelfDestroy destroySelf;
    14.             public Trigger2D trigger;
    15.         }
    16.  
    17.         protected override void OnStartRunning()
    18.         {
    19.             foreach (var e in GetEntities<Components>())
    20.             {
    21.                 e.destroySelf.handler = (s, ev) =>
    22.                 {
    23.                     Object.Destroy(e.destroySelf.gameObject);
    24.                 };
    25.  
    26.                 e.trigger.Entered += e.destroySelf.handler;
    27.             }
    28.         }
    29.  
    30.         protected override void OnStopRunning()
    31.         {
    32.             foreach (var e in GetEntities<Components>())
    33.             {
    34.                 if (e.destroySelf.handler != null)
    35.                 {
    36.                     e.trigger.Entered -= e.destroySelf.handler;
    37.                 }
    38.             }
    39.         }
    40.  
    41.         protected override void OnUpdate()
    42.         {
    43.         }
    44.     }
    45. }

    This example is also a good exemple of why the OnStartRunning function is not perfect for my use.


    thank you in advance for you answers.
     
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,754
    Personally I always consider using monobehaviour methods, like Start, Update, FixedUpdate OnCollision, etc. only for fast prototyping. My scripts are driven by master script, and relevant objects / classes up to this time, had own Initialisation method. Which was called from master script. This ensures, I don't have unwanted game objects running in the scene. You can have polled list of objects/classes you want to run and simply iterate through them. Now you one step closer to ECS + JS (job system) implementation, dropping GameObjects Starts methods etc.

    Like somebody has mentioned before, using ECS, you need ditch whole concept of OOP and Unity Game objects. I always thought, using Unity3D game objects are evil ;) Is hard to refactor code, if overused.
     
  3. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    This is totaly dangerous!

    Don't use events inside ComponentSystems for collisions. Also don't use OnStartRunning and OnStopRunning for init your entities! This function will only be called if the system will start/stop to run


    What you can do for events is creating event entities:
    Code (CSharp):
    1.  
    2. struct CollisionInfo : IComponentData {
    3.     public Entity source;
    4.     public Entity other;
    5. }
    6. struct CollisionEnter : IComponentData { }
    7. struct CollisionStay : IComponentData { }
    8. struct CollisionExit : IComponentData { }
    9.  
    10. public class Trigger2D : MonoBehaviour
    11.     {
    12.         public LayerMask collisionMask = ~0;
    13.  
    14.         void OnTriggerEnter2D(Collider2D collider)
    15.         {
    16.             if(Entered != null && collisionMask.value == 1 << collider.gameObject.layer)
    17.             {
    18.                  EntityManager entityManager = World.active.GetExistingManager<EntityManager>();
    19.                  Entity eventEntity = entityManager.Create(typeof(CollisionInfo), typeof(CollisionEnter));
    20.                  entityManager.SetComponent(eventEntity,
    21.                          new CollisionInfo {
    22.                                 source = GetComponent<GameObjectEntity>().entity,
    23.                                 source = collider.GetComponent<GameObjectEntity>().entity,
    24.                          });
    25.             }
    26.         }
    27.     }
    28. }
    29.  
    30. class PhysicEventSystem : ComponentSystem {
    31.        struct EventData {
    32.               public readonly int Length;
    33.               public ComponentDataArray<CollisionEnter> collisionEvents;
    34.               public ComponentDataArray<CollisionInfo> collisionInfos;
    35.               public EntityArray entities;
    36.        }
    37.  
    38.        [Inject] EventData eventData;
    39.  
    40.        public override void OnUpdate() {
    41.              for(int i = 0; i<eventData.Length; i++) {
    42.                  CollisionInfo collisionInfo = eventData.collisionInfos[i];
    43.                  XY someComponent  = EntityManager.GetComponent<XY>(collisionInfo.source);
    44.                  //....
    45.                  PostUpdateCommand.DestroyEntity(eventData.entities[i]);
    46.              }
    47.        }
    48. }
    49.  
    For initialization you can use SystemStateComponentData
    Code (CSharp):
    1.  
    2.  
    3. class SomeSystem: ComponentSystem {
    4.        struct InitSomeComponent : ISystemStateComponentData { }
    5.  
    6.        struct Data {
    7.               public readonly int Length;
    8.               public ComponentDataArray<SomeComponent> someComponents;
    9.               public SubstractiveComponent<InitSomeComponent> initSomeComponents;
    10.               public EntityArray entities;
    11.        }
    12.  
    13.        [Inject] Data data;
    14.  
    15.        public override void OnUpdate() {
    16.              for(int i = 0; i<data.Length; i++) {
    17.                  SomeComponent someComponent = data.someComponents[i];
    18.                  //....
    19.                  PostUpdateCommand.AddComponent(data.entities[i], new InitSomeComponent());
    20.              }
    21.        }
    22. }
    23.  

    This is clean and uses the ecs design pattern
     
    Antypodish likes this.