一个不靠谱的2D物理引擎(2) - 分离被碰撞物体与求碰撞冲量

如果已经知道了两个相互碰撞的物体进行碰撞前的 线速度, 角速度, 质心坐标, 被碰撞点的坐标, 碰撞方向, 质量, 转动惯量, 恢复系数, 那么可以用以下公式求得两个物体对对方造成的碰撞冲量大小:

J = -vr (e + 1) / ( 1/m1 + 1/m2 + n * ((r1 × n) / I1) × r1 + n * ((r2 × n) / I2) × r2 )

其中:

m1与m2是两物体的质量

I1与I2是两物体的转动惯量

e是恢复系数

r1r2是从物体质心指向被碰撞点的向量, 也就是力臂

vr = v1‘ - v2‘

v1‘ = v1 + r1×ω1, 即把角速度转成线速度也加到速度上来进行计算

n是两物体的碰撞方向向量, 上一篇有提到如何求

求得J之后, 将冲量分别施加在两物体上可以得到物体被碰撞后的线速度和角速度:

v1+ = v1- + (Jn) / m1

ω1+ω1- + (r1 × Jn) / I1

v2+ = v2- + (- Jn) / m2

ω2+ = ω2- + (r2 × - Jn) / I2

上面的公式可以在《游戏开发物理学》的103页找到, 有写如何推导. 也可以看这里的最后一部分.

applyImpulse: function (oImpulse, oPosition) {
                this.velocity = this.velocity.add(oImpulse.mul(this.inverseMass));
                this.angularVelocity += oPosition.sub(this.centroid).cross(oImpulse) * this.inverseInertia;
            }
applyCollisionImpulse: function (bodyA, bodyB, contactPoint, normal, e) {
                var cp = contactPoint,
                    rA = cp.sub(bodyA.centroid),
                    rB = cp.sub(bodyB.centroid),
                    vA = bodyA.velocity.add(rA.scalarCross(bodyA.angularVelocity)),
                    vB = bodyB.velocity.add(rB.scalarCross(bodyB.angularVelocity)),
                    invIA = bodyA.inverseInertia,
                    invIB = bodyB.inverseInertia,
                    invMA = bodyA.inverseMass,
                    invMB = bodyB.inverseMass,
                    vr = vA.sub(vB),
                    rsnA = rA.cross(normal),
                    rsnB = rB.cross(normal),
                    kn = invMA + invMB + rsnA * rsnA * invIA + rsnB * rsnB * invIB,
                    jn = -(1 + e) * vr.dot(normal) / kn,
                    impulse = normal.mul(jn);

                    bodyA.applyImpulse(impulse, cp);
                    bodyB.applyImpulse(impulse.negate(), cp);
                }

2D向量与标量叉乘可以这样算:

scalarCross: function (num) {
                return new Vector2D(-num * this.y, num * this.x);
            }

如果希望添加摩擦力, 可以这样修改:

applyCollisionImpulse: function (bodyA, bodyB, cp, normal, e, friction) {
                var rA = cp.sub(bodyA.centroid),
                    rB = cp.sub(bodyB.centroid),
                    vA = bodyA.velocity.add(rA.scalarCross(bodyA.angularVelocity)),
                    vB = bodyB.velocity.add(rB.scalarCross(bodyB.angularVelocity)),
                    invIA = bodyA.inverseInertia,
                    invIB = bodyB.inverseInertia,
                    invMA = bodyA.inverseMass,
                    invMB = bodyB.inverseMass,
                    vr = vA.sub(vB),
                    rsnA = rA.cross(normal),
                    rsnB = rB.cross(normal),
                    kn = invMA + invMB + rsnA * rsnA * invIA + rsnB * rsnB * invIB,
                    jn = -(1 + e) * vr.dot(normal) / kn,
                    impulse = normal.mul(jn);

                    bodyA.applyImpulse(impulse, cp);
                    bodyB.applyImpulse(impulse.negate(), cp);

                    vA = bodyA.velocity.add(rA.scalarCross(bodyA.angularVelocity));
                    vB = bodyB.velocity.add(rB.scalarCross(bodyB.angularVelocity));
                    vr = vA.sub(vB);
                    var tangent = vr.sub(normal.mul(vr.dot(normal))).normalize(),
                        rstA = rA.cross(tangent),
                        rstB = rB.cross(tangent),
                        kt = invMA + invMB + rstA * rstA * invIA + rstB * rstB * invIB,
                        jf = Math.min(vr.dot(tangent) / kt, Math.abs(jn * friction)),
                        tangentImpulse = tangent.mul(jf);

                    bodyA.applyImpulse(tangentImpulse.negate(), cp);
                    bodyB.applyImpulse(tangentImpulse, cp);
                }

其中jf = Math.min(vr.dot(tangent) / kt, Math.abs(jn * friction))中的jn * friction就是 μ * 压力冲量, vr.dot(tangent) / kt 是使得摩擦力恰好使切线方向速度变成0的冲量大小, 因为摩擦力会在相对速度为0时变成0, 不如此计算最大摩擦冲量的话施加摩擦冲量后可能会导致切向方向物体反向加速.

仅靠碰撞冲量实际无法完全分离两个物体, 因为物体可以相互嵌入而不是恰好接触. 已知两物体的相互嵌入距离的话可以用下面的方法修正物体的位置:

correctPosition: function (bodyA, bodyB, penetration) {
                var maxPenetration = 0.05,
                    correctPercent = 0.4,
                    correction,
                    tA,
                    tB;
                if (penetration <= maxPenetration) {
                    return;
                }
                correction = this.normal.mul((penetration - maxPenetration) / (bodyA.inverseMass + bodyB.inverseMass)
                    * correctPercent);
                tA = correction.negate().mul(bodyA.inverseMass);
                tB = correction.mul(bodyB.inverseMass);
                bodyA.translate(tA.x, tA.y);
                bodyB.translate(tB.x, tB.y);
                return;
            }

这里有简单解释这个函数是如何工作的.

时间: 2024-10-05 08:46:33

一个不靠谱的2D物理引擎(2) - 分离被碰撞物体与求碰撞冲量的相关文章

一个不靠谱的2D物理引擎(4) - 裁剪出碰撞点(manifold)

主要参考: http://www.codezealot.org/archives/394 , 建议阅读 第一篇已经解决了如何判断两个图形是否相交以及求嵌入深度. 之后还需要求两物体的接触点. 实际上物体的接触部分可以是一个面, 叫做manifold. 2D下就是一条线段. 对于两个多边形的碰撞, 首先要找出两个多边形相互离得最近的两条边. 在第一篇的修改版的getAxisLeastPenetration函数已经完成了这个工作. 把polyA和polyB相互交换作为该函数的参数执行2次, 取dis

一个不靠谱的2D物理引擎(5) - 约束(Constraints)

参考: http://blog.sina.com.cn/s/blog_61feffe10100msbz.html, http://allenchou.net/2013/12/game-physics-constraints-sequential-impulse/ 约束就是限制物体的运动方式, 可以用来处理碰撞, 模拟关节等的东西. 例如碰撞就是两个物体最小距离至少是0, 关节是两个物体被一颗钉子钉在一起只能绕这个钉子旋转, 弹簧是两个物体的距离要保持在某一范围内. 对于一个3D刚体有6个自由度,

制作简单的2D物理引擎(零)

最近发现了Github上的开源物理引擎项目Matter.js,对它很感兴趣,发现源码并不算长,算上注释大约1万行左右,值得剖析一番.Matter.js实现一个最小化的2D物理引擎,性能不错,故打算用C#重写并学习之. 由于JS是弱类型,而C#是强类型的,所以不得不还原相应的类型.在重写过程中,我也发现了源码中的一些问题,以及代码冗余,不过都无关紧要.在一万行之内实现一个简单的物理引擎本来就很令人激动了,这样可以以最小的工作量来熟悉物理引擎. 重写过程中,渲染用自带GDI实现,所以只需考虑物理引擎

Matter.js – 你不能错过的 2D 物理引擎

Matter.js 是一个 JavaScript 2D 刚体物理引擎的网页.Matter.Engine 模块包含用于创建和操作引擎的方法.这个引擎是一个管理更新和渲染世界的模拟控制器. Matter.js 目前是测试版本,这意味着 API 仍在开发中,可能偶尔会发生变化. 在线演示      源码下载 您可能感兴趣的相关文章 网站开发中很有用的 jQuery 效果[附源码] 分享35个让人惊讶的 CSS3 动画效果演示 十分惊艳的8个 HTML5 & JavaScript 特效 Web 开发中很

2D物理引擎--开坑篇

你们还记力学和向量吗?有了这两宝就可以写物理引擎了诶~给读书无用论一个漂亮的耳光. 最近闲来无事,一拍脑门,不如写一个 2D 的物理引擎吧.自己刚好会一门图形编程的框架– QtQuick ,于是决定使用 QtQuick+JavaScript 写一个简单的 2D 物理引擎,支持圆形的碰撞检测与碰撞反应. 虽然网络上有提供一些文章介绍怎么做一个简单的 2D 物理引擎, 例如: 为 JavaScript 游戏构建一个简单的 2D 物理引擎 但是为了构造一个简单的 2D 物理引擎,至少需要具备如下知识:

HTML5之2D物理引擎 Box2D for javascript Games 系列 翻外篇--如何结合createJS应用box2d.js

太久没有更新了,新年回来工作,突然有收到网友的邮件提问,居然还有人在关注,惭愧,找了下电脑上还有一点儿存着,顺便先发这一个番外篇吧,好歹可以看到真实的效果,等我考完英语,一定会更新下一章,"愤怒的小鸟篇" 此篇,并不是书中的篇符,而是通过希望通过结合实际的canvas 绘图库实现box2d物理引擎在各绘图库上应用,绘图库网上有很多现成的 如:createjs, pixi.js 等,Egret或者其它游戏引擎有自己的物理引擎扩展库,所以就不说了. 现在通过之前的学习,基本掌握了刚体等基础

假如BOSS给了PM一个不靠谱的需求

假如BOSS给了一个PM不靠谱的需求,那么站在PM的角度上,我会怎么处理呢?在知乎上看了好多达人以及同行的真知灼见,自己也是有一番小的思考,这里做一个总结吧 其实,这种不靠谱的产生的最主要的原因无非是所处的位置和立场不同,因而会发出不同的声音. 作为一个BOSS,更可能是从当前整个市场的行情,走向去看问题,觉得我们应该做一个XXX产品,实现XXX需求,然后达到XXX目标. 作为一个PM,更多的是考虑一个产品满足这种需求所需要的成本,实现细节以及"性价比",如果PM再厉害点的话,可能技术

HTML5之2D物理引擎 Box2D for javascript Games 系列 第二部分

这是系列第二部分,之前部分在本博客中找 源码demo存放在https://github.com/willian12345/Box2D-for-Javascript-Games 向世界添加刚体 刚体(Bodies)是我们用Box2D创建物理游戏的重要对象.任何你可以移动的或交互 的对象都是刚体(Bodies). 愤怒的小鸟(Angry Birds)中创建的小鸟和小猪是刚 体,同样在图腾破坏者(Totem Destroyer)中的黄金神像和图腾砖块也是刚体. 本章将带你学习创建各种类型的Box2D刚

Cocos2d-js官方完整项目教程翻译:六、添加Chipmunk物理引擎在我们的游戏世界里

添加Chipmunk物理引擎在我们的游戏世界里         一.简介                   cocos2d JS能给我们力量来创造令人印象深刻的游戏世界.但缺乏某种现实.          虽然我们可以做复杂的计算,使游戏世界更真实的,但有另一个选择          它可以缓解我们的生活.答案是物理引擎.          物理引擎提供了重力,碰撞检测和物理模拟,可以使我们的游戏世界看起来更真实.          在本教程中,我们将介绍的ChipMunk的物理引擎进入我们的