原本计划06章是一个碰撞检测的小demo,上手之后才发现,碰撞反馈也是一个非常复杂的话题,所以就单拎出来一章,详细说明。碰撞反馈是基于碰撞检测的结果,将发生接触的物体分离开,同时应用上物理效果,使碰撞表现的接近于自然情况。

本文作者游蓝海。原创不易,未经许可,禁止任何形式的转载。

1. 刚体运动

1.1 刚体的概念

刚体,英文称作Rigidbody,是物理世界中的一个基本运动单元,包含了位移、旋转、质量、速度、力等物理属性。在物理引擎中,通常会把刚体看做是一个不可见的点,通过附加上不同的形状,才能与其他物体发生交互。从代码设计的角度而言,就是一种组合模式:一个刚体,包含了若干个形状。

public class Rigidbody
{/// 位移public Vector2 position;/// 移动速度public Vector2 velocity;/// 质量public float mass;/// 质量的倒数。方便除法计算,对于静态物体,可以认为质量无限大,invMass=0,所有作用力相乘结果都是0。public float invMass;/// 持续作用力。持续影响移动速度public Vector2 force;/// 脉冲力。仅影响移动速度一次public Vector2 forceImpulse;/// 旋转角度public float rotation;/// 角速度。单位是度,计算作用力的时候,切记要转换成弧度。public float angleVelocity;/// 角动量。相当于旋转质量public float inertial;/// 角动量倒数。方便除法计算public float invInertial;/// 扭矩力。持续的影响旋转速度public float torque;/// 扭矩脉冲力。仅影响旋转速度一次public float torqueImpulse;/// 摩擦力public float fraction = 0.3f;/// 形状集合public List<Shape> shapes;
}

1.2 形状的概念

形状(Shape),在一些物理引擎里也叫碰撞体(Collider),描述了物体的轮廓信息。常用的2D形状有圆形、线段、多边形,三角形和矩形也可以归为多边形。

1.3 牛顿三大定律

详细内容可以参考百科-牛顿运动定律,这里仅简单回顾一下。

  1. 匀速运动。S = S0 + v * t
  2. 加速度。a = F / m; v = v0 + a * t
  3. 力是相互的。F' = -F

1.3 线性运动

主要影响物体的位移和位移的速度。

velocity += force * invMass * dt;
position += velocity * dt;

1.4 旋转运动

主要影响物体的角度和旋转速度。

angleVelocity += torque * invInertial * dt;
rotation += angleVelocity * dt;

2. 碰撞反馈

2.1 碰撞接触信息

两个形状是否发生碰撞,可以使用GJK算法进行检测。当检测到碰撞后,再使用EPA算法,计算穿透方向和穿透深度。至于碰撞接触点,简单起见,可以使用求最近距离的方式,从闵可夫斯基差集多边形的边上计算得到最近点。

if (!isCollision)
{computeClosetPoint(simplex.getSupport(0), simplex.getSupport(1));
}
else
{queryEPA();computeClosetPoint(currentEpaEdge.a, currentEpaEdge.b);
}
...
void getContacts(List<ContactInfo> list)
{Edge e = gjk.currentEpaEdge;ContactInfo a = new ContactInfo{point = gjk.closestOnA, // 接触点normal = e.normal, // 穿透方向penetration = e.distance, // 穿透深度};list.Clear();list.Add(a);}

2.2 反作用力

两个物体发生碰撞后,会产生反作用力将两个物体分离开。作用力和反作用力是相互的,两者大小相等,方向相反。碰撞分离的关键点,就是如何求得作用力,然后改变物体的运动速度和旋转角度。

已知两个物体碰撞后在某时刻的速度和质量,可以根据动量定律,求得该时刻的动量dF。以下公式是box2d中采用的动量计算公式:

ΔF⃗=mΔv⃗Δv⃗=v⃗1−v⃗2+u⃗1−u⃗2u⃗=r⃗twm=1kk=1m1+1m2+1I1q1+1I2q2qn=r2−∣r⃗⋅n⃗∣2qt=r2−∣r⃗⋅t⃗∣2\begin{aligned} & \Delta \vec F = m \Delta \vec v \\ & \Delta \vec v = \vec v_1 - \vec v_2 + \vec u_1 - \vec u_2 \\ & \vec u = \vec r_t \, w \\ & m = \frac{1}{k} \\ & k = \frac{1}{m_1} + \frac{1}{m_2} + \frac{1}{I_1} q_1 + \frac{1}{I_2} q_2 \\ & q_n = r^2 - |\vec r \cdot \vec n|^2 \\ & q_t = r^2 - |\vec r \cdot \vec t|^2 \end{aligned} ​ΔF=mΔvΔv=v1​−v2​+u1​−u2​u=rt​wm=k1​k=m1​1​+m2​1​+I1​1​q1​+I2​1​q2​qn​=r2−∣r⋅n∣2qt​=r2−∣r⋅t∣2​

  • dv是该时刻两个物体上的碰撞点的相对速度,包含线性移动速度v和旋转速度u之和。
  • u是碰撞点的旋转速度,而不是刚体的旋转速度。点的旋转速度为点在单位时间沿着圆周转过的弧长,设点到中心的半径为r,刚体转速为w弧度/秒,则单位时间转过的弧长为: u = r * w。而旋转方向,就是点到中心向量的切线方向;
  • m是该时刻的有效质量。这个我还没有弄清楚,如果有懂的朋友,不妨留言说明一下;
  • q是点的旋转惯性。也是没有理解。注意,旋转惯性在法线方向和切线方向不一样,需要分开计算。

为了方便计算,沿着穿透向量方向,可以将作用力分解为平行和垂直的两个分量,分别记作法线方向n和切线方向t。法线方向主要体现为反弹力,切线方向主要体现为摩擦力。

ΔFn=max⁡(mnΔvn,0)Δvn=Δv⃗⋅n⃗ΔFt=clamp(mtΔvt,−μΔFn,μΔFn)Δvt=Δv⃗⋅t⃗\begin{aligned} & \Delta F_n = \max(m_n \Delta v_n, 0) \\ & \Delta v_n = \Delta \vec v \cdot \vec n \\ & \Delta F_t = clamp(m_t \Delta v_t, -\mu \Delta F_n, \mu \Delta F_n) \\ & \Delta v_t = \Delta \vec v \cdot \vec t \\ \end{aligned} \\ ​ΔFn​=max(mn​Δvn​,0)Δvn​=Δv⋅nΔFt​=clamp(mt​Δvt​,−μΔFn​,μΔFn​)Δvt​=Δv⋅t​

作用力Fn初始的时候是指向穿透方向的,也就是正数,反作用力是-Fn。碰撞的过程中受力是连续发生变化的,作用力会由大变小,直到变为0。发起碰撞的刚体,其速度会逐步减小至0,然后开始反向运动,当反作用力为0的时候,其速度也就达到了最大值。整个碰撞过程中,Fn的值是需要限定到0以上的,否则又会把物体拉回来。

作用力Ft会让物体侧滑,而滑动就要受到摩擦力的影响。u = 1 - fraction,当摩擦系数fraction=0的时候,侧滑作用力达到最大;当摩擦系数fraction = 1的时候,没有侧滑作用力。

2.3 速度改变

发起碰撞的刚体body1,受到反向作用力;被碰撞的刚体body2,受到正向作用力。

// 法线和切线方向力之和
Vector2 F = normal * Fn + tangent * Ft;
// 刚体中心点到碰撞接触点的向量
Vector2 r1 = point - body1.position;
Vector2 r2 = point - body2.position;body1.velocity += -F / body1.m;
body1.angleVelocity += cross(-F, r1) * body1.angleVelocity / body1.I;body2.velocity += F / body2.m;
body2.angleVelocity += cross(F, r2) * body2.angleVelocity / body2.I;

2.4 重叠分离

当两个物体发生重叠,在没有运动速度,或者速度很小的时候,是无法依靠碰撞反馈来将两个物体分开的。比如,通过代码直接设置了物体坐标,导致物体间发生了重叠。针对这种情况,需要增加一个分离速度,将发生重叠的物体“挤”出去。

设重叠的深度为s,则分离速度为:v = s / dt,在计算碰撞作用力的时候,叠加上这样一个偏移速度,就可以产生一个挤出力。但是,当时间dt很小的时候,这个分离速度将会变的很大,看起来很不真实。可以通过给公式增加一些系数,来灵活控制挤出速度。
ΔFn=max⁡(mn(Δvn+vb),0)vb=βmax⁡(0,s−δ)Δt,(β是缩放系数,δ是可容忍的穿透深度)\Delta F_n = \max(m_n(\Delta v_n + v_b), 0) \\ v_b = \frac{ \beta \max(0, s - \delta)}{\Delta t}, \\ (\beta是缩放系数,\delta是可容忍的穿透深度) ΔFn​=max(mn​(Δvn​+vb​),0)vb​=Δtβmax(0,s−δ)​,(β是缩放系数,δ是可容忍的穿透深度)

3. 范例

本章Demo使用Unity3D引擎开发,Demo工程已上传github: https://github.com/youlanhai/learn-physics/tree/master/Assets/06-seperation

4. 参考

  1. Erin Catto, GDC 2006 Physics Tutorial
  2. Erin Catto, How Do Physics Engines Work
  3. box2d-lite 轻量级的物理引擎
  4. box2d物理引擎文档
  5. chipmunk物理引擎文档

本系列文章会和我的个人公众号同步更新,感兴趣的朋友可以关注下我的公众号:游戏引擎学习。扫下面的二维码加关注:

物理引擎学习06-碰撞反馈相关推荐

  1. 物理引擎学习07-小游戏飞机大战

    到目前为止,碰撞检测的基本内容(狭义的碰撞检测)已经讲完了.广义的碰撞检测,我们到下一阶段再继续.本小节,在上节"碰撞反馈"的基础之上,扩展支持多物体间的碰撞检测.使用最简单的方法 ...

  2. 物理引擎学习08-AABB树

    AABB树是由AABB包围盒结点构成的二叉树,常用来加速场景中的射线检测和碰撞检测.树的每个结点都是一个包围盒,且结点的包围盒包裹了所有子结点的包围盒.本文深入的讲解了AABB树相关的算法,以及结合物 ...

  3. Chipmunk-js物理引擎学习笔记

    一.基本概念 空间:在Chipmunk中,空间是所有对象容器.因此,刚体.形状.链接节点等对象都需要添加到空间中.空间控制这些对象的相互作用. 刚体:物理上的刚体指的是在运动和受力作用后,形状和大小不 ...

  4. 物理引擎学习03-GJK碰撞检测算法基础

    GJK是由Gilbert,Johnson,Keerthi 三位前辈发明的,用来计算两个凸多面体之间的碰撞检测,以及最近距离.GJK算法可以在O(M+N)的时间复杂度内,检测出碰撞,算法在每次迭代的过程 ...

  5. 物理引擎学习05-GJK和EPA计算穿透向量

    EPA,是扩展多边形算法(Epanding Polytop Algorithm) ,用来计算两个多边形碰撞的穿透深度和方向,可用于将两个发生碰撞的多边形分离.本文的写作目的,主要是对GJK和EPA算法 ...

  6. 物理引擎学习04-GJK计算多边形之间的最近距离

    计算多边形之间的最近距离,才是GJK算法原本的目的.只有两个多边形不相交,计算最近距离才有效.如果相交,则最近距离无效,但是可以使用EPA算法要计算碰撞深度.本文的写作目的,主要是对GJK算法的理解和 ...

  7. [Unity 3D] 物理引擎学习笔记(一)

    刚体: 同是物理引擎提供的功能,碰撞检测只需要有 Collider 便可以运作,但所有与作用力相关的属性和函数却都依赖 Rigidbody. 重力: 一旦使用了 Rigidbody 组件,这个 Gam ...

  8. 物理引擎探究(8)---碰撞处理

    0.简介 碰撞处理就是将碰撞得到的信息进行处理后生成各种作用力,这个也算是一个单独的模块. 1.碰撞生成信息 根据碰撞的的结果,来判断产生力的大小,方向,种类等信息,然后将力作用在物体上,目前我实现了 ...

  9. 基于JBox2D物理引擎开发的“雷电”小游戏(五)——碰撞

    不好意思,隔了这么久才发这一篇文章,虽然部分原因是最近因为实习的事情很忙,还有一部分原因是比较懒,废话不多说,现在开始正文. 碰撞 既然学到了这里,想必大家都明白,物理引擎会帮我们做很多事情,省去从零 ...

最新文章

  1. 商汤科技汤晓鸥:其实不存在AI行业,唯一存在的是“AI+“行业
  2. 0. VS2015快捷键
  3. 漫画科普 | 5G到底是个什么玩意儿?
  4. 不定积分24个基本公式_高分攻略丨语文阅读理解的24个万能答题公式
  5. could not find curses mysql_​-- Configuring incomplete-- Could NOT find Curses
  6. 如何使用vs将asp.net core项目添加容器支持并发布docker镜像到私有dockerhub和添加k8s/helm管理...
  7. 搜索算法(二)--DFS/BFS求解炸弹人问题(JAVA )
  8. 安卓学习笔记38:利用OpenGL ES绘制旋转立方体
  9. DXUT框架剖析(10)
  10. 迭代器模式C++实现
  11. 2017.10.26-构建之法:现代软件工程-阅读笔记
  12. windows 2003 server安装iis6,附下载文件
  13. 脚本精灵服务器引擎数据为空,脚本精灵服务器
  14. HTML5+CSS大作业 网页制作代码_大学生网页制作作业代码——年会抽奖网页设计(1页)
  15. java md5加密与解密_Java——MD5加密与解密
  16. 微信打不开MP4文件 (记录编码问题)
  17. 关于获得淘宝商品评论的那些事
  18. 嵌入式linux开发,.mak是什么文件
  19. 方差、标准差、均方误差
  20. 【FME】模板模块化组织思路

热门文章

  1. 安全绳使用方法图解_安全带和安全绳的正确使用方法
  2. mysql 多表查询语句
  3. 推荐一个rss源搜索引擎
  4. 都掌握了吗?Kotlin技能小成
  5. 联想笔记本连不上手机热点_笔记本找不到手机热点怎么办_电脑搜索不到手机热点的解决方法-系统城...
  6. centos7 ceph 集群部署
  7. 高职计算机自主招生面试题,高职自主招生面试题
  8. MAC OS 上的app重新签名与打包
  9. cmd命令行切换目录路径
  10. echarts中的地图与Axure交互