The physics of DREAD IT is built on the interaction between objects with Physics and Collider components. The Physics component is responsible for all things regarding kinematics and dynamics, using semi-implicit Euler integration. The Collider component stores all needed information for detecting and resolving overlap, such as restitution and information on the shape of the collider and how it is currently transformed
In order to reduce code duplication across different types of colliders, I decided to go with a method in which I implemented a library of Shapes that all inherit from a base class. Each shape has its own implementation for serialization, display, and editor integration, all called through the base class by the Collider. I would have been able to reduce some casting calls by templatizing the Collider class, but I took this approach to keep the entity serialization process simpler.
For the Polygons, I implemented the separating axis theorem in order to detect overlap with circles and other polygons. This meant that anything polygonal had to be convex, but it made it much more performant than alternative methods.
The separating axis theorem also gave me the benefit of being able to accurately detect whether entities were standing in puddles, as it's a method that detects intersection rather than crossing.
Instead of using a collection of AABB or polygon colliders, I instead used a special type of single-direction polyline collider that calculates collisions in a continuous rather than discrete manner. There are a few benefits to this approach over other options for tilemap colliders, namely:
Less Memory: each wall in the environment has exactly one line segment, rather than each tile or each region holding an AABB
Prevents Clipping: since the collision detection is continuous using this method, it becomes mathematically impossible for an object to be pushed into a wall, regardless of frame rate or object speed
Implicit Corner Array: Using this method, the only verticies that exist are ones that are actually on the corners of walls. We were able to use this data to build the shadow casting that the flashlight uses in game
Learn more about the shadow casting: Gabe Gramblicka - Dread It
To save development time, I implemented an algorithm in the Tiled parser that marches across the wall layer of the map and automatically generates the wall collider. It also supports being dynamically updated at runtime, though we did not end up using this feature in game.
In order to save on performance in large levels with lots of furniture, I implemented a broad phase that sorts all the colliders into smaller regions, since collision detection has a time complexity of O(n^2) on each pass.
Within the narrow phase, I also included a significant amount of early outs to reduce redundancy. For instance, wall detection only happens when an object is moving, since the walls never move.
One of the main goals of this project was to make the editor interface robust enough to support quick development and level creation. Building the editor itself was not my responsibility, though I did create widgets for the components I made.
The colliders are easily able to swap between collider shapes, and the shapes then have their own implementation of their interface from there. All other properties of the collider and physics classes are editable as well through the GUI.
The physics engine works very well for what this game needs, though if I got an opportunity to remake it, I would be able to use the knowledge I gained this time around to make it more robust.
I put some effort into adding rotational dynamics into our engine, but in the interest of time and implementing features more impactful on gameplay first, I decided to shelve it. If I get the opportunity to make another physics engine, I would be able to build it on top of a better foundation as well, which would make it easier.
Another feature I would change about the physics engine would be the creation of material assets to maintain consistency between game objects. Currently, the mass, restitution, and friction are all exposed, but to somebody who isn't familiar with physics these numbers don't mean much, and they would likely set them to wildly inconsistent values. Next time, I would instead define some materials such as wood, metal, glass, and whatever else the game needed and then just have the collider components be able to select from one of the presets.