ECS Concepts – Part 8: ECS Integration
by Cory on July 15, 2018 12:06 PM
I was going to call this post “Systems” but then I realized I already made a post called “Systems”… Anyway, let me layout how my engine is organized because I don’t think I’ve done that from beginning to end in a single post. Let’s zoom out a little bit to the main entry point of my game engine.
The Game Loop Logic
I have a “top level” Game class that contains the main game loop logic. This loop configures the SFML classes and starts everything up. This loop has a delay statement from the SFML library in it so that it executes as close to 60 times a second as possible. Later on, I think I’ll need to replace this with my own delay scheme when I figure out more advanced frame interpolation stuff.
Inside the loop, the game logic does the following:
- Manage scene flow. This means checking to see if the anything has signaled to unload the current scene and/or swap it with another new scene. Also, if there are no scenes left to execute, the game loop exits.
- Do input polling. I tell the InputManager to update here.
- Tell the currently active scene to update itself (and by implication, all its systems)
- Clear and redraw the game window
It is important that the window is cleared before updating everything and the redraw is done last. The clear needs to be done first because, during the update, anything that is graphical will draw itself to the framebuffer. You need to clean the framebuffer of its previous contents before drawing more stuff to it. When the redraw is done, SFML will swap the framebuffer currently used to draw to the screen with the new one that was just drawn on. This is called double buffering. I will probably beef this code up a bit to make the scene management stuff more robust because I will need some more features soon.
A More Consistent Engine Paradigm
Also, I really want to change the name of the Game class to Engine. So, some engines like libgdx use a theater paradigm where they call their Scene equivalent a “Stage” and entities are “Actors” or something. I kind of half adopted that with “Scene”, as in a movie scene, but nothing else is consistent with it. I think my next revision will just go with a world simulation paradigm. So Game will become Engine, and Scene will become World. The rest of the stuff, like entities, components, and systems stay the same.
I think this naming will be more descriptive. When I called it “Scene”, this name suggests something specific to the user that a scene should be a linear picture or short series of pictures like what you would think of when you say “movie scene”. However, in the engine, what I am calling “Scene” has different connotations. This class contains a copy of the ECS, meaning that a “Scene” contains entities and events and there’s no requirement to script the class into an implied chronological narrative. For instance, you could create a tile map and a character that you control as a 2d platformer. This is quite literally a space where things interact with each other. Another example of a Scene/World could be the main menu of an RPG. Here there’s no physical space being simulated but a bunch of menus that the user can scroll through. The point here is that each class instance encapsulates some sort of simulated space. So that’s why I think “World” is more accurate.
The user should define their own scenes by inheriting from the Scene base class. This class has virtual function hooks for executing things when the Game loads, unloads, and switches between Scenes. This class also has utilities for creating and removing Entities (I made the constructor for this class private.) and registering new systems. The scene class is also messageable, so you can send and receive messages to it.
By default, the Scene class comes with a few systems that are automatically registered by default. One of them is the SpatialSystem, which is a system that constructs an implicit scene graph using Space components. I have my engine guarantee that Entities must have a Space component at all times. Another default System is the CallbackSystem. This system operates on all Entities with a Callback component, which is basically just a mapping of input events (mouse in, click, etc) to function pointers. The system listens on InputManager events and executes the corresponding callback function on each Entity if it exists.
All of the “guts” i.e. business logic of the game should be in the Scene. The outer Game class just coordinates between these things, but the Scene contains all the ECS related stuff so the game logic should be there too. The business logic is split between user defined system code, callback system components, and I’ll probably want to define like a director class or something that helps coordinate cutscenes and scripted events.
For an example of how everything works put together, you can look at the levels/BuilderScene folder in my git repo. I make a BuilderScene that is basically a tilemap editor. It recreates that stuff I was showing off before I started all this ECS bullshit but this time using an ECS. The application is simple so far, but I think it’s cleaner and much more extendable and accomplishes what I need it to do. The only problem is that it took like a year to make.
There’s a few more finishing touches I need to add to the ECS “package”. The next obvious thing is probably prefabs, which is basically just a group of pre-configured entities. Even in the BuilderScene code, there was a lot of entity groups that I made which could be neatly captured by the prefab sequence. For example, I could create a cursor prefab out of an entity with a graphics component that was a red dot and a callback component that had handlers to make the cursor’s position track the mouse position. Prefabs would make it easier to do a lot of things that would be cumbersome to do if I had to edit things on the entity level, such as transmitting things over the network or between scenes. It might also make it easier to serialize into a save file and stuff.
I’m done writing these fucking stupid long articles. I don’t know what got into me. It takes way too long to motivate myself to do it. I’m going to finish off this series talking about object pooling and describing some other ECS variants and implementations and then go back to doing shorter updates to show off progress. I want to start sharing some gifs or cool graphics and stuff. I didn’t mean to turn this into some huge text programming blog.
This Thought is part of Game Programming
Game programming general topic. I eventually hope to split this into separate ideas exclusively about specific games that I make.