Most people involved in games development have likely heard of Havok Physics: a middleware solution which provides collision detection. HEXTERMINATE used Havok for a long time, as it was free for Windows even in commercial projects, as long as they didn’t sell over a certain threshold. However, the hobbyist version has stopped being distributed years ago and this caused some issues: more modern versions of Visual Studio weren’t supported (anything past VS2012) and most importantly, could the game still be shipped with it?
An open-source approach
The alternative chosen was to use the open-source library Bullet:
- Conceptually similar: most of what HEXTERMINATE uses could be done in Bullet without massive refactoring.
- Functionally equivalent: there were no holes in the feature list, everything I needed was present.
- Permissive licence: the ZLIB licence puts essentially no restrictions on the game.
- Source code available: I could republish the library binaries myself, getting me up and running in VS2017 quickly.
- Linux support: although not something I have spent time on, Havok was the last library holding off multi-platform support.
Making it all work
Getting the basics up and running was fairly straightforward. What ended up taking up most of the time was that many places in the game’s code were using Havok’s maths library rather than GLM, which required some head scratching and debugging to make sure that it was all working. Most of it was fine, but swapped order of operations caught me off-guard a couple of times.
In the end it was actually the smaller things that took the longest: getting callbacks with the required information for when two ships collide was non-trivial, as I needed information about which two modules were colliding, not just the parent “ships”. The other problem which took a while to figure out was that raycasts which return multiple results do not return results in an ordered fashion, so I was seeing odd behaviour with some weapons, e.g. a beam weapon being blocked by a shield but still damaging the module behind it.
In HEXTERMINATE, each ship is composed of a number of individual modules. Each module can be destroyed separately, so each ship’s physical hull is composed of a number of physics shapes. Historically these shapes have been spheres, as it is algorithmically much faster to calculate the collision between pairs of spheres than pairs of hexagons.
However, a problem that comes with the sphere approach is that it is possible for a projectile to go through two modules and hit the module behind them. It doesn’t happen very often, but because the outer modules are normally some sort of armour, when it does happen the player definitely notices.
The alternative was to use cylinders rather than spheres: still faster than hexagons and it prevents the frustrating projectile punch-through.
Shield modules generate the classic science-fiction bubble around a ship. Until the replacement of Havok with Bullet, the shield had to be disabled before the underlying ship took any damage. This had a few issues, such as making the choice of weapon less relevant, but more crucially this meant that the optimal way of building your ship was to put as many shield modules on it as you could.
A way to mitigate this was to make certain types of weapons to be able to bypass shields: missiles and torpedoes can now hit the underlying modules directly, making up for the fact that unlike other weapon types they can be intercepted. This worked very well and made some encounters more interesting after a few tweaks to the designs of certain ships.
No more ghost collisions
A long-standing bug had to do with the ships or projectiles colliding with something invisible. This was quite rare, but it was obvious that some of the physics collision shapes weren’t being properly deleted. Tiny memory leak aside, I was concerned this might lead to a memory stomp and crashes, as the parent ship no longer existed.
To help me track this down I’ve made use of Omar’s excellent imgui library to get some of required debug information on screen. In the end this was tracked down to the shields on ships: if the player left the sector, any ships which still had their shields up wouldn't remove the associated physics object correctly.
In addition to everything that was done to get the physics up and running, I've also received the assets for a some of the characters which the player will run into throughout the campaign:
The game is now very much near completion. There are now two chunks of work remaining:
- Finish the campaign: the campaign leads the player through the galaxy, providing a direction to head towards without overly constraining the player’s liberty. It is about 80% completed, so the push is to finish the remaining couple of missions.
- Steam achievements: achievement support is already in place but not all achievements have been hooked yet, as some depend on the campaign.
Thanks for reading!