Momentum and Impulse Forces
Add explosions and jumping to your game by mastering momentum-based impulse forces
In this lesson, we'll explore how to implement physics interactions by adding momentum and impulses to our simulations.
We'll learn we can use physics to create features requiring sudden changes to motion, such as having our characters jump or get knocked back by explosions. We'll also learn how to modify the strength of those forces based on how far away the source of the force is.
This lesson continues to use the components and application loop we introduced earlier in the section. We'll mostly be working in the GameObject
class.
The most relevant parts of this class to note for this lesson are its Tick()
and ApplyForce()
functions, as well as the Position
, Velocity
, Acceleration
, and Mass
variables. Those functions and variables currently look like this:
// GameObject.h
// ...
class GameObject {
public:
void Tick(float DeltaTime) {
ApplyForce(FrictionForce(DeltaTime));
ApplyForce(DragForce());
Velocity += Acceleration * DeltaTime;
Position += Velocity * DeltaTime;
Acceleration = {0, -9.8};
Clamp(Velocity);
// Don't fall through the floor
if (Position.y < 2) Position.y = 2;
}
void ApplyForce(const Vec2& Force) {
Acceleration += Force / Mass;
}
// ...
private:
Vec2 Position{0, 0};
Vec2 Velocity{5, 0};
Vec2 Acceleration{0, -9.8};
float Mass{70};
// ....
};
Momentum
Before we start adding to our class, there are two more concepts in physics we need to understand - momentum, and impulse. Momentum is a combination of an object's velocity with its mass:
For example, if two objects have the same velocity, the heavier object will have more momentum. If a lighter object has the same momentum as a heavier object, that means the lighter object is moving faster.
Given that momentum is a mass multiplied by a velocity, the unit used to represent momentum will be a unit of mass (often kilograms, ) multiplied by a unit of velocity (often meters per second, ). When units are multiplied together, it's common to represent that multiplication using a center dot
For example:
- A object moving at has a momentum of
- A object moving at has a momentum of
- A object moving at also has a momentum of
Impulse
A change in an object's momentum is called an impulse. In our simulations, changes in momentum are almost always caused by changes in velocity, rather than a change in the object's mass.
As we've seen, a change in velocity requires acceleration, and acceleration is caused by a force being applied to an object for some period of time. Increasing the force, or applying the force for a longer duration, results in larger changes of momentum. Therefore:
Newton-Seconds
The unit used to measure momentum (and changes in momentum) is often referred to by an alternative, equivalent unit: the Newton-second, . For example:
- Applying of force for creates an impulse of
- Applying of force for creates an impulse of
- Applying of force for also creates an impulse of
Previously, we saw how a Newton is the amount of force required to accelerate a object by .
If we apply this acceleration for one second, the object's velocity will change by . And, given the object has of mass, its momentum will change by . Therefore, . Here are some more examples:
- of impulse will change a object's velocity by
- of impulse will change a object's velocity by
- of impulse will change a object's velocity by
Implementing Impulses
The time component of an impulse can be any duration but, in our simulations, the main use case for impulses is to apply instantaneous changes in momentum. That is, a force that is applied a single time, within a single tick / frame.
Examples of this might include letting our character jump off the ground or having objects react to an explosition.
The key technical difference between an instant impulse and a continuous force is that impulses should not be modified by our frame's delta time. A continuous force gets applied across many steps of our simulation, so its effect on each frame should depend on how much time has passed since the previous frame.
But an instant impulse is applied all within a single step, and its magnitude should not depend on how long that frame took to generate. Therefore, an implementation of impulses might look like this:
// GameObject.h
// ...
class GameObject {
// ...
private:
void ApplyImpulse(const Vec2& Impulse) {
Velocity += Impulse / Mass;
}
// ...
};
Example: Jumping
Below, we use this mechanism to let our character jump when the player presses their spacebar:
// GameObject.h
// ...
class GameObject {
public:
// ...
void HandleEvent(const SDL_Event& E) {
if (E.type == SDL_KEYDOWN) {
if (E.key.keysym.sym == SDLK_SPACE) {
ApplyImpulse({0.0f, 15.0f});
}
}
}
// ...
};

Non-Instant Impulses
If we want to apply a force that continues for a fixed period of time, we can use our previous ApplyForce()
technique, in conjunction with some mechanism that keeps track of time.
For example, we could use the SDL_Timer
mechanisms we introduced previously, or accumulate time deltas that are flowing through our Tick()
function until our accumulation has reached some target.
The following program uses the latter technique to apply a force for approximately 5 seconds:
// GameObject.h
// ...
class GameObject {
public:
// ...
void Tick(float DeltaTime) {
ApplyForce(FrictionForce(DeltaTime));
ApplyForce(DragForce());
// Apply the timed force if active
if (ForceTimeRemaining > 0) {
ApplyForce({1, 2});
ForceTimeRemaining -= DeltaTime;
}
Velocity += Acceleration * DeltaTime;
Position += Velocity * DeltaTime;
Acceleration = {0.0f, -9.8};
Clamp(Velocity);
// Don't fall through the floor
if (Position.y < 2) {
Position.y = 2;
Velocity.y = 0;
}
}
// ...
private:
float ForceTimeRemaining{5.0f};
// ...
};
Positional Impulses
So far, our examples have implemented forces such that our objects feel their full effect. This is reasonable for effects like gravity and jumping but, in many cases, we want to simulate forces that have a specific point of origin.
For example, we might have an explosion happening somewhere in the world, with the effect that objects are knocked away from that location.
As such, the direction of the effect of that force on each object depends on that specific object's position relative to where the explosion happened. Let's set that up:
// GameObject.h
// ...
class GameObject {
// ...
private:
void ApplyPositionalImpulse(
const Vec2& Origin, float Magnitude
) {
Vec2 Direction{(Position - Origin)
.Normalize()};
ApplyImpulse(Direction * Magnitude);
}
// ...
};
Example: Explosions
Let's create an example where the player can click a position on the screen to create an explosion, knocking nearby objects away. The first challenge we have is that SDL will report the position of the click in screen-space, and we need the position to be in world-space for our physics simulation.
Our Scene
class already has a function to convert world space positions to screen space positions. Let's add an inverse function for converting screen space to world space:
// Scene.h
// ...
class Scene {
public:
// ...
Vec2 ToScreenSpace(const Vec2&) {/*...*/}
Vec2 ToWorldSpace(const Vec2& Pos) const {
auto [vx, vy, vw, vh]{Viewport};
float HorizontalScaling{WorldSpaceWidth / vw};
float VerticalScaling{WorldSpaceHeight / vh};
return {
(Pos.x - vx) * HorizontalScaling,
WorldSpaceHeight - (Pos.y - vy)
* VerticalScaling
};
}
// ...
};
Our GameObject
instances are currently receiving events within their HandleEvent()
method, so we can access click events and the corresponding mouse position from there.
However, within our GameObject.h
file, Scene
is an incomplete type, meaning we can't use our new ToWorldSpace()
function from this header file. So, similar to our Render()
function, our HandleEvent()
function definition needs to move to the source file:
// GameObject.h
// ...
class Scene; // Incomplete type
class GameObject {
public:
// Definition moved to GameObject.cpp
void HandleEvent(const SDL_Event& E);
};
In our source file, we'll create a Vec2
based on the mouse position, and we'll pass that vector to our new ToWorldSpace()
function to determine where that click happened in our world.
The force from an explosion is a quick, sudden, blast. As such, we'll implement it as an immediate change in momentum using our ApplyPositionalImpulse()
function. We'll pass our Vec2
to that function, alongside a magnitude that feels right We'll go with for now:
// GameObject.cpp
// ...
void GameObject::HandleEvent(const SDL_Event& E) {
if (E.type == SDL_MOUSEBUTTONDOWN) {
if (E.button.button == SDL_BUTTON_LEFT) {
ApplyPositionalImpulse(
Scene.ToWorldSpace({
static_cast<float>(E.button.x),
static_cast<float>(E.button.y)
}), 100);
}
}
// ... Other event handlers
}
Running our game, we should now see that any time we click in our window, our objects are knocked away from our mouse:

Falloff and the Inverse-Square Law
An additional property of a force originating from a specific position, such as an explosion, is that objects closer to the explosion are affected more than objects further away.
To make our calculations simpler, when specifying the magnitude of a positional force, we specify it in terms of how that force will feel to an object that is one meter away from it.
For example, we might want to create an explosion where objects one meter away experience of force. Let's represent that as .
The magnitude of the force experienced by objects at different distances follows the inverse-square law, where the effect falls off in proportion to the square of the distance.
As such, a function for calculating this falloff would look something like the following, where represents the distance between the explosion and the object that's reacting to it, and is the magnitude that would be experienced by an object one meter away:
Let's revisit our hypothetical explosion, where we defined to be . An object meters away from the same explosion will experience Newton of force as:
An object that is only centimeters ( meters) away will experience Newtons of force as:
Implementing Falloff
Let's update our ApplyPositionalForce()
function to modify its effects based on the distance between our object and the origin of the force:
// GameObject.h
// ...
class GameObject {
// ...
private:
void ApplyPositionalImpulse(
const Vec2& Origin, float Magnitude
) {
Vec2 Displacement{Position - Origin};
Vec2 Direction{Displacement.Normalize()};
float Distance{Displacement.GetLength()};
// Apply inverse-square law with a small
// offset to prevent extreme forces
float AdjustedMagnitude{Magnitude /
(Distance * Distance)};
ApplyImpulse(Direction * AdjustedMagnitude);
}
// ...
};
The inverse-linear law is quite often a source of janky physics as, when the distance between an object and a force gets very small, the resulting magnitude of that force becomes extremely large. Additionally, if the distance is ever exactly , our division will become problematic.
To solve this, we should intervene in our falloff calculation. In games, we don't need to be especially accurate, but we do need to be resource-efficient, so a common solution is to add some small number to our distances:
Let's update our code to use this:
// GameObject.h
// ...
class GameObject {
// ...
private:
void ApplyPositionalImpulse(
const Vec2& Origin, float Magnitude
) {
Vec2 Displacement{Position - Origin};
Vec2 Direction{Displacement.Normalize()};
float Distance{Displacement.GetLength()};
// Apply inverse-square law with a small
// offset to prevent extreme forces
float AdjustedMagnitude{Magnitude /
((Distance + 0.1f) * (Distance + 0.1f))};
ApplyImpulse(Direction * AdjustedMagnitude);
}
// ...
};
Complete Code
Our updated Scene
and GameObject
classes that include all of the topics we covered are provided below:
Summary
This lesson explores implementing momentum and impulses in our physics system, enabling instantaneous forces like jumps and explosions.
We've learned how to calculate the effects of these forces on objects with different masses and at varying distances. Key takeaways:
- Momentum is the product of mass and velocity, measured in or Newton-seconds,
- Impulse represents a change in momentum and equals force multiplied by time
- Instantaneous impulses apply forces within a single frame without being affected by delta time
- Positional impulses allow for effects like explosions where force originates from a specific point
- Force falloff follows the inverse-square law, making objects closer to the origin experience stronger effects
- Adding a small offset to distance calculations prevents extreme forces when objects are very close to the origin
Bounding Boxes
Discover bounding boxes: what they are, why we use them, and how to create them