Making a physics engine from scratch
In project 7 (Disarray) each member of the group was encouraged to work on something they found interesting and challenging, something you could show of in your portfolio. Because of this I chose to make the physics for this game from scratch.
In the end I managed to get rigid body motion with spheres and OBBs, utilizing momentum, angular momentum, bounciness and friction.
Its accessable for designers through both level editior and script.
In our level editor (Unity) you add a rigid body component to your game object where you can set mass and positional / rotational constraints in the xyz axes.
In the script editor there are nodes to add force, add impulse and get/set velocity.
This was made over the duration of about a month half time. Features like an OBB rigid body turned out to be overkill and has not yet been used in one of our games but in other areas the physics is lacking.
The biggest shortcoming of the physics is that it is prone to "overshoot". With low frame rates, a player falling to the ground might keep falling through it. This could have been solved with swept collision detection but I did not have the time to implement it and we where able to work around the problem.
A puzzle prototype made by one of our level designers, Jacob Tjernström. As it turns out, more predictable scripted motion on the box would be more suitable here but it serves as a great demonstration of both the physics and how accessible it is to designers as this level was made very quickly without any programmer help.
Disarray was the game the physics was first developed for.
Exposing functionality to designers is something I find super valuable. It shortens iteration times massively, frees up programmer hands and seeing you tech used in someones creative work is incredibly rewarding. Our game engine is completely datadriven utilizing visual scripting. There are the nodes that involves physics.
How Its Made
Rigid body motion - The theory
I did not follow a guide when making this. Therefore this is my own take on how to make physics.
There are a few distinct steps to it.
Anatomy of a collision:
Finding the intersection point and normal.
Calculating the relative velocity at the intersection point.
If the two bodies are moving towards each other at the intersection point relative to the intersection normal then calculate an impulse.
Handle the impulse both momentum and angular momentum wise.
Separate the bodies from each other so that they no longer overlap.
Besides handling a collision, the position and orientation will be updated based on momentum, angular momentum, mass and angular inertia.
Rigid body motion - In practice
Step one: finding the intersection point and normal.
The way I find the intersection data is to have an intersection-function for each shape combination. sphere on sphere, box on box, box on sphere etc. This starts out easily enough but quickly spirals out of hand when adding more collider shapes, increasing exponentially. Because of this I chose to stick with spheres and OBBs.
Beginning with the sphere on sphere I thought that physics seemed easy. The function was tiny and the math was really manageable. Sphere on OBB was a bit harder but still easy enough. Then I got to OBB on OBB. Holy crap. I where able to wing the previous ones but this one was next level. I got a tip that separating axis theorem was used to know if boxes overlapped, which could only get me about halfway there since I need the intersection point and normal as well. After reading up about it I at least had somewhere to start. This was the toughest part of writing the physics and after about half a week of work I had managed to produce about 200 lines of some of the most obscure code I have ever written.
Step two: Calculating the relative velocity at intersection point.
To get the velocity of any point on a collider you add the velocity of the collider with the local velocity of the point. You can get the local velocity of a point with the cross product between the angular velocity and the center to point vector.
Step three: If the two bodies are moving towards each other at the intersection point relative to the intersection normal, then calculate an impulse.
When you have the velocities of both colliders at the intersection point you can determine if they are moving towards each other with the dot product of the intersection normal and delta velocity at the intersection point.
To determine the impulse I came up with a formula using the lesser mass of the two colliders, the portion of mass and delta velocity along collision normal. This results in a prefect bounce but since I don't want rigid bodies to bounce indefinitely the bounce impulse is multiplied by a bounciness value. At the moment it is hard coded to not bounce at all since our game so far hasn't required it to be exposed to designers.
Step four: Handle the impulse both momentum and angular momentum wise.
The solution I came up with here is strictly speaking wrong but looks good enough that investing in a correct solution would not be worth the time.
Here you can also see how I ensure that the positional and rotational constraints are followed.
Before I even began working on the physics I researched angular velocity and moments of inertia. It's a part of the physics I would have loved to go in depth but the time constraints didn't really allow it. If I had the time I would have made a system that use multiple moments of inertia on a single rigid body. Implemented correctly, things like gyroscopic effects and chaotic rotation when rotating along an axis between the minimum and maximum moments of inertia could emerge.
Step five: Separate the bodies from each other so that they no longer overlap.
Bouncing, aka changing rigid bodies velocities, is not enough when going about this the way I do. If two colliders are intersecting without any velocity then they'll just stay there. Or if a character is pulled down my gravity while standing on top of a platform, it will slowly sinking through the floor.
Therefore, as a last step, I adjust the rigid bodies positions away from each other so that they are no longer intersecting.
That's more or less it! Much like network programming, there isn't a hole lot of code but it can still be a lot to keep track of.