The strongforce library is a light-weight framework to build game engines. It provides a backbone for a powerful game loop and helper classes for efficiently implement new game components such as models, renders and simulators. Main framework features include:
- Model oriented.
- Decoupled simulation and render loops.
- Real time simulation loop for integration with physics engines.
- Separated read and write stages during simulation to avoid expensive model copies.
- Versatile and dynamic visitor pattern to implement the Game Model Hierarchy.
- Helper classes to ease implementation of Renders and Simulators.
- Events for Model to allow asynchronous programming.
- Fully control of the loop step and execution.
- Careful separation of responsibilities allowing the developer to focus on data model, business logic and rendering process separately.
Strongforce is not a graphic engine, not even a game engine. It is only a proposal for a game architecture and a main loop. You can read more about the framework internals and philosophy of design in the wiki pages of the repository or further in this post. Please feel free to provide any feedback by opening new issues on GitHub.
Hadron was the name of a full HTML5 isometric game engine I started around a year ago. As the project was too big, I decided to split it into several smaller projects. What I realized was the main loop of Hadron and the basic infrastructure suited very well for each of these little cases so I preferred to isolate this specific functionality.
So the framework keeping Hadron engine’s and all the side projects’ infrastructure was called after the mentioned phenomenon.
As the lead developer, when I was designing the engine, my main focus was to provide an agile development cycle where more than one team could work simultaneously on the different aspects of the game. Specifically, one team could focus on designing the game abstractions working side by side with game designers while others would implement business logic and rendering systems in parallel. If suddenly we choose to change the graphic engine or game rules, these changes should only affect specific and contained parts of the source code.
The data model
From strongforce perspective, a game is no more than a state being simulated and rendered. The state holds the properties of the fictional world where the game unfolds, the render reads the model and realizes it in a physical medium and the simulator alters the state through time applying the business logic, i.e. the game rules. You can see an overview of the split of responsibilities in the presentation I gave to the development team. Notice there were no separation between Hadron and strongforce entities at that moment.
There is only one model per loop but this model can be arbitrary complex. Strongforce assumes a graph-like structure and the loop traverses the nodes in pre-order. For such end, the base class Model implements a traverse() visitor method which receives a method name and pass through the model calling that method on the parent node and its descendants. The specified method is called twice, once before visiting children nodes and once again after all children have been visited. First and second calls are designated as pre-call and post-call respectively and are distinguished by a flag argument passed to the function call.
This allows us to think about renders or simulators as complete and separated objects / classes with its own state. The model is split into three responsibilities following the original design: a state, a simulator and a render. In terms of responsibility and inside strongforce, each is called a facet. The game loop reveals one facet or another by executing the proper functor in its respective stage.
The execution model
Although this design is suitable for any game loop, after much reading I choose the one proposed by the article Fix your TimeStep! at gafferongames.com. It allows to decouple the render and simulation loops allowing as many FPS as possible while keeping a constant rate for simulation. Nevertheless, current implementation of strongforce uses requestAnimationFrame() to schedule the next game step so FPS are clamped to 60.
For me, most important thing about this design is that it provides two independent dimensions of the single responsibility principle. In one hand, there is a vertical split aimed to provide a top-down definition at the level of which actors are involved in the game model (for instance a world with objects, the hero party with the hero characters and enemy patrols with the enemies themselves) while in the other hand, there is a horizontal split to provide the specific behaviors for such actors while simulating or rendering (how the enemies act or are rendered? how the hero react to user input or is rendered? …).
The split allows the team to simultaneously evolve the number of entities of the game while refining a specific render and simulation aspects in a predictable and regular, OO-friendly and dynamic way.
A note about performance
In order to keep the illusion of fluid animations, the complete game loop can take no more than 16.6 ms. Profiling current overhead introduced by the loop and model traverse reveals that no more than the 4% of time is spend in the game loop.
Of course, traversing the model is directly dependent on the model complexity (nesting level) itself.
Hope I will be able to improve performance and provide even less overhead. Sure there is a lot of room for improvement!
And that’s all. Along this post I talked about the motivation, internals and design choices of one of the more ambitious projects I’m developing right now. Hope you enjoyed and learn. Remember you can use comments to provide as much feedback and opinion as you want.