撰写了文章 更新于 2017-12-15 18:08:12
第11篇 判定逻辑
【判定逻辑】
在弹幕游戏中,有诸多物体需要发生判定关系,自机、敌弹、敌机、Boss、自机子弹、Spell(Bomb)、道具等等,那么这些判定关系在程序里怎么描述,判定代码写在哪里,会牵涉到什么问题,这些就是本次科普的内容。
早在本系列第二篇的时候,讲到过游戏中物体叠放次序的问题。其实这就已经明确了一件事情,在游戏里这些物体应该分类管理。比如建立单独一个列表来存放子弹,单独一个列表来存放敌机,单独一个列表来存放特效。将所有物体分类存放主要有两个目的,一个是为了控制叠放次序,另一个是为了实现判定逻辑。
首先,先来罗列一下游戏里所有的判定关系:
1. 敌弹与自机
2. 敌机与自机
3. Boss与自机
4. 自机子弹与敌机
5. 自机子弹与Boss
6. Spell与敌弹
7. Spell与敌机
8. Spell与Boss
9. 道具与自机
以上总共9项是比较常用而且必须要有的判定项目,至于判定了要做什么,应该不用讲吧。。。此外,还有一些特殊情况也需要判定,比如自机接触到符卡名字、Boss血条等UI元素时,UI透明度要降低,这种也需要判定逻辑,不过原理都不复杂,这里就不一一例举了。
我相信,类似于“两点间距离是否小于指定值”这种逻辑大家肯定都会写,以下重点问题在于这些判定代码写在哪里比较合适。当然,不管写在哪里,肯定是属于数据处理环节(在上两次文章提到过,游戏有输入捕捉、数据处理、画面显示这三个环节)。对于初学者来说,可能会有疑问的地方在于,这个判定方法应该写在全局的Update方法里,或者是建立一个管理器,来通过管理器来判断各种物体碰撞情况,还是说将判定方法和待判定物体封装在一起,在各物体的Update方法中进行判定。
其实,我们只要了解一下这中间最关键的问题,就差不多该知道这个架构应该是怎样了。这中间最关键的问题其实在于判定形状,对于不同的物体,可能要用到不同的判定形状。举例来说,自机和敌弹判定,通常情况下,敌弹是圆的,自机判定点也是圆的,但是有一种敌弹叫做激光,可能采用椭圆判定,还有一种敌弹叫做曲线激光,判定形状更复杂。判定方法必然要描述物体的判定形状,那么就可想而知,如果将判定方法写在全局的Update方法里,或者是建立一个管理器来统一管理子弹和自机的话,就很难照顾所有的判定形状,而且如果要使用特殊形状的话也不易扩展。因此,最好还是将判定方法和待判定物体封装在一起,这其实也是将判定形状与物体封装在一起,就好比射线激光拥有椭圆形判定这个属性。
接下来的问题是,当两个物体要判定时,究竟将判定方法和哪个物体封装在一起。举例来说,当敌弹与自机发生判定时,判定方法也该和敌弹封装在一起,还是和自机封装在一起?其实这个原则很简单,和判定形状复杂的东西封装在一起。比如说,自机和敌弹的判定应该和敌弹封装在一起,因为自机判定形状总是圆的,而敌弹则不一定,可能有激光、曲线激光或者更复杂的形状。为了能够便于重写判定形状,因此应该将判定方法和那些需要重写形状的物体封装在一起。以下罗列了所有物体判定逻辑的情况供大家参考:
1. 敌弹与自机:判定方法写在敌弹里,因为自机判定是圆的,而敌弹不一定
2. 敌机与自机:判定方法写在敌机里,因为自机判定是圆的,而敌机不一定
3. Boss与自机:判定方法写在Boss里,因为自机判定是圆的,而Boss可能出现其它形态,比如加个翅膀什么的。
4. 自机子弹与敌机:判定方法写在敌机里,因为自机子弹是圆的,而敌机不一定
5. 自机子弹与Boss:判定方法写在Boss里,因为自机子弹是圆的,而敌机不一定
6. Spell与敌弹:判定方法写在Spell里,因为各种Spell的形状大相径庭
7. Spell与敌机:判定方法写在Spell里,因为各种Spell的形状大相径庭
8. Spell与Boss:判定方法写在Spell里,因为各种Spell的形状大相径庭
9. 道具与自机:判定方法写在道具里,以便兼容不同判定形状的道具
可以看到,虽然敌弹、敌机、Boss可能会出现不一样的形状,但是相比之下,Spell的形状更加多变,它们发生判定时,只能退而求其次,将判定方法写在Spell里,而将敌机、敌弹、Boss当成圆形判定或者点判定来处理。
那么本次科普就到这里。下一期会介绍一下各种判定形状及其适用场合,敬请期待。
此外,感谢E.S.C.大大的补充:
1、攻击判定可以和受击判定分开,这样就不用纠结应该封装在哪种对象里了
2、为了最大的可编程性,所有对象判定应当统一判断,然后再调用各个对象的方法,对象的onhit、onhitby方法传进来的应当是个数组
统一管理的好处是存在全局受击框表,可以在对象script里遍历受击框做追踪 另外最好也能支持一个对象有多个受击或攻击框