Great questions you raised! Actually some of the most interesting challenges we faced during the development of the core movement mechanic - so excuse me if I slightly nerd out a bit here on my answer.
In terms of the data type and integer accuracy issue, this was a decision made pretty early on.
Dealing with decimals, floats, doubles, and the like all have unique accuracies across the different programming languages, and sometimes even within one depending on how you manage your data.
To combat this, we decided to stick with a purely integer based system, that uses specific and consistent rounding logic when performing calculations.
Most of these can be found in the VectorCalculatorService.cs file, in the Services folder of the game-engine.
Much like you mentioned, because they are all integer radii, this leaves us with three possible colliding states:
- Not colliding - the distance between the center points of two objects is greater than the sum of their radii
- Touching - the distance between the center points of two objects is exactly equal to the sum of their radii
- True overlap - the distance between the center points of two objects is less than the sum of their radii.
The engine only considers a collision when two objects have a true overlap - at least 1 (one) integer value worth of distance less than the sum of their radii.
In terms of the “bullet through paper”, this was a fascinating problem and is pretty well described in game engine development.
Typically referred to as the tunneling problem, there are some very interesting solutions to this.
The simplest approach, and the one we chose to go with, is to simulate the movement of all objects concurrently through the course of their total movement in that tick.
This means that, at any given moment during the simulation, each object has moved proportionally to their overall movement required, allowing us to detect collisions at each point across the entire movement, rather than just considering the change in position as a whole.
You can think of it much like a ray cast, through the entire movement, and collecting all collisions along the path.
Therefor, if at any point during their movement, those two objects overlapped by at least 1 integer worth of distance, they will have had a collision detected and the consequences thereof applied to them.
I hope this helped answer your question, my apologies if it was a bit overwhelming but this topic gets me really excited!