转自:

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. Hnu 11187 Emoticons :-) (ac自己主动机+贪心)

    题目大意: 破坏文本串.使之没有没有出现表情.破坏就是用空格替换.问最少须要破坏多少个字符. 思路分析: 初看跟Hdu 2457 没什么差别,事实上Hdu2457是要求将字符替换成ACGT,而这个仅仅 ...

  2. HDU 4923 Room and Moor(推理+栈维护)

    HDU 4924 Room and Moor 题目链接 题意:给定一个01组成的a序列.要求一个b序列,b序列每一个数值为[0, 1]之间的数,而且b序列为非递减序列,要求∑(ai−bi)2最小,求这 ...

  3. python--网络编程--socket

    网络通信标准---网络协议 互联网协议--osi七层协议 五层协议:应用层:应用层.表示层.会话层          传输层:传输层    网络层:网络层    数据链路层:数据链路层    物理层: ...

  4. SAP-ABAP系列 第一篇SAP简介

    第一篇 SAP简介 SAP全名为System Application and Products in Data Processing.SAP目前是全世界排名第一的RP软件,号称“全球最大的企业管理解决 ...

  5. SQL还有多少"理所当然";还有那些"就是这样"

    前言废话——sql是程序员的饭碗,繁琐but万能,但能干并不意味着适合干,每当多表关联寻找外键时,我都在经历一种没有选择的痛苦.sql不完美,但长期代码让人无暇顾及完美,再痛苦的呐喊到最后都归于疲倦已 ...

  6. iframe仿ajax图片上传

    1.前台页面: iframe_upload.html <html> <body> <form action="upload.php" id=" ...

  7. TTimer源码研究

    TTimerProc = procedure of object; IFMXTimerService = interface(IInterface) ['{856E938B-FF7B-4E13-85D ...

  8. 基于地理位置信息的traceroute

    我们在机房选择.測试网络的质量的时候,往往仅仅依据跳数.延迟.抖动.网络吞吐量等指标来衡量,非常多时候跳数并不能全然显示网络拓扑优劣,于是写了个traceroute结合whois的小脚本来直观显示每一 ...

  9. 【Effective C++】让自己习惯C++

    条款01:视C++为一个语言联绑 C++的四个语言层次: C:C++是以C为基础的.基本数据类型.语句.预处理器.数组.指针等统统来自C. Oject-Oriented C++:面向对象这一特性包含了 ...

  10. Linux-VMware三种网络模式

    虚拟机网络模式 对于VMware虚拟软件来说,有三种网络模式 1.桥接 2.NAT 3.Host-only 桥接 桥接网络是指本地物理网卡和虚拟网卡通过VMnet0虚拟交换机进行桥接,因此物理网卡和虚 ...