到目前为止,碰撞检测的基本内容(狭义的碰撞检测)已经讲完了。广义的碰撞检测,我们到下一阶段再继续。本小节,在上节“碰撞反馈”的基础之上,扩展支持多物体间的碰撞检测。使用最简单的方法来检测哪些物体间会发生碰撞。算法每次遍历所有的物体,两两之间进行碰撞检测测试。所以时间复杂度是 O ( N 2 ) O(N^2) O(N2)。
算法伪代码:
for i = 1 to n - 1 do
for k = i + 1 to n do
检测物体 i 和物体 k 是否发生了碰撞
end
end
GJK碰撞检测算法比较耗时,因此在GJK碰撞检测之前,可以先用物体的包围盒快速测试是否相交。后面将会讲到广义的碰撞检测,可以近一步减少测试次数。
1. 小游戏概述
本小节,我们来制作一个简单的打飞机小游戏。通过制作小游戏,来学习如何应用物理引擎。小游戏的基本元素有: 敌人, 玩家, 子弹。玩家操作自己的飞机,尽可能的躲避敌人的子弹,同时发射子弹去消灭敌机。这里面就涉及到了非常多的碰撞检测。
敌机的逻辑比较简单。从屏幕上方开始。给敌机刚体一个初始的向下速度,边移动边发射子弹。当移动超出屏幕底部的时候,自动销毁掉。
子弹的逻辑也比较简单。有一个初始的位置和初始速度,一直向前移动,直到撞击到障碍物或者撞击到目标的时候销毁。同时对目标计算伤害。子弹这里用到了触发器的概念,只进行碰撞检测,不执行碰撞反馈。既可以避免子弹把敌机撞击到屏幕之外的问题,也能避免大量子弹执行碰撞反馈所引起的开销。
玩家则通过键盘控制自己的飞机移动,手动发射子弹。有一个细节需要注意,如果通过力的方式去控制玩家飞机的刚体移动,会有操作上的延迟,体验很不好。所以我倾向于直接修改飞机的坐标,但是这种方式会出现穿墙的情况。所以在飞机与墙发生碰撞的时候,按照穿透向量的反方向重新修正一下飞机的位置,避免它钻到墙里面去。
public void OnCollisionStay(CollisionInfo info)
{
// 矫正坐标,不要穿到墙里
rigidbody.position -= info.normal * info.penetration;
// 将碰撞深度归零,不要让物理引擎产生作用力
info.penetration = 0;
}
2. 碰撞掩码
在屏幕的四周创建四个box来包围住屏幕,避免玩家的飞机以及玩家的子弹移动出屏幕。但是敌机和敌机的子弹是从屏幕外边飞到屏幕内的,所以不希望障碍物挡住敌机和敌机的子弹。
这里需要引入“碰撞掩码”的概念,给每个物体添加不同的标记,以及他所期望碰撞到哪些物体。将标记信息用二进制位来表示,碰撞检测的时候可以用二进制“与”运算快速执行判断,决定拿些物体之间可以发生碰撞。
if ((a->collisionMask & b->selfMask != 0) ||
(b->collisionMask & a->selfMask != 0))
{
// 执行碰撞检测
}
- selfMask 表示物体的标记;
- collisonMask 表示物体需要和哪些其他标记的物体发生碰撞。
我们来梳理一下飞机和子弹的碰撞掩码的关系,避免出现重复的伤害计算。总共有五类物体: 障碍物, 敌机, 敌机的子弹, 玩家的飞机, 玩家的子弹。
碰撞关系 | 障碍物 | 敌机 | 敌机子弹 | 玩家飞机 | 玩家子弹 |
---|---|---|---|---|---|
障碍物 | |||||
敌机 | |||||
敌机子弹 | 1 | ||||
玩家飞机 | 1 | 1 | |||
玩家子弹 | 1 | 1 |
- 障碍物属于静态物体,不需要主动撞击任何其他物体。
- 敌机也不需要主动撞击任何其他物体。敌机与玩家飞机的碰撞伤害计算,由玩家飞机来进行处理。
- 敌机子弹会主动的撞击玩家飞机,由子弹来处理伤害计算。
- 玩家飞机碰撞到障碍物之后,会进行位置矫正。碰撞到敌机之后会进行伤害计算。
- 玩家子弹碰撞到障碍物后会销毁,碰撞到敌机之后进行伤害计算。
3. 子弹发射器
我们在这里实现一个稍微复杂一点的子弹发射器。每次发射能够发射若干组,每组发射中又包含若干个子弹。通过控制每次发射的位置偏移,角度偏移,时间偏移。能够得到非常多样性的发射效果。
3.1 双孔发射器
每次发射三组子弹。每组包含两颗子弹。子弹之间增加位置偏移。
3.2 太阳辐射发射器
每次发射三组子弹。每组包含36颗子弹。子弹之间有10度的角度偏移。
3.3 甩鞭发射器
每次发射一组子弹。通过控制子弹的角度偏移和时间偏移,在最小和最大角度之间进行摆动。
3.4 螺旋发射器
每次发射一组子弹。通过控制子弹的角度偏移和时间偏移。
3.5 更复杂的发射器
以前我在《雷霆战机》项目组的时候,做过一版非常灵活的弹幕编辑器。每个发射器包含若干个发射组,每个组的属性可以单独编辑,组发射子弹的时机也由时间参数控制。每个组还包括若干个“影响器”,在不同的时间点,修改子弹的运动属性,如位移,旋转,速度,角速度,加速度,角加速度,运动曲线等。具体细节已经超出本节范畴,就不再去实现了。
4. 范例
本章Demo使用Unity3D引擎开发,Demo工程已上传github: https://github.com/youlanhai/learn-physics/tree/master/Assets/07-2d-demo
本系列文章会和我的个人公众号同步更新,感兴趣的朋友可以关注下我的公众号:游戏引擎学习。扫下面的二维码加关注: