Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. 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.