转自:

How Physics Engines Work

高中物理全还给老师了啊啊啊啊啊啊


牛顿第二定律

物体加速度的大小跟物体受到的作用力成正比,跟物体的质量成反比,加速度的方向跟合外力的方向相同。 而以物理学的观点来看,牛顿运动第二定律亦可以表述为“物体随时间变化之动量变化率和所受外力之和成正比”,即动量对时间的一阶导数等 于外力之和。牛顿第二定律说明了在宏观低速下,a∝F/m,F∝ma,用数学表达式可以写成F=kma,其中的k为比例系数,是一个常数。但由于当时没有 规定多大的力作为力的单位,比例系数k的选取就有一定的任意性,如果取k=1,就有F=ma,这就是今天我们熟知的牛顿第二定律的数学表达式。

The process for simulating an object’s motion goes something like this:

  1. Figure out what the forces are on an object
  2. Add those forces up to get a single “resultant” or “net” force  更新F
  3. Use F = ma to calculate the object’s acceleration due to those forces  计算加速度a
  4. Use the object’s acceleration to calculate the object’s velocity  计算速度v
  5. Use the object’s velocity to calculate the object’s position  计算位置
  6. Since the forces on the object may change from moment to moment, repeat this process from #1, forever 迭代
last_acceleration = acceleration
position += velocity * time_step + ( 0.5 * last_acceleration * time_step^ )
new_acceleration = force / mass
avg_acceleration = ( last_acceleration + new_acceleration ) /
velocity += avg_acceleration * time_step

通常涉及到的简单的力:

  • weight force   重力
  • spring force   弹簧
  • viscous damping force  阻尼
  • air drag 空气阻力  $F_D=\frac{1}{2}\rho v^2C_DA$

示例:一维, 重力+空气阻力+阻尼

/*
The following is not free software. You may use it for educational purposes, but you may not redistribute or use it commercially.
(C) Burak Kanber 2012
*/ var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
height = 400,
width = 400,
x = 200,
y = 0,
vy = 0,
ay = 0,
m = 0.1, // Ball mass in kg
r = 10, // Ball radius in cm, or pixels.
dt = 0.02, // Time step.
e = -0.5, // Coefficient of restitution ("bounciness")
rho = 1.2, // Density of air. Try 1000 for water.
C_d = 0.47, // Coeffecient of drag for a ball
A = Math.PI * r * r / 10000 // Frontal area of the ball; divided by 10000 to compensate for the 1px = 1cm relation
; ctx.fillStyle = 'red'; function loop()
{
var fy = 0; /* Weight force, which only affects the y-direction (because that's the direction gravity points). */
fy += m * 9.81; //重力 /* Air resistance force; this would affect both x- and y-directions, but we're only looking at the y-axis in this example. */
fy += -1 * 0.5 * rho * C_d * A * vy * vy; //空气阻力 /* Verlet integration for the y-direction */
dy = vy * dt + (0.5 * ay * dt * dt); //计算位置
/* The following line is because the math assumes meters but we're assuming 1 cm per pixel, so we need to scale the results */
y += dy * 100;
new_ay = fy / m;
avg_ay = 0.5 * (new_ay + ay);
vy += avg_ay * dt; /* Let's do very simple collision detection */
if (y + r > height && vy > 0)
{
/* This is a simplification of impulse-momentum collision response. e should be a negative number, which will change the velocity's direction. */
vy *= e; //碰撞,反向,速度减小
/* Move the ball back a little bit so it's not still "stuck" in the wall. */
y = height - r; //避免重复判断
} draw();
} function draw()
{
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2, true);
ctx.fill();
ctx.closePath();
} /* A real project should use requestAnimationFrame, and you should time the frame rate and pass a variable "dt" to your physics function. This is just a simple brute force method of getting it done. */
setInterval(loop, dt * 1000);

对于全三维的情况,需要6个维度来描述(x,y,z维度的位移和旋转):

translation in x, y, and z (called “sway”, “heave” and “surge”), and rotation about x, y, and z (called “pitch”, “yaw”, and “roll”).


旋转+弹簧

position, velocity, and acceleration change  : 位置、速度、加速度
rotation angle, angular velocity, and angular acceleration : 角度、角速度、角加速度

看不下去了啊

/*
The following is not free software. You may use it for educational purposes, but you may not redistribute or use it commercially.
(C) Burak Kanber 2012
*/
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
height = 400,
width = 400,
stiffness = 0.5,
b = -1,
angularB = -7,
dt = 0.02; /* A more generic vector class could take an arbitrary number of elements, rather than just x, y, or z. This vector class is strictly 2D */
var V = function(x, y) {
this.x = x;
this.y = y;
}; V.prototype.add = function(v) {
return new V(v.x + this.x, v.y + this.y);
}; V.prototype.subtract = function(v) {
return new V(this.x - v.x, this.y - v.y);
}; V.prototype.scale = function(s) {
return new V(this.x * s, this.y * s);
}; V.prototype.dot = function(v) {
return (this.x * v.x + this.y * v.y);
}; /* Normally the vector cross product function returns a vector. But since we know that all our vectors will only be 2D (x and y only), any cross product we calculate will only have a z-component. Since we don't have a 3D vector class, let's just return the z-component as a scalar. We know that the x and y components will be zero. This is absolutely not the case for 3D. */
V.prototype.cross = function(v) {
return (this.x * v.y - this.y * v.x);
}; V.prototype.rotate = function(angle, vector) {
var x = this.x - vector.x;
var y = this.y - vector.y; var x_prime = vector.x + ((x * Math.cos(angle)) - (y * Math.sin(angle)));
var y_prime = vector.y + ((x * Math.sin(angle)) + (y * Math.cos(angle))); return new V(x_prime, y_prime);
}; /* Our simple rectangle class is defined by the coordinates of the top-left corner, the width, height, and mass. */
var Rect = function(x, y, w, h, m) {
if (typeof(m) === 'undefined') {
this.m = 1;
} this.width = w;
this.height = h; this.topLeft = new V(x, y);
this.topRight = new V(x + w, y);
this.bottomRight = new V(x + w, y + h);
this.bottomLeft = new V(x, y + h); this.v = new V(0, 0);
this.a = new V(0, 0);
this.theta = 0;
this.omega = 0;
this.alpha = 0;
this.J = this.m * (this.height*this.height + this.width*this.width) / 12000;
}; /* The Rect class only defines the four corners of the rectangle, but sometimes we need the center point so we can calulate that by finding the diagonal and cutting it in half. */
Rect.prototype.center = function() {
var diagonal = this.bottomRight.subtract(this.topLeft);
var midpoint = this.topLeft.add(diagonal.scale(0.5));
return midpoint;
}; /* To rotate a rectangle we'll update both its angle and rotate all four of the corners */
Rect.prototype.rotate = function(angle) {
this.theta += angle;
var center = this.center(); this.topLeft = this.topLeft.rotate(angle, center);
this.topRight = this.topRight.rotate(angle, center);
this.bottomRight = this.bottomRight.rotate(angle, center);
this.bottomLeft = this.bottomLeft.rotate(angle, center); return this;
}; /* Simply translate all four corners */
Rect.prototype.move = function(v) {
this.topLeft = this.topLeft.add(v);
this.topRight = this.topRight.add(v);
this.bottomRight = this.bottomRight.add(v);
this.bottomLeft = this.bottomLeft.add(v); return this;
} var rect = new Rect(200, 0, 100, 50);
rect.v = new V(0, 2);
var spring = new V(200, 0); ctx.strokeStyle = 'black'; var loop = function() {
var f = new V(0, 0);
var torque = 0; /* Start Velocity Verlet by performing the translation */
var dr = rect.v.scale(dt).add(rect.a.scale(0.5 * dt * dt));
rect.move(dr.scale(100)); /* Add Gravity */
f = f.add(new V(0, rect.m * 9.81)); /* Add damping */
f = f.add( rect.v.scale(b) ); /* Add Spring; we calculate this separately so we can calculate a torque. */
var springForce = rect.topLeft.subtract(spring).scale(-1 * stiffness);
/* This vector is the distance from the end of the spring to the box's center point */
var r = rect.center().subtract(rect.topLeft);
/* The cross product informs us of the box's tendency to rotate. */
var rxf = r.cross(springForce); torque += -1 * rxf;
f = f.add(springForce); /* Finish Velocity Verlet */
var new_a = f.scale(rect.m);
var dv = rect.a.add(new_a).scale(0.5 * dt);
rect.v = rect.v.add(dv); /* Do rotation; let's just use Euler for contrast */
torque += rect.omega * angularB; // Angular damping
rect.alpha = torque / rect.J;
rect.omega += rect.alpha * dt;
var deltaTheta = rect.omega * dt;
rect.rotate(deltaTheta); draw();
}; var draw = function() {
ctx.strokeStyle = 'black';
ctx.clearRect(0, 0, width, height);
ctx.save();
ctx.translate(rect.topLeft.x, rect.topLeft.y);
ctx.rotate(rect.theta);
ctx.strokeRect(0, 0, rect.width, rect.height);
ctx.restore(); ctx.strokeStyle = '#cccccc';
ctx.beginPath();
ctx.moveTo(spring.x,spring.y);
ctx.lineTo(rect.topLeft.x, rect.topLeft.y);
ctx.stroke();
ctx.closePath();
}; setInterval(loop, dt*1000);

碰撞检测

/*
The following is not free software. You may use it for educational purposes, but you may not redistribute or use it commercially.
(C) Burak Kanber 2012
*/
var canvas,
ctx,
height = 400,
width = 400,
stiffness = 0.5,
b = -1,
angularB = -1,
dt = 0.02;
Array.prototype.max = function() {
return Math.max.apply(null, this);
}; Array.prototype.min = function() {
return Math.min.apply(null, this);
};
var V = function(x, y) {
this.x = x;
this.y = y;
}; V.prototype.length = function() {
return Math.sqrt(this.x*this.x + this.y*this.y);
}; V.prototype.add = function(v) {
return new V(v.x + this.x, v.y + this.y);
}; V.prototype.subtract = function(v) {
return new V(this.x - v.x, this.y - v.y);
}; V.prototype.scale = function(s) {
return new V(this.x * s, this.y * s);
}; V.prototype.dot = function(v) {
return (this.x * v.x + this.y * v.y);
}; V.prototype.cross = function(v) {
return (this.x * v.y - this.y * v.x);
}; V.prototype.toString = function() {
return '[' + this.x + ',' + this.y + ']';
}; V.prototype.rotate = function(angle, vector) {
var x = this.x - vector.x;
var y = this.y - vector.y; var x_prime = vector.x + ((x * Math.cos(angle)) - (y * Math.sin(angle)));
var y_prime = vector.y + ((x * Math.sin(angle)) + (y * Math.cos(angle))); return new V(x_prime, y_prime);
};
var Rect = function(x, y, w, h, m) {
if (typeof(m) === 'undefined') {
this.m = 1;
} this.width = w;
this.height = h; this.active = true; this.topLeft = new V(x, y);
this.topRight = new V(x + w, y);
this.bottomRight = new V(x + w, y + h);
this.bottomLeft = new V(x, y + h); this.v = new V(0, 0);
this.a = new V(0, 0);
this.theta = 0;
this.omega = 0;
this.alpha = 0;
this.J = this.m * (this.height * this.height + this.width * this.width) / 12000;
}; Rect.prototype.center = function() {
var diagonal = this.bottomRight.subtract(this.topLeft);
var midpoint = this.topLeft.add(diagonal.scale(0.5));
return midpoint;
}; Rect.prototype.rotate = function(angle) {
this.theta += angle;
var center = this.center(); this.topLeft = this.topLeft.rotate(angle, center);
this.topRight = this.topRight.rotate(angle, center);
this.bottomRight = this.bottomRight.rotate(angle, center);
this.bottomLeft = this.bottomLeft.rotate(angle, center); return this;
}; Rect.prototype.move = function(v) {
this.topLeft = this.topLeft.add(v);
this.topRight = this.topRight.add(v);
this.bottomRight = this.bottomRight.add(v);
this.bottomLeft = this.bottomLeft.add(v); return this;
}; Rect.prototype.draw = function(ctx) {
ctx.strokeStyle = 'black';
ctx.save();
ctx.translate(this.topLeft.x, this.topLeft.y);
ctx.rotate(this.theta);
ctx.strokeRect(0, 0, this.width, this.height);
ctx.restore();
};
Rect.prototype.vertex = function(id)
{
if (id == 0)
{
return this.topLeft;
}
else if (id == 1)
{
return this.topRight;
}
else if (id == 2)
{
return this.bottomRight;
}
else if (id == 3)
{
return this.bottomLeft;
}
};
function intersect_safe(a, b)
{
var result = new Array(); var as = a.map( function(x) { return x.toString(); });
var bs = b.map( function(x) { return x.toString(); }); for (var i in as)
{
if (bs.indexOf(as[i]) !== -1)
{
result.push( a[i] );
}
} return result;
} satTest = function(a, b) {
var testVectors = [
a.topRight.subtract(a.topLeft),
a.bottomRight.subtract(a.topRight),
b.topRight.subtract(b.topLeft),
b.bottomRight.subtract(b.topRight),
];
var ainvolvedVertices = [];
var binvolvedVertices = []; /*
* Look at each test vector (shadows)
*/
for (var i = 0; i < 4; i++) {
ainvolvedVertices[i] = []; // Our container for involved vertces
binvolvedVertices[i] = []; // Our container for involved vertces
var myProjections = [];
var foreignProjections = []; for (var j = 0; j < 4; j++) {
myProjections.push(testVectors[i].dot(a.vertex(j)));
foreignProjections.push(testVectors[i].dot(b.vertex(j)));
} // Loop through foreignProjections, and test if each point is x lt my.min AND x gt m.max
// If it's in the range, add this vertex to a list
for (var j in foreignProjections) {
if (foreignProjections[j] > myProjections.min() && foreignProjections[j] < myProjections.max()) {
binvolvedVertices[i].push(b.vertex(j));
}
} // Loop through myProjections and test if each point is x gt foreign.min and x lt foreign.max
// If it's in the range, add the vertex to the list
for (var j in myProjections) {
if (myProjections[j] > foreignProjections.min() && myProjections[j] < foreignProjections.max()) {
ainvolvedVertices[i].push(a.vertex(j));
}
}
} // console.log( intersect_safe ( intersect_safe( involvedVertices[0], involvedVertices[1] ), intersect_safe( involvedVertices[2], involvedVertices[3] ) ) );
ainvolvedVertices = intersect_safe(intersect_safe(ainvolvedVertices[0], ainvolvedVertices[1]), intersect_safe(ainvolvedVertices[2], ainvolvedVertices[3]));
binvolvedVertices = intersect_safe(intersect_safe(binvolvedVertices[0], binvolvedVertices[1]), intersect_safe(binvolvedVertices[2], binvolvedVertices[3]));
/*
If we have two vertices from one rect and one vertex from the other, probably the single vertex is penetrating the segment
return involvedVertices;
*/ if (ainvolvedVertices.length === 1 && binvolvedVertices.length === 2)
{
return ainvolvedVertices[0];
}
else if (binvolvedVertices.length === 1 && ainvolvedVertices.length === 2)
{
return binvolvedVertices[0];
}
else if (ainvolvedVertices.length === 1 && binvolvedVertices.length === 1)
{
return ainvolvedVertices[0];
}
else if (ainvolvedVertices.length === 1 && binvolvedVertices.length === 0)
{
return ainvolvedVertices[0];
}
else if (ainvolvedVertices.length === 0 && binvolvedVertices.length === 1)
{
return binvolvedVertices[0];
}
else if (ainvolvedVertices.length === 0 && binvolvedVertices.length === 0)
{
return false;
}
else
{
console.log("Unknown collision profile");
console.log(ainvolvedVertices);
console.log(binvolvedVertices);
clearInterval(timer);
} return true; } var rect = new Rect(200, 0, 100, 50);
var wall = new Rect(125, 200, 100, 50);
rect.omega = -10; var loop = function() {
var f = new V(0, 0);
var torque = 0; /* Start Velocity Verlet by performing the translation */
var dr = rect.v.scale(dt).add(rect.a.scale(0.5 * dt * dt));
rect.move(dr.scale(100)); /* Add Gravity */
f = f.add(new V(0, rect.m * 9.81)); /* Add damping */
f = f.add(rect.v.scale(b)); /* Handle collision */
var collision = satTest(rect, wall);
if (collision)
{
var N = rect.center().subtract(collision); //.rotate(Math.PI , new V(0,0));
N = N.scale( 1 / N.length());
var Vr = rect.v;
var I = N.scale( -1 * (1 + 0.3) * Vr.dot(N) );
rect.v = I
rect.omega = -1 * 0.2 * (rect.omega / Math.abs(rect.omega)) * rect.center().subtract(collision).cross(Vr);
} /* Finish Velocity Verlet */
var new_a = f.scale(rect.m);
var dv = rect.a.add(new_a).scale(0.5 * dt);
rect.v = rect.v.add(dv); /* Do rotation; let's just use Euler for contrast */
torque += rect.omega * angularB; // Angular damping
rect.alpha = torque / rect.J;
rect.omega += rect.alpha * dt;
var deltaTheta = rect.omega * dt;
rect.rotate(deltaTheta); draw();
}; var draw = function() {
ctx.clearRect(0, 0, width, height);
rect.draw(ctx);
wall.draw(ctx); }; var timer; canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
ctx.strokeStyle = 'black';
timer = setInterval(loop, dt * 1000);

[翻译] 物理引擎javascript实现的更多相关文章

  1. HTML5之2D物理引擎 Box2D for javascript Games 系列 第一部分

    我要的是能在H5页面上跑的javascript版的Box2D啊!!! 最近想学习Javascript版本的Box2D JS物理引擎,无奈搜了半天也没找到相对比较系统的资料 官方网站也只是简单的介绍,A ...

  2. Verlet-js JavaScript 物理引擎

    subprotocol最近在Github上开源了verlet-js.地址为https://github.com/subprotocol/verlet-js.verlet-js是一个集成Verlet的物 ...

  3. HTML5之2D物理引擎 Box2D for javascript Games 系列 翻外篇--如何结合createJS应用box2d.js

    太久没有更新了,新年回来工作,突然有收到网友的邮件提问,居然还有人在关注,惭愧,找了下电脑上还有一点儿存着,顺便先发这一个番外篇吧,好歹可以看到真实的效果,等我考完英语,一定会更新下一章," ...

  4. Cocos2d-js官方完整项目教程翻译:六、添加Chipmunk物理引擎在我们的游戏世界里

    添加Chipmunk物理引擎在我们的游戏世界里         一.简介                   cocos2d JS能给我们力量来创造令人印象深刻的游戏世界.但缺乏某种现实.       ...

  5. Matter.js – 你不能错过的 2D 物理引擎

    Matter.js 是一个 JavaScript 2D 刚体物理引擎的网页.Matter.Engine 模块包含用于创建和操作引擎的方法.这个引擎是一个管理更新和渲染世界的模拟控制器. Matter. ...

  6. Cocos2d-x3.2 使用物理引擎进行碰撞检测[转]

    通常在游戏简单逻辑判断和模拟真实的物理世界时,我们只需要在定时器中判断游戏中各个精灵的条件是否满足判断条件就可以了.例如,在飞机大战中,判断我方子弹和敌机是否发生碰撞一般在定时器中通过敌机所在位置的矩 ...

  7. Cocos2d-x3.2总结---使用物理引擎进行碰撞检测

    [转自]: http://blog.csdn.net/cbbbc/article/details/38541099 通常在游戏简单逻辑判断和模拟真实的物理世界时,我们只需要在定时器中判断游戏中各个精灵 ...

  8. Cocos2d-x 使用物理引擎进行碰撞检测

    [转自]: http://blog.csdn.net/cbbbc/article/details/38541099 通常在游戏简单逻辑判断和模拟真实的物理世界时,我们只需要在定时器中判断游戏中各个精灵 ...

  9. p2.js物理引擎学习

    P2简介 P2是一款基于Javascript编写的HTML5 2D物理引擎,和Box2D.Nape等2D物理引擎一样,P2集成了各种复杂的物理公式和算法,可以帮助我们轻松的实现碰撞.反弹等物理现象的模 ...

随机推荐

  1. 升级OpenSSL修复高危漏洞Heartbleed

    升级OpenSSL修复高危漏洞Heartbleed 背景:          OpenSSL全称为Secure Socket Layer.是Netscape所研发.利用数据加密(Encryption) ...

  2. A20 烧录和启动 log

    用 LiveSuit 烧写了一个 lubuntu 的映像文件到板子上, 同时接了串口观察烧录过程的串口打印信息, 如下 ES: FES:Fes Ver: 098 FES:=============== ...

  3. bootstrap-table自己配置

    function initTable(){ var methodNameSearch=$("#methodNameSearch").val(); var requestUrl =  ...

  4. 程序基石系列之C++多态的前提条件

    准备知识 C++中多态(polymorphism)有下面三个前提条件: 必须存在一个继承体系结构. 继承体系结构中的一些类必须具有同名的virtual成员函数(virtualkeyword) 至少有一 ...

  5. 一堂C++课玩转rpm包的制作

    常见的Linux发行版主要可以分为两类,类ReadHat系列和类Debian系列,这里我们是以其软件包的格式来划分的,这两类系统分别提供了自己的软件包管理系统和相应的工具.类RedHat系统中软件包的 ...

  6. 【BZOJ3193】[JLOI2013]地形生成 DP

    [BZOJ3193][JLOI2013]地形生成 Description 最近IK正在做关于地形建模的工作.其中一个工作阶段就是把一些山排列成一行.每座山都有各不相同的标号和高度.为了遵从一些设计上的 ...

  7. 【BZOJ1110】[POI2007]砝码Odw 贪心

    [BZOJ1110][POI2007]砝码Odw Description 在byteotian公司搬家的时候,他们发现他们的大量的精密砝码的搬运是一件恼人的工作.公司有一些固定容量的容器可以装这些砝码 ...

  8. ffmpeg ffplay播放延时大问题:播放延时参数设置

    使用ffplay播放视频源时,rtsp/rtmp等,会有一定的延时,这里我们可以通过设置ffplay播放参数将延时控制到最小. ffplay.exe -i rtmp://xxxxxxx -fflags ...

  9. java编程之JDBC

    JDBC的常用类和接口 1.       DriverManager类 管理数据库中的所有驱动程序,其所有的方法都是静态方法,调用时无需实例化,通过类名就可以直接调用. 2.       Connec ...

  10. 九度OJ 1141:Financial Management (财务管理) (平均数)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:939 解决:489 题目描述: Larry graduated this year and finally has a job. He's ...