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
Release: 2016-05-16 18:24:49
Original
1437 people have browsed it
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:
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:
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:
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);
//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); }; // ... }
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).
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:
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.
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);
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
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:
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.
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.
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。本文完整源代码可下载。 续篇会谈及在此基础上加其他물리现象,有机会再加入其他물리模拟课题。希望各位支持,并给本人更多意见。
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