转自:

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. initializer_list、初始化列表、列表初始化

    什么是列表初始化 使用一个花括号来初始化变量,表现形式如下: std::vector<int>a{1,2,3,4,5}; 或者 std::vector<int>a = {1,2 ...

  2. kubernetes对象之cronjob

    系列目录 类似于Linux的Cron模块,CronJob用来运行定时性任务,或者周期性.重复性任务.注意CronJob启动的是kubernetes中的Job,不是ReplicaSet.DaemonSe ...

  3. windows下的txt格式转换成linux下的TXT

    存在的问题是 多出一个方框或者黑格子 主要是因为bash 不能忽略windows的问题 用sed 命令来处理,分别是windows转linux,linux转windows sed -e 's/.$// ...

  4. OpenCV 入门示例之一:显示图像

    前言 本文展示一个显示图像的示例程序,它用于从硬盘加载一副图像并在屏幕上显示. 代码示例 // 此头文件包含图像IO函数的声明 #include "highgui.h" int m ...

  5. 在OC项目中实现swift与oc混编 相互引用

    --------------------------------------------------------Begin--------------------------------------- ...

  6. EasyPusher手机直播推送是如何实现后台直播推送的

    本文由EasyDarwin开源团队成员John提供:http://blog.csdn.net/jyt0551/article/details/52276062 EasyPusher Android是使 ...

  7. EasyDarwin开发的短视频拍摄、录制开源项目EasyVideoRecorder

    在前面的博客<EasyDarwin开发出类似于美拍.秒拍的短视频拍摄SDK:EasyVideoRecorder>和<美拍.秒拍中安卓.IOS短视频拍摄的一些关键技术>中我们简单 ...

  8. TControl,TWinControl和TGraphicControl的显示函数

    -------------------------- 显示隐藏刷新 -------------------------- TControl = class(TComponent)procedure S ...

  9. mongodb学习之:条件操作符

    在前面的章节中我们已经有用到了条件操作符,这里我们再重点介绍下.MongoDB中条件操作符有: (>) 大于 - $gt (<) 小于 - $lt (>=) 大于等于 - $gte ...

  10. AbstractFactory Pattern

    AbstractFactory模式用来解决这类问题:要创建一组相关或者相互依赖的对象. AbstractFactory Pattern结构图 实现: #ifndef _PRODUCT_H_ #defi ...