Home >Web Front-end >JS Tutorial >Use JavaScript to play with game physics (1) Kinematics simulation and particle system_javascript skills

Use JavaScript to play with game physics (1) Kinematics simulation and particle system_javascript skills

WBOY
WBOYOriginal
2016-05-16 18:24:491436browse

Series Introduction
Perhaps Sir Issac Newton (1643-1727) three hundred years ago did not imagine that physics is widely used in many games today , animation. Why use physics in these applications? The author believes that since we were born, we have been feeling the laws of the physical world and realizing how objects "move normally" in this world. For example, when shooting a ball, the ball is a parabola (a spinning ball may become a curved ball), A stone tied to the end of a string will oscillate at a fixed frequency, and so on. For objects in games or animations to feel realistic, they need to move in a way that matches our expectations of "normal movement."
Today’s game animations apply a variety of physical simulation technologies, such as kinematics simulation, rigid body dynamics simulation, string/cloth simulation, and soft body dynamics. Simulation (soft body dynamics simulation), fluid dynamics simulation (fluid dynamics simulation), etc. In addition, collision detection is required in many simulation systems.
This series hopes to introduce some of the most basic knowledge in this area, continue to use JavaScript as examples, and experience it in a real-time interactive way.
Introduction to this article
As the first article in the series, this article introduces the simplest kinematic simulation, with only two very simple formulas. Kinematic simulation can be used to simulate the movement of many objects (such as Mario's jumping, cannonballs, etc.). This article will use the particle system to create some visual special effects (the particle system can actually be used to play the game, not just the visual special effects). ).
Kinematics simulation
Kinematics studies the movement of objects. The difference from dynamics is that kinematics does not consider the mass/moment of inertia of the object, and The force and torque applied to the object are not considered.
Let’s first recall Newton’s first law of motion:
When an object is not acted upon by an external force, or when the net force is zero, what was originally at rest will always be at rest, and what was originally in motion will always be moving along a straight line with constant speed. This law is also called the "Law of Inertia". This law states that in addition to its position, every object also has a linear velocity state. However, it is not interesting to only simulate objects that are not affected by forces. Putting aside the concept of force, we can use linear acceleration to affect the movement of an object. For example, to calculate the y-axis coordinate of a free-falling body at any time t, you can use the following analytical solution:
Use JavaScript to play with game physics (1) Kinematics simulation and particle system_javascript skills
where, and are respectively the y-axis coordinates at t=0 initial coordinates and velocity, and g is the gravitational acceleration.
Although this analytical solution is simple, it has some shortcomings. For example, g is a constant and cannot be changed during the simulation process. In addition, when an object encounters an obstacle and collides, it is difficult for this formula to handle this discontinuity. (discontinuity).
In computer simulations, it is often necessary to calculate continuous object states. In game terms, this means calculating the state of the first frame, the state of the second frame, and so on. Assume the state of the object at any time t: the position vector is, the velocity vector is, and the acceleration vector is. We want to calculate the state at the next simulation time from the state at time. The simplest method is to use the Euler method for numerical integration:
Use JavaScript to play with game physics (1) Kinematics simulation and particle system_javascript skills
The Euler method is very simple, but it has accuracy and stability issues, which will be ignored in this article. these questions. The example in this article uses two-dimensional space. We first implement a JavaScript two-dimensional vector class:

Copy the code The code is as follows:

// Vector2.js
Vector2 = function(x, y) { this.x = x; this.y = y; };

Vector2.prototype = {
copy : function() { return new Vector2(this.x, this.y); },
length : function() { return Math.sqrt(this.x * this.x this.y * this.y ); },
sqrLength : function() { return this.x * this.x this.y * this.y; },
normalize : function() { var inv = 1/this.length() ; return new Vector2(this.x * inv, this.y * inv); },
negate : function() { return new Vector2(-this.x, -this.y); },
add : function(v) { return new Vector2(this.x v.x, this.y v.y); },
subtract : function(v) { return new Vector2(this.x - v.x, this.y - v.y); },
multiply : function(f) { return new Vector2(this.x * f, this.y * f); },
divide : function(f) { var invf = 1/f; return new Vector2(this.x * invf, this.y * invf); },
dot : function(v) { return this.x * v.x this.y * v.y; }
};

Vector2.zero = new Vector2(0, 0);

Then, you can use HTML5 Canvas to depict the simulation process:
Copy the code The code is as follows:

var position = new Vector2(10, 200);
var velocity = new Vector2(50, -50);
var acceleration = new Vector2(0, 10);
var dt = 0.1;
function step() {
position = position.add(velocity.multiply(dt));
velocity = velocity.add(acceleration.multiply(dt));
ctx.strokeStyle = "#000000";
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
ctx.arc(position.x, position.y, 5, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
start("kinematicsCancas", step);












 

修改代码试试看


  • 改变起始位置

  • 改变起始速度(包括方向)

  • 改变加速度




  • 这程序的核心就是step()函数头两行代码。很简单吧?
    粒子系统
    粒子系统(particle system)是图形里常用的特效。粒子系统可应用运动学模拟来做到很多不同的效果。粒子系统在游戏和动画中,常常会用来做雨点、火花、烟、爆炸等等不同的视觉效果。有时候,也会做出一些游戏性相关的功能,例如敌人被打败后会发出一些闪光,主角可以把它们吸收。
    粒子的定义
    粒子系统模拟大量的粒子,并通常用某些方法把粒子渲染。粒子通常有以下特性:
  • 粒子是独立的,粒子之间互不影响(不碰撞、没有力)

  • 粒子有生命周期,生命结束后会消失

  • 粒子可以理解为空间的一个点,有时候也可以设定半径作为球体和环境碰撞

  • 粒子带有运动状态,也有其他外观状态(例如颜色、影像等)

  • 粒子可以只有线性运动,而不考虑旋转运动(也有例外)


  • 以下是本文例子里实现的粒子类:
    复制代码 代码如下:
    // Particle.js
    Particle = function(position, velocity, life, color, size) {
    this.position = position;
    this.velocity = velocity;
    this.acceleration = Vector2.zero;
    this.age = 0;
    this.life = life;
    this.color = color;
    this.size = size;
    };

    游戏循环
    粒子系统通常可分为三个周期:
    发射粒子
    模拟粒子(粒子老化、碰撞、运动学模拟等等)
    渲染粒子
    在游戏循环(game loop)中,需要对每个粒子系统执行以上的三个步骤。
    生与死
    在本文的例子里,用一个JavaScript数组particles储存所有活的粒子。产生一个粒子只是把它加到数组末端。代码片段如下:
    复制代码 代码如下:
    //ParticleSystem.js
    function ParticleSystem() {
    // Private fields
    var that = this;
    var particles = new Array();
    // Public fields
    this.gravity = new Vector2(0, 100);
    this.effectors = new Array();
    // Public methods
    this.emit = function(particle) {
    particles.push(particle);
    };
    // ...
    }

    粒子在初始化时,年龄(age)设为零,生命(life)则是固定的。年龄和生命的单位都是秒。每个模拟步,都会把粒子老化,即是把年龄增加Delta t,年龄超过生命,就会死亡。代码片段如下:
    复制代码 代码如下:
    function ParticleSystem() {
    // ...
    this.simulate = function(dt) {
    aging(dt);
    applyGravity();
    applyEffectors();
    kinematics(dt);
    };
    // ...
    // Private methods
    function aging(dt) {
    for (var i = 0; i < particles.length; ) {
    var p = particles[i];
    p.age = dt;
    if (p.age >= p.life)
    kill(i);
    else
    i ;
    }
    }
    function kill(index) {
    if (particles.length > 1)
    particles[index] = particles[particles.length - 1];
    particles.pop();
    }
    // ...
    }

    In the function kill(), a trick is used. Because the order of the particles in the array is not important, to delete a particle in the middle, just copy the last particle to that element and use pop() to remove the last particle. This is usually faster than directly deleting elements from the middle of an array (the same is true for arrays or std::vectors in C).
    Kinematics simulation
    Just apply the two most important sentences of kinematics simulation code in this article to all particles. In addition, each simulation will first write the gravitational acceleration into the acceleration of the particles. This is done so that the acceleration can be changed each time in the future (we will talk about this in the sequel).
    Copy code The code is as follows:
    function ParticleSystem() {
    // ...
    function applyGravity() {
    for (var i in particles)
    particles[i].acceleration = that.gravity;
    }
    function kinematics(dt) {
    for (var i in particles) {
    var p = particles[i];
    p.position = p.position.add(p.velocity.multiply(dt));
    p.velocity = p.velocity.add (p.acceleration.multiply(dt));
    }
    }
    // ...
    }

    Rendering
    particles can be done in many different ways Rendering, such as using circles, line segments (current position and previous position), images, sprites, etc. This article uses a circle and controls the transparency of the circle according to the age-to-life ratio. The code snippet is as follows:
    Copy the code The code is as follows:
    function ParticleSystem() {
    // ...
    this.render = function(ctx) {
    for (var i in particles) {
    var p = particles[ i];
    var alpha = 1 - p.age / p.life;
    ctx.fillStyle = "rgba("
    Math.floor(p.color.r * 255) ","
    Math.floor(p.color.g * 255) ","
    Math.floor(p.color.b * 255) ","
    alpha.toFixed(2) ")";
    ctx.beginPath();
    ctx.arc(p.position.x, p.position.y, p.size, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fill();
    }
    }
    // ...
    }

    The basic particle system is completed
    In the following examples, each The frame will emit a particle, its position is in the middle of the canvas (200,200), the emission direction is 360 degrees, the speed is 100, the life is 1 second, the color is red, and the radius is 5 pixels.
    Copy code The code is as follows:

    var ps = new ParticleSystem();
    var dt = 0.01;
    function sampleDirection() {
    var theta = Math.random() * 2 * Math.PI;
    return new Vector2(Math.cos(theta), Math.sin(theta) );
    }
    function step() {
    ps.emit(new Particle(new Vector2(200, 200), sampleDirection().multiply(100), 1, Color.red, 5)) ;
    ps.simulate(dt);
    clearCanvas();
    ps.render(ctx);
    }
    start("basicParticleSystemCanvas", step);












    Simple collision
    In order to illustrate the advantages of using numerical integration over analytical solutions, this article adds a simple collision to the particle system. We want to add a requirement. When the particles hit the inner wall of the rectangular chamber (which can be set to the size of the entire Canvas), they will collide and rebound. The collision is perfectly elastic.
    In terms of programming, I use callbacks to perform this function. The ParticleSystem class has an array of effectors. Before performing kinematics simulation, the apply() function of each effectors object is executed:
    The rectangular chamber is implemented like this:
    Copy code The code is as follows:
    // ChamberBox.js
    function ChamberBox(x1, y1, x2, y2) {
    this.apply = function( particle) {
    if (particle.position.x - particle.size < x1 || particle.position.x particle.size > x2)
    particle.velocity.x = -particle.velocity.x;
    if (particle.position.y - particle.size < y1 || particle.position.y particle.size > y2)
    particle.velocity.y = -particle.velocity.y;
    };
    }

    This is actually to reverse the velocity component in that direction when it detects that the particle exceeds the range of the inner wall.
    In addition, the main loop of this example no longer clears the entire Canvas every time, but draws a translucent black rectangle every frame to simulate the effect of motion blur. The color of the particles is also randomly sampled from two colors.
    Copy code The code is as follows:

    var ps = new ParticleSystem();
    ps .effectors.push(new ChamberBox(0, 0, 400, 400)); // The most important thing is the addition of this statement
    var dt = 0.01;
    function sampleDirection(angle1, angle2) {
    var t = Math.random();
    var theta = angle1 * t angle2 * (1 - t);
    return new Vector2(Math.cos(theta), Math.sin(theta));
    }
    function sampleColor(color1, color2) {
    var t = Math.random();
    return color1.multiply(t).add(color2.multiply(1 - t));
    }
    function step() {
    ps.emit(new Particle(new Vector2(200, 200), sampleDirection(Math.PI * 1.75, Math.PI * 2).multiply(250), 3, sampleColor (Color.blue, Color.purple), 5));
    ps.simulate(dt);
    ctx.fillStyle="rgba(0, 0, 0, 0.1)";
    ctx.fillRect (0,0,canvas.width,canvas.height);
    ps.render(ctx);
    }
    start("collisionChamberCanvas", step);



    Interactive launch
    Last example added The interactive function emits particles at the mouse position, and the direction of the particles is based on the mouse movement speed plus a little noise. Randomness is added to the particle size and life.
    Copy code The code is as follows:
    var ps = new ParticleSystem();
    ps.이펙터.push(new ChamberBox(0, 0, 400, 400));
    var dt = 0.01;
    var oldMousePosition = Vector2.zero, newMousePosition = Vector2.zero;
    function SampleDirection(angle1, angle2) {
    var t = Math.random();
    var theta = 각도1 * t 각도2 * (1 - t);
    새 Vector2(Math.cos(theta), Math.sin(theta))를 반환합니다.
    }
    function SampleColor(color1, color2) {
    var t = Math.random();
    color1.multiply(t).add(color2.multiply(1 - t))를 반환합니다.
    }
    function SampleNumber(value1, value2) {
    var t = Math.random();
    반환 값1 * t 값2 * (1 - t);
    }
    function step() {
    var speed = newMousePosition.subtract(oldMousePosition).multiply(10);
    속도 = 속도.add(sampleDirection(0, Math.PI * 2).multiply(20));
    var color = SampleColor(Color.red, Color.yellow);
    var life = 샘플번호(1, 2);
    var size = SampleNumber(2, 4);
    ps.emit(new Particle(newMousePosition, 속도, 수명, 색상, 크기));
    oldMousePosition = newMousePosition;
    ps.simulate(dt);
    ctx.fillStyle="rgba(0, 0, 0, 0.1)";
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ps.render(ctx);
    }
    start("interactiveEmitCanvas", step);
    canvas.onmousemove = function(e) {
    if (e.layerX || e.layerX == 0) { // Firefox
    e.target.style.position='relative';
    newMousePosition = new Vector2(e.layerX, e.layerY);
    }
    else
    newMousePosition = new Vector2(e.offsetX, e.offsetY);
    };

    <버튼 onclick="stop();" type="button">중지


    总结
    本文介绍了最简单的运动school模拟,使用欧拉方法数值积分,并以此法去实现一个有简单碰撞的粒子系统。本文的精华其实只有两条简单公式(只有两个加数 and 两个乘数),希望让读单,其实물리模拟可以很简单.但这例子能扩至삼국공공间,只须把Vector2换成Vector3。本文完整源代码可下载
    续篇会谈及在此基础上加其他물리现象,有机会再加入其他물리模拟课题。希望各位支持,并给本人更多意见。
    Statement:
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

    Modify the code and try it


  • Change the launch position

  • Launch upwards, the launch range is within 90 degrees

  • Change life

  • Change radius

  • Emit 5 particles per frame