Unity3D RPG Core | 05 鼠标控制人物移动

这集开始写代码了,首先需要配置一下 Visual Studio(现在还不明白为什么有人喜欢使用 VSCode。除非 VS 没得用,VS 不香么😂),使用 Visual Studio Installer 安装 Unity 开发扩展。不先安装也没有事情,如果没有安装,打开项目的时候也会提示你安装,非常方便。

1. 新建 C# 脚本

首先在 Assets 中,如图 1 所示,新建一个 C# 脚本。这边将其命名为 MouseManager,用于鼠标相关功能的实现。

图1 新建 C# 脚本

接着在 Hierarchy 窗体中新建一个空对象用于挂载脚本,视频中让养成好习惯,要将空对象的位置信息重置一下。如图 2 所示,直接把资产中的脚本拖拽到对象查看界面就可以添加。

图2 空对象

设置完成之后,我们双击脚本就会自动打开 Unity VS 工程。

2. Unity 事件

代码清单 1 中实现了添加一个 Unity 事件。第 4 行引入事件需要的命名空间 UnityEngine.Events;第 7 行定义一个 Unity 事件,参数是三维向量,因为后续需要传入人物所要移动到的坐标;需要额外添加第 6 行的反序列化特性,这样声明的变量才能显示在 Unity 界面上;第 10 行声明事件变量,命名为 OnMouseClick。

代码清单 1 定义 Unity 事件
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Events;
  5.  
  6. [System.Serializable]
  7. public class EventVector3 : UnityEvent<Vector3> { }
  8. public class MouseManager : MonoBehaviour
  9. {
  10.     public EventVector3 OnMouseClick;
  11.  
  12.     // Start is called before the first frame update
  13.     void Start()
  14.     {
  15.        
  16.     }
  17.  
  18.     // Update is called once per frame
  19.     void Update()
  20.     {
  21.        
  22.     }
  23. }

写完如上代码之后,回到 Unity 界面。可以发现,如图 3 所示,脚本处显示出了添加的 OnMouseClick 事件变量。我们将人物对象拖拽其中,指定响应事件的对象;然后设置响应设置事件的变量为 NavMeshAgent.destination(人物的 NavMeshAgent 组件在上一节视频中设置过了)。

图3 Unity 中设置变量

设置完后,接下来只需要实现如何触发事件就可以了。

3. 实现鼠标控制人物移动

我们控制人物移动方式是使用 Unity 框架中现成的 NavMeshAgent 组件功能,只要设置其 destination 属性就可以对 Agent 进行移动。

至于如何获取到移动的位置?我们通过从摄像机处引出一条到鼠标点击处的射线,再判断这条射线碰撞到了什么物体。如果是地面,则获取其碰撞的坐标,就是所要移动到的位置。

3.1 Camera.ScreenPointToRay

首先我们来看如何从相机处引出一条射线,它使用 Camera.ScreenPointToRay 函数。

我们可以按照手册上的样例,画一条到 (0, 0) 的射线。(0, 0) 位于屏幕的左下角,所以我们会看到图 4 中的结果。

  • Ray ray = Camera.main.ScreenPointToRay(new Vector3(0, 0, 0));
  • Debug.DrawRay(ray.origin, ray.direction * 1000, Color.red);
图4 射线

Debug.DrawRay 只能在 Scene 场景中显示,不知道怎么在 Game 场景中显示。

3.2 Physics.Raycast

有了射线,判断射线碰撞到的内容,可以使用 Physics.Raycast 函数,其中一个重载函数原型为:

  • bool Raycast(Ray ray, out RaycastHit hitInfo);

返回值返回是否有物体与射线相交。额外返回值 hitInfo,返回具体碰撞信息。具体内容可以参见手册中的 RaycastHit,我们就是通过它来获取碰撞的坐标点。

3.3 开始实现功能

学习了 Camera.ScreenPointToRay 和 Physics.Raycast 的使用之后,就可以开始实现鼠标控制人物移动的功能了。

实现代码如代码清单 2 所示:第 22 行判断是否点击了鼠标左键;如果点击了,就则相机引出一条射线(第 24 行);如果有物体碰撞(第 26 行),则判断碰撞对象是否是地面(第 28 行);如果是地面,则调用之前设置好的 OnMouseClick 事件,其实就是设置 NavMeshAgent.destination,进而控制人物移动。

代码清单 2 实现鼠标控制人物移动
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Events;
  5.  
  6. [System.Serializable]
  7. public class EventVector3 : UnityEvent<Vector3> { }
  8. public class MouseManager : MonoBehaviour
  9. {
  10.     public EventVector3 OnMouseClick;
  11.  
  12.     //Camera camera;
  13.     // Start is called before the first frame update
  14.     void Start()
  15.     {
  16.  
  17.     }
  18.  
  19.     // Update is called once per frame
  20.     void Update()
  21.     {
  22.         if (Input.GetMouseButtonDown(0))
  23.         {
  24.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  25.             RaycastHit raycastHit;
  26.             if (Physics.Raycast(ray, out raycastHit))
  27.             {
  28.                 if (raycastHit.collider.gameObject.CompareTag("Ground"))
  29.                     OnMouseClick.Invoke(raycastHit.point);
  30.             }
  31.         }
  32.     }
  33. }

需要注意的是,这边判断碰撞对象是比较对象的标签,我们需要设置地面物体的标签为 "Ground"。设置的位置如图 5 所示,初始的时候没有名称是 "Ground" 的标签,我们需要添加后再进行选择设置。

图5 设置标签

3.4 调节 Nav Mesh Agent 参数

至此,移动的功能就已经实现了。但是运行程序后,人物移动看起来有点慢,我们可以调节 Nav Mesh Agent 组件中的现有参数。

如图 6 所示,Nav Mesh Agent 组件中可以设置速度、角速度、加速度、停止距离以及是否有自动刹车效果,我们按照自己想要的效果进行设置即可。

图6 修改参数

最终的效果如图 7 所示,绑定的动画还是原先素材的循环逻辑,后续需要自己定义。

图7 实现效果