The Physics System: Our approach and challenges
Introduction
Physics often play an integral part of a game. Whether they provide a realistic simulation, something more surreal such as the Portal games, or just basic platforming mechanics, a game will suffer immensely if the physics don’t behave like they should. Physics can make or break a game. Naturally, physics will be a vital part for our engine as well, but there is more to it than just implementing an existing library and creating a simple interface to use it with. In this blog post we will elaborate on our goals and explain the choices we made in order to create an easy to use yet flexible physics system.
Multiple physics engines
Providing a performant and accurate physics simulation for the end user is important, of course, but not the greatest challenge for this project. There are currently various high-quality physics libraries available, such as PhysX, Havok, and Bullet. Many other engines and developers have made use of these libraries for good reasons: They provide many features, they are fast, and they are reliable. These physics engine have an incredible amount of research and development behind them, so using them will save a lot of time to get a correct physics simulation running and allows us to put our focus towards the main goal: Creating the engine.
We aim to support these three major physics libraries. In order to do that, an interface will be created that allows us to easily swap between different libraries. One of the goals for this is to get a common denominator for a physics interface and ensure the front-end of the interface will not change based on the library in use, so the implementation-specific details are all kept on the back-end.
This also grants easy benchmarking between the different engines, by creating a test scene that uses multiple instances of all types of physics areas that will be used in the engine. Such as: constraints between entities, ragdolls, parenting and ropes, simple collisions like one on one and concave; complex collisions like many tipping over towers and concave and material interactions. This way we can measure performance and test for odd behaviour.
But not only measuring different well-established physics engines against each other. It is also a great learning opportunity. The stretch goal of ours is to write a custom physics implementation. By implementing all these physics engines, enough knowledge and familiarity should be gained to come up with a decent custom interface, and we should be able to test our own physics against other existing engines. This could be a good learning opportunity without risking the state of our engine’s physics system, as we can ensure a functional physics implementation is in place.
The interface
In the previous section we already talked a bit about the interface between the physics implementation and the engine itself, but the layer between the engine and the end user is just as important. After all, it will be difficult to efficiently use the physics system in the engine without a proper interface.
Looking purely at the implementation, we want to provide modules that are to be used to create components for the entities in the world. After all, complex-looking objects can be made by using these ‘simple’ building blocks. Objects such as cloth or ragdolls are ultimately built up using small rigid bodies that are bound together through constraints, for instance. Additionally, this approach coincides well with the flexibility of the Entity Component System. Standard physics components are defined without having to hard-code objects, and any new components can be added later with relative ease, should the need arise.
The interface will also provide other common functionalities such as raycasts, collision filtering and callbacks. These subjects will be tackled later, possibly in a future blog post.
The engine and the physics engine
The component system that was discussed in the previous blog post has to work together with the physics system very closely. The architecture of the engine itself states that everything should be an aggregation, not a composition. The physics interface should be coupled neatly using interfaces and not be hardcoded into the engine. However, the physics root deeply into the game world. Every entity that has a collider and/or rigidbody component has to be updated by the physics step. That means that everything gets copied to the physics engine via the interface, the physics engine does its simulation and everything gets sent back to the world. When the two layers communicate with each other, a lot of data is copied from one system to the other. This has to happen as efficiently as possible. This is where the data oriented part of the engine design comes in. By having a so to say “Struct of arrays” instead of a “Array of structs” design, the CPU can predict what to copy into its cache, keeping the cache miss or noise to a minimum. This way we can minimize the overhead caused by the synchronization between the physics and the world, making the simulation as fast as it can be.
Left: An array of structs
Right: A struct of arrays where the entity is only an index
However, because all these physics engines are different, finding a common denominator is going to be essential, since it is a common practice one creates a new product when others do not suffice. Any limits or differences in feature support need to be taken into account. However, a physics engine simulates physics - to a certain extent. And luckily there is only one instance of physics in our known universe. We all apply one model at least. In other words, there will be a common denominator for the simple things, the simple things that will be implemented into the engine. And as we strive to keep the engine lightweight, without the more advanced features like fluids and soft bodies, we believe we can settle on a feature list that is well-supported by the physics libraries we use.