5

foreword

Two months have passed since the first article. In the last article, I introduced what a physics engine is and what basic knowledge needs to be mastered to continue development. On this basis, we launched the second article to explore the learning path of the physics engine. In our daily development, we naturally do not use very complex physical systems. Most games are based on rigid bodies, and then make certain adaptations in the game scene, and finally simulate the motion state of objects in our conventional understanding. It makes us feel that these displacements and deformations seem to be taken for granted and conform to the laws. One of the most famous mobile games is Angry Birds. So how do we achieve the effect in Angry Birds? Let's explore step by step.

愤怒的小鸟

Movement of particles in the game

First of all, let's take a look at such a scene. In reality, the speed of an ordinary bullet is about 300m/s, which is close to the speed of sound. If you put it in the game and still follow the normal speed, the bullet will only be fired at Disappearing in an instant, the shooting game has become a game in which the player cannot observe the ballistics at all, and the horizontal version of the game has also become a horror game in which only the muzzle flame can be seen, but the bullet screen cannot be seen.

弹幕游戏

The same is true for the bird as a "bullet". If it is really calculated according to the speed of flying in the air, then we can only see a vigorous figure flashing by, and then pierce through the building like a bullet and fly to the sky. far away. This is obviously not the effect we want.

So how to solve such a problem? Obviously, we should reduce the speed of high-speed objects in the game. Generally speaking, depending on the size of the map, the speed can be limited to 5-25m/s. Then the problem of speed is solved, but when the speed of an object is reduced from 300 to 25, its energy, the phenomenon of collision, will be very different. When a slow bird hits a wall, it may just fall silently, and the wall says: "What tickled me just now?". How to solve such a problem?

First of all, we all know the relationship between kinetic energy, speed and mass: $E=\frac{1}{2}mv^2$, then when the speed decreases, we need to increase the mass. The relationship between the two is that the mass increases The ratio is equal to the ratio of the speed drop, that is, $\Delta m$ = $\Delta s^2$. When the acceleration of a bullet with a mass of 5g and a speed of 300m/s is reduced to 25m/s, the mass should increase by 144 times to 720g. In this way we solve the problem of reduced energy due to reduced speed. At this point, our little bird has changed from an ordinary chick to a strong and muscular bird that can knock wild boars away with just one movement.

But a new problem has arisen. The speed reduction is followed by the deformation of the parabola. It could fly farther, but because the initial speed is too small, it will fall to the ground not too far away. It turned into a big weight again. How to solve this problem? That is to restore the parabola to its original shape.

When an object is moving obliquely or horizontally, its initial velocity determines the shape of the parabola. The initial velocity in the height and vertical directions determines the action time of gravity, and the initial velocity in the horizontal direction determines the horizontal displacement. When the speed is reduced, the time of gravity and the displacement in the horizontal direction will change, resulting in a very different trajectory than before. We know the displacement in the horizontal direction: $x = v_{horizontal}t$, and the displacement in the vertical direction: $y = v_{vertical}t - \frac{1}{2}gt^2$, where t is replaced , you can get the parabola equation, $y = \frac{v_{vertical}}{v_{horizontal}}x - \frac{1}{2}\frac{g}{v_{horizontal}^2}x^ 2$, where $\frac{v_{vertical}}{v_{horizontal}}$ is determined by the angle of the object's emission, which is a fixed value, then what affects the curve is $\frac{g}{v_{horizontal}^ 2}$, from this we can know that the conversion formula of gravitational acceleration is $g_b = \frac{g_{normal}}{\Delta s^2}$ , but is this the case? No, because when we reduce the speed, our scene is actually shrinking. The original distance of 300 meters is shortened to 25 meters at the same time, so y and x need to have the same scaling ratio to get the same shape of the parabola. Then we can conclude that the real gravitational acceleration conversion formula should be $g_b = \frac{g_{normal}}{\Delta s}$ (some simultaneous boring equations are omitted here).

抛物线

Synergy in the game

force accumulator

Anyone who has been in contact with simple mechanics knows that when multiple forces act on an object, we can process the combination of complex multiple forces into forces in several directions that we can easily calculate through the combined force and the component force.

First of all, let's make it clear that the resultant force is the superposition of multiple forces at the vector level, so we need to use the addition and subtraction of vectors. The most commonly used is the parallelogram rule, and the processing of the parallelogram rule in the coordinate axis is very simple, that is, adding the coordinates of the two vectors. But the force itself is not necessarily a constant force, such as air resistance, which may increase with speed; buoyancy, which increases with the volume entering the water. So we need to calculate the resultant force all the time, so in the game, it is calculated once every frame.

合力与分力

We can do this by creating a class dedicated to the resultant force and call it a force accumulator. The concept of accumulator is widely used in object-oriented programming and functional programming, and there is also a corresponding operation method in js - Array.reduce. It can directly accumulate the elements in the array. The accumulation here does not only mean accumulation, but can be any operation. Then things become simpler, we just need to aggregate the forces in an array and give it an accumulating function. Let's take a two-dimensional plane as an example to see:

class ForceAccum () {
    constructor () { // 初始化力的数组
        this.forceArray = []
    }

    addForce(force) { // 添加力到数组中
        this.forceArray.push(force)
    }
    
    clearForceAccum () { // 清除数组用于下一次计算
        this.forceArray = []
    }
    
    forceReducer (prevForce, currentForce) { // 计算合力
        const x = prevForce.x + currentForce.x
        const y = prevForce.y + currentForce.y
        return {x, y}
    }
    
    getForce () { //获取合力
        const force = this.forceArray.reduce(forceReducer)
        return force
    }
}

const forceAccum = new ForceAccum() //新建一个力累加器
forrceAccum.addForce(重力) // 增加重力
forrceAccum.addForce(阻力) // 增加阻力
forrceAccum.addForce(推力) // 增加推力
const force = forrceAccum.getForce() //计算合力
forrceAccum.clearForceAccum() // 清除数组用于下次计算

The above is the simplest force accumulator and its application, adding force to the system, and finally obtaining the resultant force. After obtaining the resultant force, we can obtain the acceleration according to the second law of Niu, and finally obtain the velocity according to the acceleration integral, and then obtain the position according to the velocity integral. Then we can repeat the above operation every frame.

And what kind of power do we need in Angry Birds? It can be seen that what is needed is the thrust caused by the elastic force of a slingshot and its own gravity, then we only need to add these two forces to our force accumulator to calculate the force on our muscle bird and its level. and the vertical force.

![joint force and component force](
https://img10.360buyimg.com/ling/jfs/t1/117644/36/21135/201920/622de89dE5e46e517/396716c4cc67189e.png)

In our actual motion, there is actually only one gravity at work (ignoring air resistance), and the thrust is finally converted into the initial speed of flight in the elastic deformation of the slingshot. Therefore, in order to simplify the calculation, the thrust of the spring can be directly converted into the initial velocity. Of course, it can also be calculated by impulse and momentum.

So how should we get and calculate each such force in the code? This requires the use of another thing - a force generator.

force generator

A force generator, as the name suggests, is a device for creativity. Because we are in motion, there are all kinds of forces, some of which are constant, such as gravity when the mass is constant; some are changed according to the scene, such as the air resistance when the speed is constantly changing; and some are It is changed according to the player's operation, such as thrust, elastic force, etc.

So in fact, we can register corresponding force generators for them according to the characteristics of these forces, so that they can be better managed. The principle of the force generator is very simple, we create it through a class. However, the characteristics of each force are different, so we need to deal with different forces. There are two different methods. One is to write all the forces that need to be included in a class. When you need to create a force, use the corresponding method; the other is to pass the method and parameters for generating the force into a class, and finally return the required force, or directly inject the force into the force accumulator. The latter flexibility will allow us to better control our systems in complex mechanical systems.

class ForceRegister () {
    constructor (forceAccum,func, param) {
        this.forceAccum = forceAccum
        this.func = func
        this.param = param
        this.force = null
    }
    
    createForce () { // 返回需要的力
        this.force = this.func(this.param)
        return this.force
    }
    
    addForce () { // 直接将力注入力累加器
        this.createForce()
        forrceAccum.addForce(this.force)
    }
}

The func and param in the above code are the methods and parameters of the force we need to generate. For example, gravity, gravity only needs to enter the mass of the object (or the inverse of the mass) and the acceleration of gravity, and the corresponding force can be obtained - { x:0, y: mg }. The same is true for the resistance, the resistance equation is $\displaystyle F_{D}\,=\,{\tfrac {1}{2}}\,\rho \,v^{2}\,C_{D}\,A $. Let's not study the parameters in detail, and simplify it to get $F_{D} = av^2$, a is a parameter under a certain condition. In this case the parameters of our resistance generator are a and velocity v, and then the direction is the opposite direction of the movement velocity.

With force generators and force accumulators, we can easily manage the mechanics in the game. However, each frame in the game requires a lot of calculation and refresh, and the performance requirements are definitely relatively high. So there are various optimization methods.

For example, we can remove air resistance, water flow resistance, etc. to save complicated calculations, or only give a fixed value for calculation. For the more important gravity, we can directly store the acceleration of gravity into the entire physical system by means of built-in gravity, instead of incorporating gravity into each calculation of each object.

So in fact, we understand that we only need to set the gravitational acceleration in the system, and set the vector of the initial velocity according to the user operation, and then we can quickly complete the parabolic motion of a bird.

Summarize

With simple mechanics knowledge and proper code, we can create a super simple version of Angry Birds world that conforms to the laws of mechanics. But this is actually far from enough. In addition to the simple superposition and displacement of forces in a game, there are also moments, collisions, rotations, angular velocities, etc. Only with these additions can we calculate collisions, score, and perform reasonably The force, rotation, and movement of an object after being hit. Please look forward to the following chapters of our physics engine series for these interesting content~

Welcome to the blog of Bump Lab: aotu.io

Or pay attention to the AOTULabs official account (AOTULabs), and push articles from time to time.


凹凸实验室
2.3k 声望5.5k 粉丝

凹凸实验室(Aotu.io,英文简称O2) 始建于2015年10月,是一个年轻基情的技术团队。