转自:

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. LNMPA遇到504 Gateway time-out错误的解决方法

    Nginx的特点是处理静态很给力,Apache的特点是处理动态很稳定,两者结合起来便是LNMPA,nginx处理前端,apache处理后端,这样处理静态会很快,处理动态会很稳定. 当我以为安装完成以后 ...

  2. 自己定义UITextField

    目的是实现例如以下的效果: UITextField的leftView是自己定义的UIView,当中: 1.包括一个居中显示的icon.而且上,左,下各有1px的间隙 2.左上和左下是圆角,右边没有圆角 ...

  3. Linux - D-Bus

    http://en.wikipedia.org/wiki/D-Bus D-Bus is a free and open-source inter-process communication (IPC) ...

  4. ubuntu 16.04 更新 gcc/g++ 4.9.2

    ubuntu 转载 2016年10月12日 :: 标签:ubuntu /g++ /gcc [html] view plain copy sudo dpkg -l g++ 最近在学C++primer , ...

  5. Linux系统目录数和文件数限制

    对于系统管理员来说,了解系统的一些限制是非常有必要的,这样可以根据需要进行必要的参数配置和调整,进而实现更优的性能,对于系统设计人员甚至程序员来说,了解系统的一些限制,也会有助于设计更为合理的存储结构 ...

  6. dsoframer注冊说明及在VC2010使用

    一.dsoframer在XP.win7和win8中的注冊方法. 从微软站点下载DsoFrmaer_KB311765_x86.exe,双击解开后得到的dsoframer.ocx等文件. (一)XP注冊 ...

  7. LeetCode(125)题解--Valid Palindrome

    https://leetcode.com/problems/valid-palindrome/ 题目: Given a string, determine if it is a palindrome, ...

  8. poi 处理excel 小数问题 整数不保留小数位 整数多.0

    读取的单元格为 hssfCell ,传入下面我提供的方法处理默认poi返回的为DOUBLE,所有先转为Long判断下,再进行返回: private String getValue(Cell hssfC ...

  9. CenterOS下搭建Hadoop环境

    检查防火墙状态 service iptables status 关闭防火墙 service iptables stop 查看防火墙开机启动状态 chkconfig iptables --list 关闭 ...

  10. Protocol_OSPF

    Protocol_OSPF 作者:Danbo 2015-7-4 从一个非常概括的角度来看OSPF协议的操作是比较容易理解的 1.宣告OSPF的路由器从所有启动OSPF协议的接口上发出Hello数据包. ...