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. Dismiss Notice

ECS Game Programming Patterns

Discussion in 'Entity Component System' started by SomeCode, Nov 4, 2018.

  1. SomeCode

    SomeCode

    Joined:
    Jul 5, 2013
    Posts:
    32
    Hey guys! As someone relatively new to ECS, one thing that I find myself struggling with quite a bit is that I have almost zero patterns on which to fall back when I encounter familiar situations.

    I'm sure a lot of you are familiar with https://www.gameprogrammingpatterns.com/, and while that website provides a great foundation for tackling real world scenarios, to me it doesn't seem that many of these concepts map one-to-one with the ECS paradigm. I'm sure there are golden nuggets out there that I haven't yet discovered, but almost all examples of how to use ECS in practice seem to be a bit on the simple side.

    For example, in classic Unity, I can almost instantly visualize how I'd make a Pacman game. I have all sorts of tricks up my sleeve for managing the game's state so that enemies and Pacman don't spawn/start moving until the intro song has played, making sure Pacman's and the ghosts' movements respect the collision values in the level's grid, making sure that that enemies correctly pathfind throughout the same grid, making sure that the player can't move Pacman around while he's in the middle of his death animation, and making sure that the whole game cleans itself up and does it all over again for the next level. At this point, I'm not sure how I'd achieve this sort of structured gameplay in an ECS-friendly manner. I could probably brute-force it, but there's no way it'd be pretty.

    On the other hand, ask me to make thirty-million birds fly around in the sky in a circle in ECS, and I've got it covered.

    So I was hoping maybe some of the more advanced ECS users could drop by and share some of their favorite ECS patterns for dealing with common game behaviors. I'm a huge Overwatch player and I believe that's a game that uses ECS, so personally I'd love to hear examples contextualized on what components they might be making and which systems they'd use to act upon them to create some of the behaviors that can be seen in that game.

    Despite being as far from an "advanced ECS user" as one could get, I'll share the one, single pattern that I use which might or might not even be good!

    I like to represent "actions", "verbs", or essentially any situation wherein something is happening "to" an Entity as a Component added to a separate Entity referencing the target Entity, rather than adding that component directly to the Entity. For example: if my input system detects the input for "Jump", rather than adding a "new DesiresJump()" component to the Player's Entity and having my DesiresJumpSystem process and consume that component, I'd create a new Entity, add a "new DesiresJump() { TargetEntity = playerEntity }" to that Entity, and the DesiresJumpSystem would know to process that component against its TargetEntity, consume the Entity, and perhaps add a "new JumpingComponent()" to the playerEntity.

    The advantage I've seen out of doing it this way is "a thing" can happen to an Entity multiple times within the same frame, and not only will I not get a Unity error from adding multiple components of the same type to my player (since I can't always check for this from within a Job), but the DesiresJumpSystem can prevent the same action from being processed on the same Entity more than it needs to be. The only disadvantage I've seen to this is that I have to create a NativeArray each frame to keep track of which Entities have already been acted upon within the current frame.

    I'd like to hear what you guys think of my example, but much more so I'd love to hear your own examples!
     
    futurlab_peterh likes this.
  2. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,907
    Think about it this way:

    - you have entities like you player (in reality it's not an object, it's a group of components, similar to the gameobjects in the editor from this point of view)
    - you have components which have two distinct reasons to exist:
    - store some data about the component meaning (jump component: how high)
    - and/or tell the systems that this entity desires to jump
    - systems will make this action happen and can change the entities' (the group of components) state (in other words can add or remove components)

    So if you create a system which reacts the presence of the component "jump" what you will do?
    - read the "how high" data from the component
    - execute the action
    - when it ends you remove the component

    Sometimes (most of the time) you need to go deeper. Which means you need to break up your action to fragments in order to be able to work with it across many frames.
    So, you will have "BeginJump", "Jumping" and "EndJump" components (forget that the example is not realistic, you get the idea, it's a general example).
    So when the player hit the space bar, you know (s)he desire to jump. You detect it with the input system.
    You identify the player entity, put a BeginJump component on it.
    The DesireJump system can read it, so it can calculate forces and whatnot. Also removes the BeginJump component and add a Jumping component.
    The Jumping system detects the Jumping component, so it know that your entity began to jump. When it runs, adjust the forces/height/position/animation/whatever and calculate if it needs to stay in the next frame or the jump can be finished, if should stay, do not change the component. If the jump is about to finish, it removes the Jumping component and adds an EndJump component.
    The EndJump system (if it even needed), tie up the jump, calculate anything should remain after the action and removes the EndJump component, so the entity finishes the jump action.

    Again, this is a somewhat dumb example, but whatever. You can break down the Action into reusable pieces. If you then add a new entity (some enemies for example), which can jump, you just add the BeginJump to it along with the "how high" data and you're good to go.

    All of these mean that you will not design you're application's command flow, you will design your application's data-flow. You will need to design how and what data will be present in any systems you add.

    It's good to get a whiteboard and draw it up, something like the flow-charts were back in the good ol' days, when they OOD wasn't a popular, hyped thing.

    And one last thing: you cannot add multiple components of the same type to one entity. So you can only once BeginJump or Jumping.
     
    futurlab_peterh likes this.
  3. slim_trunks

    slim_trunks

    Joined:
    Dec 31, 2017
    Posts:
    41
    I feel like adding and removing components is the single most useful pattern right now.
    As LurkingNinjaDev said: dataflow can be used to represent any structured gameplay progression.

    Game programming patterns is really a solid book no doubt and some of the patterns are very helpful.. in an OOP land at least.
    But some of them feel like dancing around the convoluted OOP way of thinking and will therefore probably just hinder you in ECS land.

    I find it is generally easiest to pick the simplest solution you can think of and model that with dataflow (adding and removing components).
    That would be the closest to what I would call a pattern.

    Edit: If you want multiple components on one Entity you could also look into DynamicBuffer.
     
    futurlab_peterh likes this.
  4. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    Any game can be described as a game-world state and changes to that state that happen over time

    "state"(pure data) and "change"(pure logic) are 2 fundamental concepts
    DOD app always clearly splits this two

    Patterns in DOD are about organizing data structure, and ways it can be changed.