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
与重力和反弹有关的内容只是非常糟糕的近似值,不应用于任何物理类型的运动