参考文章:https://blog.csdn.net/sajiazaici/article/details/78702144 C#回调函数的简单讲解与应用例子
理解回调: https://blog.csdn.net/weixin_46879188/article/details/120164775
先看下面两个例子
1.基础的函数回调
public class delegateNoParameter : MonoBehaviour
{
public delegate void Work();
//定义委托-方法容器,并且定义执行方法
//让外部的类把方法填充进该类的这个方法来执行 当含参数的时候就定义对应参数的委托
public void oneDay(Work work)
{
//1.先做完己的事情,然后自动执行work
work();
}
}
public class excuteDelegate : MonoBehaviour
{
delegateNoParameter delegateCallback = new delegateNoParameter();
void OnEnable()
{
//调用我们的一天方法,先到委托类中把自己的事情做了,然后执行回调work
//这样不管今天是做什么样的work,只要切换不同的回调方法即可,都可以先把自己的事情做了
//在委托函数内我们定义如何使用回调函数,这里是先做自己的事情然后执行回调
//在下面回调实现加法的实现里就是把输入的两个数字执行回调的加法函数
delegateCallback.oneDay(CallbackLogic);
}
void CallbackLogic()
{
//2.work work
}
}
2.利用委托回调函数实现加法
public class _4_1DelegateCallback : MonoBehaviour
{
//2.加法回调 ------------------------------
//封装起来,只提供函数接口。相当于系统底层
public delegate int Calculate(int num1,int num2);
public int CalculateCallback(int num1, int num2, Calculate calculate)
{
return calculate(num1, num2);
}
}
public class _4_2DelegateCallbackExecution : MonoBehaviour
{
//
_4_1DelegateCallback delegateCallback = new _4_1DelegateCallback();
//2.加法回调执行 -------------
//用户层,执行输入等操作
void Start()
{
int sumNum= delegateCallback.CalculateCallback(1,2, GetSum);
Debug.Log("042-----------" + sumNum);
}
//开发层处理,开发人员编写具体的计算方法
int GetSum(int num1,int num2)
{
return num1 + num2;
}
}
对于一般的函数来说,我们自己编写的函数会在自己的程序内部调用,也就是说函数的编写方是我们自己,调用方也是我们自己。
但回调函数不是这样的,虽然函数编写方是我们自己,但是函数调用方不是我们,而是我们引用的其它模块,也就是第三方库,我们调用第三方库中的函数,并把回调函数传递给第三方库,第三方库中的函数调用我们编写的回调函数,如图所示
要给第三方库指定回调函数,是因为第三方库的编写者并不清楚在某些特定节点,比如我们举的例子油条制作完成、接收到网络数据、文件读取完成等之后该做什么,这些只有库的使用方才知道,因此第三方库的编写者无法针对具体的实现来写代码,而只能对外提供一个回调函数,库的使用方来实现该函数,第三方库在特定的节点调用该回调函数就可以了。
最后值得注意的一点就是回调函数被调用的时间节点,回调函数只在某些特定的节点被调用,就像上面说的油条制作完成、接收到网络数据、文件读取完成等,这些都是事件,也就是event,本质上我们编写的回调函数就是用来处理event的,因此从这个角度看回调函数不过就是eventhandler,因此回调函数天然适用于事件驱动编程event-driven。
3.利用事件回调机制实现根据数据更新外部UI展示信息
参考:https://www.itread01.com/content/1550158208.html
玩家模块类(定义)
玩家自身有名字
定义添加监听和事件的触发(必须在一个类中)
其实玩家类更像主控类,玩家类发布改名事件,控制类(观察者)添加监听并且在触发方法内调用面板方法更改显示
public class PlayerModel : MonoBehaviour
{
string Name;
public string playerName
{
set { Name = value; }
get { return Name; }
}
//定义改名(更新)事件
event UnityAction<PlayerModel> updateEvent;
public void AddEventListener(UnityAction<PlayerModel> function)
{
updateEvent += function;
}
//事件执行方法
public void ChangeName()
{
updateEvent(this);
}
private void Start()
{
playerName = "PlayerName";
Debug.Log("改名触发");
ChangeName();
Debug.Log("回调结束");
}
}
控制器类(执行)事件管理类
添加对玩家改名事件的监听,在知道玩家改名后执行对应方法
只留意触发的判断,并不关心玩家和面板的属性,同时玩家和面板之间也不需要互相了解。
public class Controller : MonoBehaviour
{
PlayerModel model;
MainView view;
private void Start()
{
//中间类添加监听 订阅改名(更新)事件
model.AddEventListener(updateInfo);
}
//
public void updateInfo(PlayerModel data)
{
//事件触发后 调用面板的方法,把传来的名字作为参数再传给面板类
Debug.Log("开始改名");
view.updateInfo(data);
Debug.Log("已经改完名字了");
}
}
外部展示面板类(开发)
面板内有命名信息
public class MainView : MonoBehaviour
{
public void updateInfo(PlayerModel data)
{
Debug.Log(data.playerName);
}
}
4.总结
委托回调中,定义委托的类更像是从属,定义了在回调前自己这个object想要干什,做完自己的内容后自动执行主逻辑传来的方法(回调方法)
主逻辑(调用端)调用委托类的执行方法,在委托类做完它的事后才会执行回调,因此在执行我们的回调方法时我们就知道委托类的方法已经执行完了。
在事件机制中(观察者模式),事件定义端更像是一个主控制器,定义了外部添加监听的方法,和触发事件的方法。
事件观察者(监听者)添加监听后,当事件触发自动执行相应的方法。一个事件可以有多个监听者;一个监听者也可以添加多个监听(如果想知道是哪个对象触发事件,需要在委托定义阶段需要把事件定义端作为对象传输过来进行判断)
更新:上面的理解并没有理解委托和事件,现在的理解委托和事件是为了获取场景对象的引用即用来对象间的消息通信。
在做内容的过程中会发现会发现我们手动实例的物体对象很难与unity已经在场景内的物体进行数据传输,例如实例的物体想控制场景内的面板、获取scene内的Gameobject挂挂载的某一脚本。因为unity物体在场景中生成时,该物体及其脚本已经实例了具体的对象(Gameobject obj = new Gameobject),我们在其他脚本中拿到该物体时已经是一个另外一个新的对象了,并没有拿到场景中该物体的引用。
利用继承来获取父类中的数据或物体对象也是不行的 - 父类只要继承了mono,在场景中挂载或生成时就已经成为一个单独的对象了。
此时要获取其他物体对象的引用,就需要用到委托和事件。
如果监视面板内物体存在父子物体关系使用 TargetGgameobject.SendMessage();
TargetGgameobject.SendMessage(); // 向自身物体的脚本发送消息
TargetGgameobject.SendMessageUpwards(); // 向自身或父物体的脚本发送消息
TargetGgameobject.BroadcaseMessage(); // 向自身或父物体的脚本发送消息
public class B : MonoBehaviour {
void Start ()
{
GameObject.Find("TargetName").SendMessage("Show_number","10");
}