Unity事件,委托
最主要的作用是消息传递,倒无所谓其他什么代码效率之类的考虑,遇到什么监听的情况,比如鼠标有没有单击,某一个敌人是不是死亡,摄像机是不是还在启动这些事,还是得靠update去拿每一帧检测。
既然是消息传递,必然涉及两个类或者说是两个C#脚本。
具体使用方法: 先定义一个委托 public event Action<string> onCameraPositionChanged;
有人要问了委托不是delegate吗,这个event Action是什么?
Action
是一个预定义的委托类型,它可以表示一个没有参数和返回值的方法。
event
具有一些特定的限制和语法要求,使其更安全、封装和易于使用。
event
只能在类的内部定义和触发,不能在外部直接访问或触发。event
不能被继承类重写,是只读的,无法直接赋值或清空。event
的触发方法只能使用+=
和-=
运算符进行订阅和取消订阅,不能直接调用。
以上三条大概看看就行了,真正写代码的时候写错了编译器会提醒你的
public event Action<string> onCameraPositionChanged;
public const int a;
所以这句代码的意思是,onCameraPositionChanged是一个event,类似const,static这样的关键字,限定了onCameraPositionChanged的一些功能。onCameraPositionChanged的具体类型是一个事件Action。对比一下int a的定义就完全理解了。
如果不是在Unity里,是完全C#编码的话,也经常能在其他博客里看到以下写法
public event delegate onCameraPositionChanged(string);
event加上就是一个有限制的委托,event不加上就是一个纯自定义的委托。
好,现在拐回去,说消息传递的事。
这东西最主要的作用通过订阅在两个C#文件之间进行消息传递。一定要记住用的这个东西了肯定是两个文件之间的事了,两个文件!一个文件也行,一个文件里那就有两个类,不过这样印象不深刻。
----------------------------.cs文件1-----------------------
----------------------------.cs文件1-----------------------
----------------------------.cs文件1-----------------------
using UnityEngine;
using System;
public class 脚本1: MonoBehaviour
{
public Camera camera;
public event Action<bool> onCameraPositionChanged; // 声明一个带参数的事件委托
private void update()
{
Vector3 cameraPosition = camera.transform.position;
if (cameraPosition == new Vector3(1f, 0f, 0f))
{
// 触发事件并传递参数
onCameraPositionChanged?.Invoke(true);
}
}
}
好,在文件1里面把委托都定义好了,现在再去文件2里操作
onCameraPositionChanged?.Invoke(true);简介一下这句代码的意思,?是自动判空,这里判空不是判onCameraPositionChanged本身是不是空,是判断有没有函数订阅这个委托,如果有才执行Invoke,执行Invoke的意思就是正式的执行这个委托了,正式的执行这个委托了会发生什么在文件2里会说明。括号里面自然是普通的传参,毕竟之前定义的Action就有bool的参数。
--------------------------.cs文件2-----------------------------
--------------------------.cs文件2-----------------------------
--------------------------.cs文件2-----------------------------
using UnityEngine;
public class 脚本2: MonoBehaviour
{
private void Start()
{
脚本1 positionChecker = GetComponent<脚本1>();
positionChecker.onCameraPositionChanged += HandleCameraPositionChanged;
}
private void HandleCameraPositionChanged(bool flag)
{
if (flag==true)
{
Debug.Log("Camera position is (1, 0, 0).");
// 在这里执行其他需要的操作
}
}
}
好,现在文件2也写好了。在文件2里面的Start()里面我们先get到了脚本1
positionChecker.onCameraPositionChanged += HandleCameraPositionChanged;
然后把脚本1的委托注册了一个函数叫HandleCameraPositionChanged。函数HandleCameraPositionChanged里面的内容就是我们真正要做的事情。
这样文件1里正式执行委托时,也就是Invoke时,真正执行的是函数HandleCameraPositionChanged或者其他一切注册到委托OnCameraPositionChanged上的函数。此时完成了消息传递的效果。
那么好了好了,你问哪里完成消息传递的效果了?这有两层意思
第一层是脚本2中的函数由脚本1来控制具体执行的时机了,通过注册这个机制,脚本1可以同时控制很多其他脚本里函数的执行了,只需要onCameraPositionChanged +=就行了
第二层是Action<bool>的参数传递过去了,函数参数可以携带很多信息。
下面是题外话,加深理解的话
可以想一想如果不搞这么复杂,又是这个又那个的,我不就是要看一下当前相机位置是不是在(1f,0f,0f)位置吗。
我在文件1里面新增一个bool flag,然后每次更新flag的值
反正我在文件2里面都已经get到脚本1了,GetComponent这一步也没少啊
直接用
if(脚本1.flag)
不也一样。我觉得也一样,所以我没怎么用过委托,我能想到的就是如果这个flag必须是private的时候就需要用这个办法了,其他的角度那只能是考虑到代码更灵活、可扩展和可维护这些内容了,Unity学多的人可能爱用这个东西吧,反正我用的少。