GMGE Physics Part 1 of 3 - Forces and movement
Introduction
Let's face it, you can't really have any sort of action games without some amount of physics. At the very least, we need to be able to detect and respond to objects colliding. The GMGE Physics Engine is not intended to build physics-style games. If you want to make a game with a lot of complex physics (described below), GMGE is definitely not the engine to use.
Did the world need a new game-physics engine? Maybe. For starters, GMGE needed a physics engine. While there are plenty to choose from, they are mostly general purpose, which means they are fairly complex. Additionally, they aren't necessarily optimized for the types of games I want to build. I wanted something that runs fast in mobile browsers and is written in Javascript. Also, I thought it would be kind of fun to try and build from a learning perspective.
In Part 1, this first article, we cover the basics of movement. We define a physics-body (typically known as a rigid-body), and apply forces to it so it moves about a game-world.
In Part 2, we'll define a collision-layer interaction matrix, and cover collision detection.
Part 3 brings it all together as we cover collision resolution.
Physics Body
This is a description of the gmgePhysicsBody object
There are 3 types of physics bodies in GMGE.
- Static - These objects don't move in the game world. If they have a physics collider defined, then things cannot move through them either. Typical static objects would be terrain-like things such as platforms, walls, trees, etc.
- Kinematic - Game logic controls movement, like the player in the game being controlled by user input, or enemies being controlled via AI. However, they still need to respond to collisions. For example, characters shouldn't be able to move through walls (typically)
- Dynamic - The physics engine controls the movement by applying forces like gravity. Typically something like a thrown ball, where you want it to look realistic by both being affected by forces and colliding with objects like the ground.
In order for various physics calculations to work, physics bodies also need values for the following:
- mass - used for physics calculations when applying forces and conserving momentum during collisions
- drag - A value from 0 to 1 that represents the friction to be applied as the object moves, eventually slowing it to a stop
- gravity scale - a bit of a hack to make things respond to gravity differently, like if you want a feather to fall slower, give this a fractional value. This is because the engine does not realistically simulate forces like air resistance
- elasticity - bounciness of the object used during collisions. A value of 1 is a perfectly elastic collision. A value of 0 would result in the objects sticking together when colliding with no bounce whatsoever. Values > 1 are not advised as they will produce unpredictable results. A good default value is 0.8
- freezeX - a boolean that prevents horizontal movement as a result of physics calculations
- freezeY - a boolean that prevents vertical movement as a result of physics calculations
- physicsCollider - a reference to a box(rectangle) or circle collider. Will react to physics for all physics body types
- triggerCollider - a reference to a box or circle collider, that will call user defined code when they collide, but does not affect any physics
- velocity - the speed and direction of the object. This shouldn't be set directly. Instead you should call the AddForce method to apply external forces. However, it is perfectly fine to just set the velocity directly, like for player objects tied to input controls. They would change direction instantly this way, where more realistic behavior is done by applying force in the direction of the input, slowing the object and eventually reversing direction
- acceleration - Calling SetAcceleration() establishes a constantly applied force on the object each frame
There are also some "hidden" values that should not be set directly by developers. They are controlled by by the physics engine. They are all prefixed with an underscore. (In general, in GMGE, anything with an underscore prefex should not be used directly by developers. Setting them will likely break things, and reading them is not reliable because you don't know at what point during a frame they will be updated.)
- _inverseMass - most of the physics math uses the inverse mass of an object (1/mass). Rather than calculating this for every collision, it is set once when the object is instantiated
- _linearDamping - this equals 1 - drag, so it can be used as a multiplier to the velocity each frame
- _forceAccumulator - a 2D vector that accrues the forces to be applied to an object
- _axisFreedom - basically a boolean 2D vector that can be used as a multiplier to negate movement on any frozen axes. A value of 1 means movement is allowed, and a value of 0 will effective freeze the corresponding axis
Not counting the velocity or acceleration, things in a physics body are set once when the object is created. Like it typically doesn't make sense for an object to lose mass during the game. However, if you wanted to simulate an ice cube melting for example, and you modified its mass over time, you would also need to directly set _inverseMass, otherwise the phyics calculations would still be based on the original mass.
IterateForDuration
This is the main starting point for physics calculations each frame. It is passed one parameter deltaTime that represents how much time has passed since the previous call of this function. In GMGE deltaTime is a global variable, so why is it passed to IterateForDuration?
GMGE runs in a browser on a device with other things running in the background. That results in variable frame rates. Variable frame rates means non-deterministic physics results, which can be maddening while trying to debug something. By passing in a value, you can force a fixed phyics frame rate to help troubleshoot things. Of course doing this means that physics time drifts away from actual time which will result in jumpy motion. For an extreme example, if we set every physics update to be 0.1s, but the real elapsed time is 0.5s, the objects appear to move slower than they should have. Therefore, in general, for most games, passing in the global deltaTime provides the best results.
Because GMGE assumes a variable frame rate, and you could change gravity during gameplay based on your design, each frame calculates a couple gravity related variables.
- gravityNormal - Normalizes the gravity vector. If gravity points to the bottom of the screen, then normalized it would be [0, -1]
- restingVelocity - gravity scaled by deltaTime. It is the amount of gravity applied for this frame. If that is the only velocity on an object in collision, then we assume it is resting. More details in Part 3
- motionThreshold - this is the length of the restingVelocity squared. It is much faster to calculate the squared length of a vector by avoiding the costly square root calculation when we don't need it
The main loop is the work horse and does its thing by looping through all active game objects. Every GMGE object has an isActive member variable. If that is set to false, then it is skipped when considering physics. It is also possible to define game objects that do not have a physicsBody defined. Those are skipped as well.
For motion calculations, the physics engine only acts on active objects, with a physicsBody where its type is dynamic. Here is the main body of the physics loop (please note that obj is a reference to the current object, and pb is a reference to its physicsBody):
The basic gist of how it works is pretty straightforward. For every dynamic object in the scene, determine the final acceleration to be applied to it by adding its current acceleration, gravity, all external forces added during the frame, and its current velocity. Then apply any friction. (Something to note about friction. It is really simplified in GMGE. An object falling through the air has the same amount of friction as two objects sliding across each other. That is why you can scale the gravity amount for an object.)
Next it clears the external forces accumulator, so it is ready for the next frame. After that, any movement along a frozen axis, if one exists, is zeroed out. Once motion falls below a threshold, the object is stopped. This helps prevent floating point error accumulation over time, and then the object moves like 1 pixel after 5-10 seconds or something. As silly as that may sound, it really did happen, and looks quite odd when it does.
Finally, with a newly determined velocity, the object's position is updated within the game world.
Summary
Game objects that need to trigger and respond to collisions require a physicsBody. The physicsBody contains values that allow physics calculations to take place, including movement for dynamic objects. Motion for dynamic objects is controlled by the physics engine. It basically adds up all the forces: acceleration, gravity, and externally applied ones with its current velocity, scaled by deltaTime (the amount of time since the previous physics iteration) to determine the new velocity. Motion on frozen axes is zeroed out, and if the object is moving slow enough, it is stopped completely.
Its velocity is then used to update the position of the object, and the loop moves on to the next object. Once all of the dynamic objects have been moved, things go to the next phase.
What Next?
After all of the dynamic objects have moved, it is time to check for any and all collisions, and ultimately respond to them. We cover collision detection in Part 2, and collision response in Part 3.
Complex Physics - Stacks
Something that seems simple, a tall stack of boxes resting on each other, is surprisingly complicated for a physics engine. Especially if you drop another box on the top of the stack. We all know in our heads that a stack of boxes should just sit there. When you drop an additional box on top, it should land and the whole stack should just sit there still, with no motion.
What is so hard about that? For starters, each box is colliding with at least one more, and gravity is acting on all of them simultaneously. Each box is trying to move down into the one below it, so it must be pushed back out of collision every frame. Doing so likely pushes it back into collision with the one above it, and so on. You don't want to see the stack vibrating with slight verticle movements as all of those collisions are taking place and getting resolved.
When you drop another box on top, a strong force pushes what was the top box down into the one below it, and it cascades down through the entire stack. But the order of collision resolution isn't magically sorted. What this means is that a box in the middle of the stack may get resolved with virtually no motion, only to eventually receive the strong downward impulse as the shockwave of the newly dropped box works its way through the entire stack.
What is sad is that a stack of boxes is pretty boring to look at. Seeing any of the boxes vibrating is pretty annoying, and makes the game look pretty stupid.
Complex Physics - Tunnelling (Fast Moving Objects)
Any object that is moving fast enough, which in reality means something close to half its size or more in one frame, has the possibilty of completely passing through another object without ever colliding with it. In a game, this could be a bullet or something that is small moving at a high velocity. The solution for this is to treat it more like a laser.
What I mean is that fast moving objects need to cast a ray in front of them, in the direction of their velocity, and see what objects that ray intersects. However, it isn't quite that easy. The other objects can be moving too, including at high velocity.
It comes down to seeing if objects paths would cross in the current frame, which involves distance calculations that are fairly expensive to calculate, so you wouldn't want to test against the paths of all objects against every other object in every frame. So then you get into optimizations, like a broad-phase detection, then move on to a more fine-grained detection. It is all doable, and if I build a game requiring high-speed objects I will have to do something to support them.
Complex Physics - Rotation
In the real world if two objects collide, unless they hit dead-center, with respect to their center of gravity, some amount of rotation would be introduced as a result. Rotation would also occur if their surface normals don't point directly at each other.
Determining the amount of rotation requires more math, and when we are talking about rectangles the surface normals require math to figure out what direction they are pointing in.
Complex Physics - Arbitrary Shapes
GMGE physics is limited to circles and axis-aligned rectangles. As soon as you go beyond that, the physics gets complicated quickly. The two objects below are not colliding, but good luck figuring that out. And if you do figure out how to calculate that, determining the surface normals to resolve the collision is no average task either. Doing it every frame in a fraction of a millisecond becomes a problem as you add more objects into the world.









