文/糍粑大叔

  上文说明在弹道实现中,关于u3d的物理引擎的一些相关要点,并跟给了实体性的关键实现代码。

  本篇中,将继续说明射线型弹道的实现的。

  四、射线型

  本章先讲的碰撞逻辑实现,由于射线型用u3d的sprite是绘制不出来的,所有需要特殊的技巧,绘制方法在下一章中说明。

  使用Physic2D库,进行非自动碰撞检测

  区别于实体,在游戏中,我需要实现类似于激光射线、范围伤害(AOE)的攻击类型。

  这种情况下,就不能依靠rigidbody来实现碰撞的检测,前文中又说了,u3d的物理引擎不支持两个collider的碰撞检测。

  所以,没有办法“自动”做碰撞检测(这里所谓自动,就是去实现一个函数,然后等着u3d在碰撞发生时自动调用)

  我依然使用u3d的2D物理库,在每一帧(每个Update)中,对目标collider进行检测。

  例如,AOE伤害:

  1. RaycastHit2D[] hits = Physics2D.CircleCastAll(transform.position, m_Info.aoeRadius, Vector2.zero,
  2. LayerManager.GetLayer(m_Faction).oppUnitMask );


  穿透激光,可以对射线上的单位造成伤害。

  其攻击的实现代码:

 

  1. IEnumerator _TakeAttack ()
    • {
      • yield return new WaitForSeconds (RAY_TAKE_ATTACK);
        •  
        • Vector3 v = Utils.Up (transform);
          • Vector3 worldCenter = transform.position + v * m_Info.attackDistance / 2;
            • Vector2 size = new Vector2 (width, m_Info.attackDistance);
              •  
              • RaycastHit2D[] hits =
                • Physics2D.BoxCastAll (worldCenter, size,
                  • Utils.DirToAngle(v), new Vector2 (0, 0),
                    • Mathf.Infinity,LayerManager.GetLayer(m_Faction).oppUnitMask);
                      •  
                      •       m_Filter.Clear();
                        •       for (int s = 0; s < hits.Length; ++s)
                          •       {
                            •         TargetPick pick = TargetPick.From (ref hits [s]);
                              •         pick.ToShield();
                                •         if( pick )
                                  •           if( m_Filter.Test(ref pick) )
                                    •         m_Info.AttackOn (pick, v, m_myUnit,hitEffectType,null);
                                      •       }
                                        • }

  定向激光对指定的单位进行攻击。

  定向激光不需要通过碰撞检测去“探测”激光与哪些collider相交的,因为定向激光是对已指定的单位进行持续攻击。

  需要解决的问题,激光与单位的碰撞点到底在哪。根据这个碰撞点,绘制激光的形状。

  其攻击的实现代码:

 

  1. public bool _TakeAttack (out Vector3 point)
    • {
      • point = Vector3.zero;
        • Vector3 v = Utils.Up (transform);
          • // The colliders in the array are sorted in order of distance from the origin point
            • RaycastHit2D[] hits = Physics2D.RaycastAll (transform.position, v, m_Info.attackDistance,
              • LayerManager.GetLayer(m_Faction).oppUnitMask);
                •  
                • // 是否有target的hit
                  • TargetPick pick = TargetPick.none;
                    • for (int s = 0; s < hits.Length; ++s)
                      • {
                        • pick = TargetPick.IsTarget (target, ref hits [s]);
                          • if (pick)
                            • break;
                              • }
                                •  
                                • if (pick)
                                  • {
                                    • point = pick.point;
                                      • m_Info.AttackOn (pick, v, m_myUnit, Const.NONE_EFFECT, this);
                                        • if (pick.unit pick.unit.curHp <= 0)
                                          • return false;
                                            •  
                                            • if (pick.part pick.part.unit.curHp <= 0)
                                              • return false;
                                                •  
                                                • return true;
                                                  • }
                                                    • return false;
                                                      • }

  类似于图中的闪电效果。原理持续的定向激光基本一致,区别是单次攻击时播放闪电(或其他效果)动画。

  同持续定向激光一样,在攻击过程中不停的用RayCast判断闪电是否“打”到了目标上,

  如果没有需要立即中断攻击和攻击动画,否则攻击单位在出现突然转身的是否,闪电会随着攻击攻击单位移动,出现bug。

  不再给出代码。

  范围攻击

  典型的是喷火器或者爆炸,在一定范围内所有单位收到伤害。

 

  1. bool _TakeAttack ()
    • {
      • Vector3 dir = Utils.Up (transform);
        •  
        • m_Filter.Clear ();
          • RaycastHit2D[] hits = Physics2D.CircleCastAll (transform.position, m_Info.attackDistance, Vector2.zero,
            • Mathf.Infinity, LayerManager.GetLayer(m_Faction).oppUnitMask);
              •  
              • bool f = false;
                • // 是否有target的hit
                  • for (int s = 0; s < hits.Length; ++s)
                    • {
                      • Vector3 v = Utils.V2toV3 (hits [s].point) - transform.position;
                        • if (Mathf.Abs (Vector3.Angle (dir, v)) < m_Info.attackArc / 2)
                          • {
                            • f = true;
                              • TargetPick pick = TargetPick.From (ref hits [s]);
                                • // AOE CircleCastAll 可能选不到shield
                                  • pick.ToShield ();
                                    • if (pick)
                                      • if (m_Filter.Test (ref pick))
                                        • m_Info.AttackOn (pick, Vector3.zero, null, Const.NONE_EFFECT ,this);
                                          • }
                                            •  
                                            • }
                                              •  
                                              • return f;
                                                • }
复制代码
  原理很简单,先找到圆形范围内的所有collider,再判断是否在喷火器的扇形角度内。

  攻击、弹道这块内容游戏逻辑是游戏的一个重点,其实很难写一辆篇文章说清,其实我很想把从最下层的u3d的物理、绘制到最上层代码逻辑架构 全部说清楚,

  但发现写一篇文博耗费的时间比我想象长,长到我写代码实现的一个功能的时间还没写一篇文章长。

  所以,我考虑了下,不能指望所有的东西全部说清楚,最要还是讲原理,不同于网上大部分的教程讲的是最基础的内容,甚至是解释api,

  而是建立读者有一定基础上,讲原理、讲结构,讲自认为的难点,有助于自己梳理游戏代码,也是对关键的技术点做一个备忘。

  下篇预告:弹道的图形效果实现、攻击逻辑的结构和要点(比如本文中展示代码中定义的类的意义)

  (本篇完,下篇待续)

相关阅读:糍粑大叔的独游之旅-战斗!之弹道实现(上)

锐亚教育

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