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

Sharing logic between systems

Discussion in 'Entity Component System' started by Ziboo, Apr 5, 2018.

  1. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Hi,

    I'm new to the ECS pattern, and I kind of block on how I could achieve what I want.
    I have a PlayerActionSystem that when an Input is register, will check if there is a tile that will collide over the next move, if not, the player do a move.
    Looks like that:
    Code (CSharp):
    1.  public class PlayerActionSystem : ComponentSystem
    2.     {
    3.         [Inject] public PlayersGroupData PlayersGroup;
    4.         [Inject] public ObstaclesGroupData ObstaclesGroup;
    5.  
    6.         protected override void OnUpdate()
    7.         {
    8.             if (PlayersGroup.Length == 0)
    9.             {
    10.                 return;
    11.             }
    12.  
    13.             for (var i = 0; i < PlayersGroup.Length; ++i)
    14.             {
    15.                 var playerInput = PlayersGroup.Inputs[i];
    16.  
    17.                 var targetPosition = PlayersGroup.GridPositions[i];
    18.  
    19.  
    20.                 if (playerInput.MoveX != 0 || playerInput.MoveY != 0)
    21.                 {
    22.                     targetPosition.Value.x += playerInput.MoveX;
    23.                     targetPosition.Value.y += playerInput.MoveY;
    24.  
    25.  
    26.                     //Check Collision
    27.                     Entity obstacle = Entity.Null;
    28.                    
    29.                     if (ObstaclesGroup.Length > 0)
    30.                     {
    31.                         for (int j = 0; j < ObstaclesGroup.Length; j++)
    32.                         {
    33.                             if (ObstaclesGroup.GridPositions[j].Value == targetPosition.Value)
    34.                             {
    35.                                 obstacle = ObstaclesGroup.Entities[j];
    36.                                 break;
    37.                             }
    38.                         }
    39.                     }
    40.                    
    41.                     if (obstacle.Equals(Entity.Null))
    42.                     {
    43.                         //Move
    44.                         PostUpdateCommands.AddComponent(PlayersGroup.Entities[i], new MoveToTarget(targetPosition.Value));
    45.                     }
    46.                     else
    47.                     {
    48.                         //Do Damage
    49.                         if (EntityManager.HasComponent<Health>(obstacle))
    50.                         {
    51.                             PostUpdateCommands.AddComponent(obstacle, new DoDamage(1));
    52.                             PostUpdateCommands.AddSharedComponent(PlayersGroup.Entities[i], new TriggerAnimation("playerChop"));
    53.                         }
    54.                         //Move
    55.                         if (EntityManager.HasComponent<FoodTag>(obstacle))
    56.                         {
    57.                             PostUpdateCommands.AddComponent(PlayersGroup.Entities[i], new MoveToTarget(targetPosition.Value));
    58.                         }
    59.                     }
    60.                     PostUpdateCommands.AddComponent(PlayersGroup.Entities[i], new HasPlayTurn());
    61.                 }
    62.             }
    63.         }
    64.  
    65.         public struct PlayersGroupData
    66.         {
    67.             public int Length;
    68.             public EntityArray Entities;
    69.             public ComponentDataArray<GridPosition> GridPositions;
    70.             [ReadOnly] public ComponentDataArray<PlayerInput> Inputs;
    71.             [ReadOnly] public SubtractiveComponent<MoveToTarget> MoveToTarget;
    72.             [ReadOnly] public ComponentDataArray<TurnBasedTag> TurnBased;
    73.             [ReadOnly] public ComponentDataArray<IsActiveTurn> IsActiveTurn;
    74.             [ReadOnly] public SubtractiveComponent<HasPlayTurn> HasPlayTurn;
    75.         }
    76.  
    77.         public struct ObstaclesGroupData
    78.         {
    79.             public int Length;
    80.             public EntityArray Entities;
    81.             [ReadOnly]
    82.             public ComponentDataArray<GridPosition> GridPositions;
    83.             [ReadOnly]
    84.             public ComponentDataArray<BoardObstacle> BoardObstacle;
    85.         }
    86.     }
    The thing is that, I also want my EnemyAISystem to check for collision before they could move.
    Since I can access entities through Inject, I would have to inject and reproduce the same code in my EnemyAISystem which doesn't seem very efficient.

    How would you go to share "global" logic like that between systems ?

    Thanks
     
  2. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    It sounds like you want is events?

    * one system that generates collision events
    * two systems that consume the events

    Simplest way is to simply add either an empty "tag" component as an event and then use the other systems to iterate only over entities that have that tag component. (If you need to add data to the event you can easily just put that event payload into that component)
     
  3. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Thanks Joachim,

    I understand why you are saying and I though about that, but I would still need to inject those results in to each system, which I'm not a huge fan.

    Also in this scenario I don't really need collisions events, I just need my AI to be aware of his surrounding to select the right action to do.

    The best scenario I could think of, is kind of a static function, that will return tiles around the targeted entities, but since systems runs OnUpdate, you can't really call a function like that and get the result right away.

    EDIT: Btw I'm trying to convert RogueLike2D to ECS, and trying to seperate systems to best I can, for them to be reusable
     
  4. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546

    Don't move your ai and player in the specific systems directly. Just set a "PrepairMovement" component at this point and handle the real movement in a 3rd system that handles the collision too.
    Maybe this approach will help you...
     
  5. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Thanks for the answer.
    I will think about that, maybe it is the solution to add a new system inbetween.