冰河世纪 发表于 2024-7-15 18:45

Unity开发随记录01-碰撞检测基础(两个Cube的碰撞、Cube与Particle System的碰撞)

Unity的碰撞检测在很多处所城市用到:
FPS游戏中,朝仇敌打了一枪,如何判定是否打中?打中了怎么让仇敌扣血?
开车游戏中,驾驶的车撞到了障碍物,按照常理,汽车的速度会降低/障碍物会被撞飞,如何实现?
......
种种应用,都需要用到碰撞检测(Collision Detection)。
本文介绍两种最简单的情况:

[*]CubeA和CubeB相碰撞,碰撞之后触发CubeA和CubeB的碰撞回调函数(碰撞之后可以对CubeA和CubeB用代码进行操作)
[*]Particle System发出的粒子与CubeA相碰撞,碰撞之后触发CubeA以及粒子系统的碰撞回调函数(碰撞之后可以对CubeA或者Particle System进行操作)
一、两个物体的碰撞



如上图所示,左边为CubeB,右边为CubeA。
当我们新建一个Cube的时候,Cube会自带属性Box Collider,Collider就是碰撞体的意思,之后的碰撞检测就主要用Collider实现。



创建Cube之后自带的Collider属性

我们可以点击Edit Collider右边的按钮编纂Collider,如下图的绿色框所示:


同样的道理,我们也创建好CubeB,接下来写碰撞检测的脚本。
//====CollisionDetection.cs====//
private void OnCollisionEnter(Collision collision)
    {
      Debug.Log(this.gameObject.name + ” is Collided with ” + collision.gameObject.name);
    }

   
private void OnTriggerEnter(Collider other)
    {
      Debug.Log(this.gameObject.name + ” is Triggered by ” + other.gameObject.name);
    }将CollisionDetection脚本挂载到CubeA和CubeB上,运行项目。
我们会发现,当我们手动让两个物体碰撞 的时候,console窗口并没有打印相关的信息。


这是因为Unity官方设置的静态碰撞体与静态碰撞体相碰撞,并不会在碰撞之后发送动静。
我理解的就是,真正在游戏中运用的时候,物体必定不是简单的默认物体,必定还会带有其他的属性(比如刚体),所以对于默认的静态碰撞体就不设置碰撞检测了。
网上也有人解释:碰撞检测是由Unity物理系统(Physical System)打点的,物体首先需要被物理系统打点才能进行碰撞检测。我感觉也挺有道理的。
那么接下来,我们给物体B加上刚体属性,这样物体B应该就被物理系统打点了,按道理碰撞检测就应该可以进行了。



即将运行的demo



手动将CubeB拖到CubeA上

由于CubeB增加了刚体属性,默认会有重力(gravity),所以刚开始运行的时候会落在地上,跟地面碰撞,可以看到第一条输出信息表白B物体与地面进行了碰撞。
当我们把CubeB放到CubeA上的时候,CubeA和CubeB的OnCollisionEnter()函数都被调用了,碰撞检测成功。
做到这里,我们又会发现两个小问题:

[*]CubeA和CubeB不能重叠了
[*]OnTriggerEnter()函数未被调用
其实如果要解决上面的两个问题,只需要将CubeA的BoxCollider属性中的isTrigger打上勾就行。



将CubeA的BoxCollider的isTrigger选项勾选

勾选之后我们再次运行项目并测验考试


我们发现CubeA和CubeB可以重合到一起了,而且调用的是OnTriggerEnter函数而不是OnCollisionEnter函数。
⚠注意,这个处所如果给有刚体属性的CubeB勾选isTrigger选项,CubeB会穿过地面掉下去。
二、物体与粒子系统的碰撞



如上图所示,我们但愿这样的粒子系统可以进行碰撞检测,即当粒子与某一个物体碰撞的时候,我们能操作回调函数控制粒子和被碰撞的物体。
既然要碰撞检测,那被碰撞的物体和碰撞的物体都得有碰撞体,CubeA已经有了碰撞体,接下来我们给粒子系统增加碰撞体。选中粒子选项的Collision就可以了,粒子系统的碰撞体默认是球形的(Sphere Collider),可以通过Radius Scale进行球体半径的设置,如果没有看见粒子系统的碰撞体,可以勾选一下最下面的Visuialize Bounds。



设置粒子系统的Collsion


设置好之后,我们就可以在Scene窗口看见每个粒子都有一个绿色的Sphere Collider了。
仿照之前的脚本,我们编写新的脚本,用于检测粒子的碰撞:
//====DetectParticleCollosion.cs====//
    private void OnParticleCollision(GameObject other)
    {
      Debug.Log(this.gameObject.name + ” detects Particle system collision with ” + other.gameObject.name);
    }

    private void OnParticleTrigger()
    {
      Debug.Log(this.gameObject.name + ” is triggered by Particle”);
    }将脚本挂载到CubeA和ParticleSystem上面。
此时我们运行项目,查看打印窗口。(注意此时CubeA的isTrigger保持选中)


我们发现粒子径直穿过了CubeA,但是无论是OnParticleCollsion还是OnParticleTrigger都没有被调用。
按照一中的实践成果,碰撞需要至少有一个物体有刚体,但这里粒子系统和CubeA都没有刚体属性,怎么办?
我们一步一步来测验考试!
首先,我们测验考试将CubeA的isTrigger打消,运行项目。


我们发现此刻OnParticleCollision能够调用了,而且不仅是CubeA和Particle System能彼此进行碰撞检测,我们通过通知台窗口还能发现CubeB与Particle System能彼此进行碰撞检测。同时,ParticleS伊斯特曼还能检测到与地面的碰撞检测。
为什么呢?还记得我们在一中,当我们没有勾选CubeA的isTrigger时,我们发现CubeB和CubeA不能重合,那这里其实也是一样的,没有勾选CubeA isTrigger的时候,粒子是不能跟CubeA重合的,粒子遇到CubeA会被反弹。我感觉这其实也间接说明了Particle System有刚体的属性(因为在一中,CubeB有刚体,CubeA 没有isTrigger的时候,CubeA和CubeB不能重合)
如果我们不想让粒子遇到CubeA反弹应该怎么做呢?
我们可以选中粒子系统的Trigers属性,并把CubeA拖入到下方列表中,这样当粒子与CubeA只要一碰撞就会消掉了。



设置Particle System的Trigger属性

运行项目,查看控制台输出。


这时,我们就会发现粒子与CubeA碰撞之后不会反弹了,不会有CubeB与Particle System的碰撞检测了。
我们可以把CubeA拉开一些,看看粒子是不是真的不能穿过CubeA。


可以发现粒子流被CubeA完全盖住了。
最后还有一点需要注意,上面我们查看控制台输出的时候,只对OnParticleCollision进行了分析,如果本身不雅察看OnParticleTrigger的话,我们会发现不管粒子与CubeA是不是碰撞了,这个函数会一直被调用。



OnParticleCollsion没有调用,但OnParticleTrigger被一直调用

三、其他参考资料


[*]粒子系统与粒子系统的碰撞检测(水浇到火上,火被灭了),详见下面的文章:
2. 相对Unity内置碰撞机制有更深了解的,可参考官方文档:



官方的碰撞操作矩阵

四、最后

我是硝基同学,一名未来想去很多处所的法式员,如果内容对您有辅佐,能不能帮我点个小小的赞呢?您的撑持是我创作的最大动力。
页: [1]
查看完整版本: Unity开发随记录01-碰撞检测基础(两个Cube的碰撞、Cube与Particle System的碰撞)