Achieving Angry Birds‑Style Physics: Speed Scaling, Mass Adjustment, and Force Accumulators in JavaScript
This article explains how to adapt real‑world physics for games like Angry Birds by scaling speeds, adjusting mass, redefining gravity, and implementing force accumulators and generators in JavaScript, providing code examples and practical tips for realistic projectile motion.
Preface
Two months after the first article on what a physics engine is and the basic knowledge required, this second part explores how to create game‑style physics that feels like Angry Birds. Most games use simple rigid‑body systems, so we need to adapt real‑world mechanics to produce believable motion.
Particle Motion in Games
In reality a bullet travels around 300 m/s, near the speed of sound. If we used that speed directly in a game, the projectile would disappear instantly, making it impossible for players to see its trajectory. To keep the action visible we must lower the speed of fast objects.
Typical game maps allow speeds of 5–25 m/s. Reducing speed, however, also reduces kinetic energy, so we compensate by increasing the object's mass proportionally. For example, a 5 g projectile at 300 m/s would need a mass 144 times larger (≈720 g) to retain the same energy when its speed is reduced to 25 m/s.
Lower speed also flattens the parabolic arc, causing the object to drop too quickly. To restore the original trajectory we must scale both the horizontal and vertical distances by the same factor used for speed, effectively adjusting the gravity conversion formula to keep the curve shape consistent.
Combined Forces in Games
Force Accumulator
When multiple forces act on an object, we can sum them as vectors using the parallelogram rule. In a game we recompute the resultant force each frame because some forces (e.g., air drag) change with velocity.
We can encapsulate this logic in a class called ForceAccum. The class stores an array of forces, provides methods to add and clear forces, and uses Array.reduce to sum the vectors.
class ForceAccum {
constructor() {
// initialize force array
this.forceArray = []
}
addForce(force) { // add a force to the array
this.forceArray.push(force)
}
clearForceAccum() { // clear the array for the next frame
this.forceArray = []
}
forceReducer(prevForce, currentForce) { // compute combined force
const x = prevForce.x + currentForce.x
const y = prevForce.y + currentForce.y
return {x, y}
}
getForce() { // retrieve the combined force
const force = this.forceArray.reduce(this.forceReducer)
return force
}
}
const forceAccum = new ForceAccum() // create a new accumulator
forceAccum.addForce(gravity) // add gravity
forceAccum.addForce(drag) // add drag
forceAccum.addForce(thrust) // add thrust
const force = forceAccum.getForce() // compute resultant force
forceAccum.clearForceAccum() // prepare for next frameWith the resultant force we apply Newton's second law to obtain acceleration, integrate to get velocity, and integrate again to update position each frame.
For an Angry Birds‑style game we only need two forces: the slingshot’s thrust and gravity. Adding these to the accumulator yields the bird’s motion in both horizontal and vertical directions.
Force Generator
A force generator creates a specific force based on parameters such as mass, gravity, or velocity. We can register generators for each type of force and let them produce the appropriate vector when needed.
class ForceRegister {
constructor(forceAccum, func, param) {
this.forceAccum = forceAccum
this.func = func
this.param = param
this.force = null
}
createForce() { // return the generated force
this.force = this.func(this.param)
return this.force
}
addForce() { // directly add the force to the accumulator
this.createForce()
this.forceAccum.addForce(this.force)
}
}For example, a gravity generator receives an object’s mass (or its inverse) and the gravitational constant to produce a force {x:0, y: m·g}. A drag generator uses a coefficient a and the current velocity v, applying a force opposite to the motion direction.
Because each frame may involve many force calculations, performance optimizations are important. Common tricks include omitting air resistance, using fixed values for minor forces, or storing gravity as a global constant rather than recomputing it for every object.
Summary
By combining basic mechanics with simple code structures—force accumulators and generators—we can build a lightweight Angry Birds‑style physics simulation. Extending this foundation with torque, collisions, angular velocity, and scoring will enable more sophisticated gameplay, which will be covered in future articles of the physics‑engine series.
Aotu Lab
Aotu Lab, founded in October 2015, is a front-end engineering team serving multi-platform products. The articles in this public account are intended to share and discuss technology, reflecting only the personal views of Aotu Lab members and not the official stance of JD.com Technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
