文/糍粑大叔

  今天谈谈一个有趣的内容——u3d实现弹道。当然这个完整的说,也非常复杂,还是由浅入深,先说说最核心的原理。

  一、定义

  我将分为至少两种:

  1、实体型。即发射后,生成一个带刚体的gameobject,可以通过u3d的物理引擎实现碰撞检测。比如机关枪、弹道等等。

  2、射线型。即的运动和碰撞不由刚体实现,弹道控制由代码实现,通过Physics2D下函数或自己计算物体检测。比如定向激光、AOE伤害。

  本文分别讲两种的实现。

  二、理解U3D的物理引擎

  知其所以然,才能更好、更快的进行代码实现。这个章节从运用和现象入手,说明一些有关u3d物理引擎的“坑”

  Collider和Rigidbody

  如果完全不了解U3D的Collider(碰撞体)、Rigidbody(刚体)可以先找下相关的文章看下,网上很多很好的文章。

  这里用自己的理解简单说下(针对2D):

  Collider描绘物体的碰撞形状,有Box、Cirlce等,是碰撞检测的基础,比如鼠标点击的碰撞判断,都是依赖于定义的Collider来知晓物体的“碰撞形状”

  Rigidbody是给物体赋予一个接受物理规律的属性,比如给一个小球加上刚体,默认的小球就会收到重力加速度往下掉。

  刚体必须有collider,不然就没有“形状”。

  在u3d的2D物理效果的设计理念是:

  不会动的物体,只设Collider,例如大地、树木;

  会动的,需要有物理运动效果的,设置Collider和Rigidbody,Collider说明“外形”,Rigidbody说明物理属性,如质量、碰撞属性等。

  注意这个理念,因为我首次使用2D物理到游戏中的时候,有很多地方很费解,不明白为什么要这么设计,如果理解这个理念了,很多东西就想明白了。

  碰撞检测的相关参数

  U3D的2D物理引擎,可以不用做任何编码实现刚体的运动碰撞效果。

  但如果需要代码获取碰撞信息,相关的属性有:

  Collider的Is Trigger,Rigidbody的Is Kinematic

1122102j51ai5zae89a61w.png
  至于其他刚体参数的作用,可以摆个场景逐个调整参数看看效果,就基本明白了。

  因为我主要是想通过u3d的2D物理引擎获得的碰撞信息,而不关注碰撞后的物理效果,所以其他参数都可以不管或设置为0。

  特别的,由于游戏是一个top-down视角的游戏,而u3d的2D物理引擎默认y轴是2D世界的上下方向,所以Gravity Scale要设置为0。

112210zey2hvw2zlsasxej.png
  对应的相关的函数有:

  OnTriggerEnter2D、OnTriggerExit2D、OnTriggerStay2D(Collider发生碰撞时被调用)

  OnCollisionEnter2D、 OnCollisionExit2D、OnCollisionStay2D(Rigidbody发生碰撞时被调用)

  下面将解释这些属性和函数的作用。

  碰撞检测的推荐实现方式——Rigidbody的Collision

  需要用u3d的2d物理引擎实现碰撞检测时,需要这么设置(假定检测A和B之间的碰撞):

  1、A和B都添加collider,不勾选Is Trigger

  2、A或B至少一个添加rigidbody,不勾选IsKenematic

  3、对A或B添加脚本,添加OnCollisionEnter2D、 OnCollisionExit2D或OnCollisionStay2D函数获取碰撞信息。

  以本文的实体型为例:

  1、对游戏单位和都添加collider

  2、对添加rigidbody

  3、对添加OnCollisionEnter2D方法,编写造成伤害的逻辑代码,并销毁对象。

  关于Rigidbody的Is Kinematic的属性:勾选后,2D物理引擎对这个刚体不起作用,只能代码去实现物体的运动。同时,OnCollisionEnter2D也不会被触发。

  另外一种碰撞检测的实现——Collider的Trigger

  通过Collider的Is Trigger的,也能实现“碰撞检测”。

  Collider的Is Trigger:顾名思义,这个属性说明是否触发,勾选后,则会有“碰撞时”OnTriggerEnter2D、OnTriggerExit2D, OnTriggerStay2D函数。

  例如,检测A和B之间的碰撞:

  1、A和B都添加collider,A勾选Is Trigger,B不勾选

  2、A添加rigidbody

  3、对A脚本添加OnTriggerEnter2D

  A和B的collider发生接触时,则A的OnTriggerEnter2D被调用。如果B的脚本也有OnTriggerEnter2D,也会被调用,尽管B没有勾选Is Trigger。

  这种“碰撞检测”,依靠Collider的trigger机制,在Collider层面就可以完成,其原理应该和鼠标点击事件的触发类似。但有以下问题:

  1、这个触发机制的碰撞检测频率和Update一样,而上文中推荐方式(利用OnCollisionEnter2D)是和FixedUpdate一样,后者是专门是做刚体物理运算,其计算频率更好,碰撞检测更准确。如果使用OnTriggerEnter2D的方式,检测到碰撞发生时可能两个碰撞的物体已经相互嵌入很久了,如果其中一个物体运动速度过快,可能已经“穿”过去了

  2、OnCollisionEnter2D的参数提供的碰撞信息更丰富,而OnTriggerEnter2D只有一个碰撞对方collider的信息,得不到更精确的点。

  3、虽然碰撞是在Collider层面完成,感觉跟Rigidbody没有什么关系(1、2两点的想象也侧面印证了我这个想法),但A和B之间必须有一个是Rigidbody,不然碰撞事件触发不了。Physics2D中IsTouching等函数也有这样。

  4、设置Trigger后,所有的碰撞事件被Trigger拦截,OnCollisionEnter2D不会再被调用。

  基于以上因素,这种碰撞检测,不能称之为有效的“碰撞检测”,在实际运用中要根据实际情况判断是否合适。

  作为游戏物体和物体的碰撞检测,不推荐使用Collider的Trigger方式。

  关于碰撞检测的总结

  1、如果想使用物理引擎实现碰撞,包括Collider的Trigger,rigidbody的Collision,Physics 2D的IsTouching等方法,除了碰撞双方都有Collider,必须有1个有rigidbody。(此点让我无力吐槽)。

  2、使用Collider的Trigger(勾选Is Trigger),可以使用OnTriggerEnter2D、OnTriggerExit2D, OnTriggerStay2D碰撞,但没有碰撞物理效果,rigidbody的collision无法使用。Collider的Trigger不是在物理引擎层面上工作的,不管是碰撞检测的更新频率、是碰撞结果都不好,且它直接“阻止”了物理引擎的对物体的作用。

  3、使用rigidbody的collision(不勾选Is Kinematic),使用OnCollisionEnter2D、 OnCollisionExit2D、OnCollisionStay2D碰撞,物理引擎会影响刚体的运动,会有碰撞反弹的物理效果。最好在OnCollisionEnter2D只获取状态而不更新物体运动,因为物理引擎这是也在控制它的运动。

  综上,u3d物理引擎的使用限制还是很多的,实现很多逻辑功能都有障碍。由于对于实体的实现,打击单位后,自我销毁,和以上第3点正好满足,可以使用u3d的collision,而非实体显然不能使用。

  三、实体型

  如果认真阅读上面的分析且理解了原理,应该对u3d的2D碰撞(3D类似)的套路应该很清楚,实现实体打击效果,仅仅是点点、配配的事。

  现实方式为:

  1、对游戏单位和都添加collider

  2、对添加rigidbody

  3、对添加OnCollisionEnter2D方法,编写造成伤害的逻辑代码,并销毁对象。

  关键代码:

  1. void Update ()
  2. {
  3. if (Common.pause)
  4. return;
  5.  
  6. m_Anim.OnUpdate (GetComponent<SpriteRenderer> ());
  7.  
  8. float l = Time.deltaTime * m_Info.speed;
  9. transform.position += m_Direction * l;
  10. m_LeftDistance -= l;
  11. if (m_LeftDistance < 0 m_DestroySelf)
  12. {
  13. FlyerManager.Free(gameObject);
  14. }
  15.  
  16. }
  17.  
  18.  
  19. public virtual void OnCollisionEnter2D (Collision2D coll)
  20. {
  21. if (m_DestroySelf)
  22. return;
  23.  
  24. TargetPick pick = TargetPick.From (coll);
  25. m_Info.AttackOn (pick,m_Direction, m_myUnit,hitEffectType);
  26. m_DestroySelf = true;
  27.  
  28. }

复制代码
  其中有很多类和函数已经封装,例如:

  FlyerManager.Free(),内部实现了的回收,便于再利用。

  再如TargetPick和AttackOn,实现了拾取最合适的游戏单位和计算打击伤害的功能。

  (待续)

相关阅读:如何用unity3d 三分钟实现简单的赛车漂移

锐亚教育

锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛