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!