Unity3D RPG Core | 06 设置鼠标指针

这集视频主要做了两个事情。一个是重新实现上一节的鼠标控制逻辑,之前使用的是 Unity 自带的事件机制,这一节使用 C# 自带的事件机制;第二个是实现鼠标图标的设置。

1. C# event

我们将原先的 Unity 事件,改为 C# 委托:

  • public event Action<Vector3> OnMouseClick;

改完可以发现,之前的代码并没有编译错误,基本的执行逻辑还是相同的。

由于目前委托的机制不像之前 Unity 的事件机制,不可以在 Unity 界面中进行拖拽指定对象,所以我们需要为 OnMouseClick 手动添加委托函数。

委托函数是不会定义在目前的 MouseManager 类里面的。所以为了能设置问题函数,我们将 MouseManager 定义为单例类:

代码清单 1 设置单例类
  1. public class MouseManager : MonoBehaviour
  2. {
  3.     public event Action<Vector3> OnMouseClick;
  4.  
  5.     // 单例
  6.     static MouseManager m_instance = null;
  7.     static public MouseManager GetInstance()
  8.     {
  9.         return m_instance;
  10.     }
  11.     void Awake()
  12.     {
  13.         if (m_instance != null)
  14.             Destroy(gameObject);
  15.  
  16.         m_instance = this;
  17.     }
  18. }

如代码清单 1 所示,我们设置静态的单例类,并设置静态的 GetInstance() 函数用于访问。在 Awake 周期,我们对单例类进行赋值,并保持唯一性。

这边保持单例唯一的逻辑看着怪怪的。不清楚 gameObject 指代什么,先留作问题。

1.1 注册事件

我们新建名叫 PlayerController 的 C# 脚本,并挂载在人物对象上,用于注册人物的鼠标移动事件。

代码清单 2 PlayerController 类
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.AI;
  5. public class PlayerController : MonoBehaviour
  6. {
  7.     NavMeshAgent m_navMeshAgent;
  8.  
  9.     void MoveToTarget(Vector3 destination)
  10.     {
  11.         m_navMeshAgent.destination = destination;
  12.     }
  13.  
  14.     void Awake()
  15.     {
  16.         m_navMeshAgent = GetComponent<NavMeshAgent>();
  17.     }
  18.  
  19.     // Start is called before the first frame update
  20.     void Start()
  21.     {
  22.         MouseManager.GetInstance().OnMouseClick += MoveToTarget;
  23.     }
  24.  
  25.     // Update is called once per frame
  26.     void Update()
  27.     {
  28.        
  29.     }
  30. }

如代码清单 2 所示,在 Awake 生命周期获取人物的 Nav Mesh Agent 组件。MoveToTarget 就是需要注册的事件,它设置 destination 属性,实现移动。在 Start 生命周期我们可以直接访问单例类 MouseManager,来注册想要的事件。

至此,原先的鼠标控制人物移动的功能就“重写”完成了。

2. 设置鼠标指针

我们想实现的功能是:鼠标移动之处,如果是地面,鼠标指针显示一种样式;如果是怪物,指针显示另一种样式;以及其他不同的对象,鼠标指针都会随着指示变化。

鼠标设置可以通过 Cursor.SetCursor 函数实现。其函数原型为:

  • void SetCursor(Texture2D texture, Vector2 hotspot, CursorMode cursorMode);

最主要的是第一个 2D 纹理贴图用于指定鼠标的图片。后面参数手册上可以看到,比如 hotspot 为用作目标点的从左上角开始的纹理偏移。

2.1 设置纹理贴图

我们首先挑选一张合适的样式图片,然后拖拽到 Assets 界面。如图 1 所示,我们可以对图片进行设置。Texture Type 设置为 Cursor;Max Size 设置为 32,防止原图片尺寸过大;最后点击 Apply 生效设置。

图1 图片设置

接着我们在之前的 MouseManager 类添加一个 2D 纹理公开变量:

  • public Texture2D m_textureMove;

然后如图 2 所示,将之前设置好的图片拖拽到相应变量上就可以了,非常方便。

图2 指定纹理

2.2 实现功能

当设置好鼠标相关的纹理贴图后,就可以开始实现鼠标样式改变的功能了。之前鼠标控制人物移动的步骤是,先判断鼠标是否进行过点击,然后再发出射线进行判断。但是现在鼠标指针样式的更改需要实时进行设置,因此需要对原先的逻辑进行一些修改。我们需要实时的发出射线进行检测。

如代码清单 3 所示,我们修改了 Update 生命周期中的逻辑(第 51 至 60 行)。这时候需要每次都发射射线(第 53 行),如果有物体碰撞再进行后续设置。SetCursorTexture() 函数中依据碰撞的内容来设置鼠标样式(第 38 行)。DoMouseEvent() 函数中进行原先控制移动的逻辑,如果点击了左键,则控制人物移动。

代码清单 3 鼠标指针设置
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5.  
  6. public class MouseManager : MonoBehaviour
  7. {
  8.     public event Action<Vector3> OnMouseClick;
  9.  
  10.     // 鼠标纹理
  11.     public Texture2D m_textureMove;
  12.  
  13.     // 单例
  14.     static MouseManager m_instance = null;
  15.     static public MouseManager GetInstance()
  16.     {
  17.         return m_instance;
  18.     }
  19.     void Awake()
  20.     {
  21.         if (m_instance != null)
  22.             Destroy(gameObject);
  23.  
  24.         m_instance = this;
  25.     }
  26.  
  27.     //Camera camera;
  28.     // Start is called before the first frame update
  29.     void Start()
  30.     {
  31.  
  32.     }
  33.  
  34.     public void SetCursorTexture(RaycastHit raycastHit)
  35.     {
  36.         string tag = raycastHit.collider.gameObject.tag;
  37.         if (tag == "Ground")
  38.             Cursor.SetCursor(m_textureMove, new Vector2(16, 16), CursorMode.Auto);
  39.     }
  40.  
  41.     public void DoMouseEvent(RaycastHit raycastHit)
  42.     {
  43.         if (Input.GetMouseButtonDown(0))
  44.         {
  45.             if (raycastHit.collider.gameObject.CompareTag("Ground"))
  46.                 OnMouseClick.Invoke(raycastHit.point);
  47.         }
  48.     }
  49.  
  50.     // Update is called once per frame
  51.     void Update()
  52.     {
  53.         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  54.         RaycastHit raycastHit;
  55.         if (Physics.Raycast(ray, out raycastHit))
  56.         {
  57.             SetCursorTexture(raycastHit);
  58.             DoMouseEvent(raycastHit);
  59.         }
  60.     }
  61. }

最终的效果如图 3 所示,可以看到鼠标指针被设置成了想要的样式。

图3 鼠标效果