网站首页 资讯 热点 行情 地区 推荐 民宿 酒店 家居 度假 滚动
首页 >  滚动 >  >  正文

Unity中使用代理器来做碰撞检测,使一个脚本能控制多个碰撞箱的碰撞事件

2023-07-28 17:30:25来源:哔哩哔哩

问题概述

Unity在做碰撞检测或触发检测时,由于Mono基类的限制,碰撞与触发必须实现特殊函数、诸如OnCollisionEnter()才能检测,而且检测的主体还是与该脚本绑定的碰撞箱物体——一个脚本中只能检测该脚本附着地同层级的单一碰撞箱所产生的碰撞,所以在处理子物体碰撞、多物体碰撞管理时,使用起来不是很灵活。


(相关资料图)

我被这狗屎问题困扰了多年,今天终于有了解决方案。

当然这个方法只是解决此问题的众多方法之一,成熟点的方法也许会用到接口与委托,这里我就使用我的方法。

文章作者:bilibili:_Iamsleepingnow

(甲)物理碰撞的条件

在Unity中,达成物理碰撞有如下的条件:

碰撞物体双方都拥有碰撞箱(Collider抽象类及其子类)

碰撞物体至少有一方拥有刚体(Rigidbody类或CharacterController)

(乙)碰撞及触发检测方法

基础的碰撞检测方法如下:

private void OnCollisionEnter(Collision collision)

private void OnCollisionStay(Collision collision)

private void OnCollisionExit(Collision collision)

基础的触发检测方法如下:

private void OnTriggerEnter(Collider other)

private void OnTriggerStay(Collider other)

private void OnTriggerExit(Collider other)

(丙)代理碰撞器

这里的设计理念是:使用代理器的方式让一个中央脚本中的碰撞触发检测方法检测到许多不同的碰撞箱身上的代理器收集到的碰撞与触发信息,然后传递回中央脚本。

一、需要一个中央脚本,这个类负责管理所有的碰撞事件,在此命名为[ObjectManager]

二、需要一个代理碰撞器类,代理碰撞器会代替中央脚本去碰撞,命名[CollisionProxy]

三、代理器中需要加入需要代理的检测方法,注意代理器与中央脚本都继承MonoBehaviour

四、代理器里还需要声明其所代理的中央脚本(基类)以及自身的代理序号。代理序号的作用是,当此代理器将其收集到了碰撞信息传递给中央脚本时,中央脚本就可以通过序号来区分不同的代理器的频道的信息了。

五、然后在中央脚本(基类)里定义与代理器中对应的虚拟代理方法,需要在参数中传递碰撞信息与代理器自身。代理方法需要写成虚拟方法,这样此基类的子类就能用override重写。

六、在代理器中调用中央脚本(基类)的代理方法,将信息传入。

七、其实现在已经做完了,目前可以加入一些测试用的行,仅做测试:(为了方便,我直接就在虚拟代理方法里测试了,但是之后在实际使用时,需要将新类继承自这个中央脚本,然后重写中央脚本中的代理方法。)

八、回到Unity中,创建一个球,携带碰撞箱,加一个刚体组件Rigidbody,取消使用重力useGravity,勾选应用关节IsKinematic,这个物体就是需要被检测的对象。然后再新建一个方体,移除其碰撞箱BoxCollider,并附上ObjectManager中央脚本。在方体的子级里添加三个空物体,并附上碰撞箱组件SphereCollider,并在每一个空物体上附着CollisionProxy代理器组件,将代理器组件中objectManager参数设置为其父级中央脚本,并更改每一个代理器中的proxyIndex序号。这里为了测试触发方法,所以将代理器中的碰撞箱的IsTrigger勾上了。

九、运行工程,手动移动代理器的位置,中央脚本就会进行输出:

十、测试成功,中央脚本获取到了所有触发信息(包括触发的物体,代理器的信息)。接下来定义一个新脚本来继承ObjectManager中央脚本,命名为ObjectManagerSon。在其中,重写代理方法,稍微对测试行进行更改。

十一、将场景中方体身上的ObjectManager类替换成ObjectManagerSon类,并将其赋给三个代理器的objectManager参数。然后运行,测试如下:

总结

这个方法最大的问题是,如果一个脚本需要复杂碰撞检测,则需要修改其继承的父类。所以推荐将此方法写在其原生父类中。如果该脚本无原生父类,即父类为Mono,那可能要考虑考虑将你的工程框架化了。

标签:

相关文章

[ 相关新闻 ]