Unity3D RPG Core | 23 扩展方法

这节我们实现敌人面向的攻击视角内,如果没有角色人物,则不对角色人物造成伤害。这也是符合现实逻辑的。

1. 使用扩展方法实现

我们新建一个 C# 脚本,命名为 ExtensionMethod。内容如代码清单 1 所示,扩展方法类需要是静态的。编写 IsFacingTarget() 函数实现对范围的判断。

IsFacingTarget() 函数的第一个参数就是需要扩展的类,使用 this 关键字申明。这边的 this 关键字就暗示了 this 指针的意思,加上语言支持反射,所以对实现的机制并不好奇。

代码清单 1 扩展方法
  1. static public class ExtensionMethod
  2. {
  3.     static float m_dotThreshold = 0.5f;
  4.  
  5.     static public bool IsFacingTarget(this Transform transform, Transform target)
  6.     {
  7.         Vector3 toTarget = target.position - transform.position;
  8.         toTarget.Normalize();
  9.  
  10.         float dot = Vector3.Dot(transform.forward, toTarget);
  11.         return dot >= m_dotThreshold;
  12.     }
  13. }

IsFacingTarget() 的实现,主要是使用了 Vector3.Dot 判断敌人面向朝向与角色人物之间的角度。Vector3.Dot 就是点积的计算:

\(\vec{a}\cdot\vec{b}=\lvert\vec{a}\rvert\lvert\vec{b}\rvert cos\theta\)

当向量都是单位向量时,两个向量的点积返回的就是两个向量之间角度的 cos 值。各个角度的 cos 值如图 1 所示,小于 0 代表背向。这边指定 cos 值大于 0.5 为在视线范围内。大于 0.5 为左右 60 度这 120 度范围内。

图1 cos

2. 增加判断条件

敌人产生伤害是在之前定义的 Hit() 函数中实现的。如代码清单 2 所示,现在我们在上面增加判断条件,只有面向角色人物的时候才会产生伤害。

代码清单 2 朝向判断
  1. public class EnemyController : MonoBehaviour, IEndGameObserver
  2. {
  3.     // Animation Event
  4.     void Hit()
  5.     {
  6.         if (transform.IsFacingTarget(m_playerObj.transform))
  7.         {
  8.             CharacterData playerCharacterData = m_playerObj.GetComponent<CharacterData>();
  9.             m_attackData.TakeDamage(playerCharacterData);
  10.         }
  11.     }
  12. }

我们做如下测试:将角色人物的血量设置为 1,如图 2 所示,通过走位来躲避敌人的攻击。

图2 走位躲避攻击