Javascript 计算碰撞后的角速度
我已经得到了碰撞分辨率的线性分量,但是我不知道如何对角度分量做同样的处理。从我所读到的,它有点像<代码>扭矩=Javascript 计算碰撞后的角速度,javascript,html,canvas,game-physics,Javascript,Html,Canvas,Game Physics,我已经得到了碰撞分辨率的线性分量,但是我不知道如何对角度分量做同样的处理。从我所读到的,它有点像扭矩=碰撞点x线速度。(叉积)我试图在我的代码中加入一个,但实际上,当对象碰撞时,我根本看不到任何旋转。另一把小提琴完美地完成了分离轴定理和角速度计算的初步实现。这是我想到的 特性定义(方向、角速度和角加速度): 计算碰撞响应中的角速度: var pivotA = this.vector(bodyA.x, bodyA.y); bodyA.angularVelocity = 1 * 0.2 * (bod
碰撞点
x线速度
。(叉积)我试图在我的代码中加入一个,但实际上,当对象碰撞时,我根本看不到任何旋转。另一把小提琴完美地完成了分离轴定理和角速度计算的初步实现。这是我想到的
特性定义(方向、角速度和角加速度):
计算碰撞响应中的角速度:
var pivotA = this.vector(bodyA.x, bodyA.y);
bodyA.angularVelocity = 1 * 0.2 * (bodyA.angularVelocity / Math.abs(bodyA.angularVelocity)) * pivotA.subtract(isCircle ? pivotA.add(bodyA.radius) : {
x: pivotA.x + boundsA.width,
y: pivotA.y + boundsA.height
}).vCross(bodyA.velocity);
var pivotB = this.vector(bodyB.x, bodyB.y);
bodyB.angularVelocity = 1 * 0.2 * (bodyB.angularVelocity / Math.abs(bodyB.angularVelocity)) * pivotB.subtract(isCircle ? pivotB.add(bodyB.radius) : {
x: pivotB.x + boundsB.width,
y: pivotB.y + boundsB.height
}).vCross(bodyB.velocity);
_bodyA.angularVelocity = direction.vCross(_bodyA.velocity) / (isCircle ? _bodyA.radius : boundsA.width);
_bodyB.angularVelocity = direction.vCross(_bodyB.velocity) / (isCircle ? _bodyB.radius : boundsB.width);
更新更新循环中的方向:
var torque = 0;
torque += core.objects[o].angularVelocity * -1;
core.objects[o].angularAcceleration = torque / core.objects[o].momentOfInertia();
core.objects[o].angularVelocity += core.objects[o].angularAcceleration;
core.objects[o].rotation += core.objects[o].angularVelocity;
我会发布我用来计算转动惯量的代码,但是每个物体都有一个单独的代码,所以会有点。。。冗长的。尽管如此,以下是一个圆的例子:
return this.mass * this.radius * this.radius / 2;
为了显示结果,这里是我的。如图所示,对象不会在碰撞时旋转。(圆圈不完全可见,但适用于0和7)
我做错了什么
编辑:它们根本不旋转的原因是因为响应函数中的组出错——它现在旋转,只是不正确。然而,我现在已经评论过了,因为这会把事情搞砸
此外,我还尝试了另一种旋转方法。以下是回复中的代码:
var pivotA = this.vector(bodyA.x, bodyA.y);
bodyA.angularVelocity = 1 * 0.2 * (bodyA.angularVelocity / Math.abs(bodyA.angularVelocity)) * pivotA.subtract(isCircle ? pivotA.add(bodyA.radius) : {
x: pivotA.x + boundsA.width,
y: pivotA.y + boundsA.height
}).vCross(bodyA.velocity);
var pivotB = this.vector(bodyB.x, bodyB.y);
bodyB.angularVelocity = 1 * 0.2 * (bodyB.angularVelocity / Math.abs(bodyB.angularVelocity)) * pivotB.subtract(isCircle ? pivotB.add(bodyB.radius) : {
x: pivotB.x + boundsB.width,
y: pivotB.y + boundsB.height
}).vCross(bodyB.velocity);
_bodyA.angularVelocity = direction.vCross(_bodyA.velocity) / (isCircle ? _bodyA.radius : boundsA.width);
_bodyB.angularVelocity = direction.vCross(_bodyB.velocity) / (isCircle ? _bodyB.radius : boundsB.width);
请注意,方向
指的是“碰撞法线” 力矢量引起的角加速度和线加速度
由作用力产生的角加速度和方向加速度是同一事物的两个组成部分,不能分开。要得到一个,你需要同时解决这两个问题
定义计算
从简单的物理和站在肩膀上,我们知道以下几点
F is force (equivalent to inertia)
Fv is linear force
Fa is angular force
a is acceleration could be linear or rotational depending on where it is used
v is velocity. For angular situations it is the tangential component only
m is mass
r is radius
对于线性力
F = m * v
我们从中得到
m = F / v
v = F / m
m = F / ( r * v )
v = F / ( r * m )
r = F / ( v * m )
对于旋转力(v为切向速度)
我们从中得到
m = F / v
v = F / m
m = F / ( r * v )
v = F / ( r * m )
r = F / ( v * m )
因为我们施加的力是瞬时的,所以我们可以交换a
加速度和v
速度,得到以下所有公式
线性的
F = m * a
m = F / a
a = F / m
旋转的
F = r * m * a
m = F / ( r * a )
a = F / ( r * m )
r = F / ( a * m )
因为我们只对线性解和旋转解的速度变化感兴趣
a1 = F / m
a2 = F / ( r * m )
其中,a1
是以每帧2像素为单位的加速度,a2
是以每帧2弧度为单位的加速度(帧平方表示它是加速度)
从1D到2D
因为这是一个二维的解决方案,上面所有的都是一维的,所以我们需要使用向量。对于这个问题,我使用两种形式的2D向量。具有大小(长度、距离等)和方向的极坐标。具有x和y的笛卡尔函数。向量表示什么取决于它的使用方式
以下函数用作解决方案中的帮助程序。它们是用ES6编写的,因此对于不兼容的浏览器,您必须对它们进行调整,尽管我不会建议您使用它们,因为它们是为了方便而编写的,但它们效率非常低,并且会进行大量冗余计算
将向量从极坐标转换为笛卡尔坐标,并返回新向量
function polarToCart(pVec, retV = {x : 0, y : 0}) {
retV.x = Math.cos(pVec.dir) * pVec.mag;
retV.y = Math.sin(pVec.dir) * pVec.mag;
return retV;
}
function cartToPolar(vec, retV = {dir : 0, mag : 0}) {
retV.dir = Math.atan2(vec.y, vec.x);
retV.mag = Math.hypot(vec.x, vec.y);
return retV;
}
将向量从笛卡尔坐标转换为极坐标,并返回新向量
function polarToCart(pVec, retV = {x : 0, y : 0}) {
retV.x = Math.cos(pVec.dir) * pVec.mag;
retV.y = Math.sin(pVec.dir) * pVec.mag;
return retV;
}
function cartToPolar(vec, retV = {dir : 0, mag : 0}) {
retV.dir = Math.atan2(vec.y, vec.x);
retV.mag = Math.hypot(vec.x, vec.y);
return retV;
}
创建极向量
function polar(mag = 1, dir = 0) {
return validatePolar({dir : dir,mag : mag});
}
将向量创建为笛卡尔坐标
function vector(x = 1, y = 0) {
return {x : x, y : y};
}
如果为True,则arg vec是极坐标形式的向量
function isPolar(vec) {
if (vec.mag !== undefined && vec.dir !== undefined) {return true;}
return false;
}
如果arg vec是笛卡尔形式的向量,则返回true
function isCart(vec) {
if (vec.x !== undefined && vec.y !== undefined) {return true;}
return false;
}
以极坐标形式返回新向量,并确保vec.mag为正
function asPolar(vec){
if(isCart(vec)){ return cartToPolar(vec); }
if(vec.mag < 0){
vec.mag = - vec.mag;
vec.dir += PI;
}
return { dir : vec.dir, mag : vec.mag };
}
计算可能会导致负幅值,尽管这对某些计算是有效的这会导致不正确的矢量(反转)这只是验证极性矢量具有正幅值它不会仅改变矢量的符号和方向
function validatePolar(vec) {
if (isPolar(vec)) {
if (vec.mag < 0) {
vec.mag = - vec.mag;
vec.dir += PI;
}
}
return vec;
}
对物体施加力
现在我们可以重新定义一些术语
F(力)是一个矢量力,大小是力,它有一个方向
var force = polar(100,0); // create a force 100 units to the right (0 radians)
没有施加力的位置,力是没有意义的
位置是一个向量,它只包含x和y的位置
var location = vector(canvas.width/2, canvas.height/2); // defines a point in the middle of the canvas
方向向量保存到位置向量之间的方向和距离
var l1 = vector(canvas.width/2, canvas.height/2); // defines a point in the middle of the canvas
var l2 = vector(100,100);
var direction = asPolar(vector(l2.x - l1.x, l2.y - l1.y)); // get the direction as polar vector
方向
现在有从画布中心到点的方向(100100)和距离
我们需要做的最后一件事是沿着方向向量从力向量中提取分量。将力应用于对象时,力分为两部分,一部分是沿对象中心线施加的力,并添加到对象加速度,另一部分是与对象中心线(切线)呈90度角的力,这是改变旋转的力
要获得这两个分量,可以获得力向量和方向向量之间的方向差,力从该方向应用到对象中心
var force = polar(100,0); // the force
var forceLoc = vector(50,50); // the location the force is applied
var direction2Center = asPolar(vector(box.x - forceLoc.x, box.y - forceLoc.y)); // get the direction as polar vector
var pheta = direction2Center - force.dir; // get the angle between the force and object center
现在你有了这个角度,力就可以用trig分解成旋转分量和线性分量
var F = force.mag; // get the force magnitude
var Fv = Math.cos(pheta) * F; // get the linear force
var Fa = Math.sin(pheta) * F; // get the angular force
现在,力可以转换回线性a=F/m和角度a=F/(m*r)的加速度
然后,将线性力转换回具有指向对象中心方向的向量
var forceV = polar(Fv, direction2Center);
Convert返回笛卡尔坐标系,因此我们可以将其添加到对象deltaX和deltaY中
forceV = asCart(forceV);
并将加速度添加到长方体中
box.dx += forceV.x;
box.dy += forceV.y;
旋转加速度只是一维的,所以只需将其添加到长方体的三角形旋转中
box.dr += accelA;
就这样
向箱子施力的功能
如果附加到长方体,该函数将在长方体的某个位置应用力向量
像这样贴在盒子上
box.applyForce = applyForce; // bind function to the box;
然后可以通过该框调用该函数
box.applyForce(force, locationOfForce);
function applyForce(force, loc){ // force is a vector, loc is a coordinate
var toCenter = asPolar(vector(this.x - loc.x, this.y - loc.y)); // get the vector to the center
var pheta = toCenter.dir - force.dir; // get the angle between the force and the line to center
var Fv = Math.cos(pheta) * force.mag; // Split the force into the velocity force along the line to the center
var Fa = Math.sin(pheta) * force.mag; // and the angular force at the tangent to the line to the center
var accel = asPolar(toCenter); // copy the direction to center
accel.mag = Fv / this.mass; // now use F = m * a in the form a = F/m to get acceleration
var deltaV = asCart(accel); // convert acceleration to cartesian
this.dx += deltaV.x // update the box delta V
this.dy += deltaV.y //
var accelA = Fa / (toCenter.mag * this.mass); // for the angular component get the rotation
// acceleration from F=m*a*r in the
// form a = F/(m*r)
this.dr += accelA;// now add that to the box delta r
}
演示
该演示仅涉及函数applyForce
与重力和反弹有关的内容只是非常糟糕的近似值,不应用于任何物理类型的运动