Unity-自定义事件派发器的两次尝试
🚀 优质资源分享 🚀
| 学习路线指引(点击解锁) | 知识定位 | 人群定位 |
|---|---|---|
| 🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
| 💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
一、前言:
在游戏开发的很多时候,需要引用其他类的方法,但是一旦类多起来了,相互引用会导致引用关系混乱,极其难以阅读。
以前初次做抖音小游戏时,和一位经验老道的cocos程序员合作,看到我写的代码他不禁皱起眉头,说我的引用关系太乱了,看不懂,但是他又不知道unity的事件派发器怎么写,就去网上找了一个。简直惊艳到我了。后来在现在公司做,又见到了一种事件派发器,于是心生感慨,模仿写了一个,并写博客记录一下。
二、现在做的事件派发器
1.声明对应的委托,此委托主要为事件用的。委托的所有返回类型都为Void,简化派发器的复杂程度;明确委托的方法参数类型,有几种类型就定义几种委托。
public delegate void VoidDelegate();
public delegate void BoolDelegate(params bool[] parameters);
public delegate void NumberDelegate(params float[] parameters);
public delegate void GameObjectDelegate(params GameObject[] parameters);
2.存储容器,用于存储事件
private static List GameStart\_List;
3.监听者,暴露给外部调用者的接口,对监听者的+=或-=对应容器里的Add和Remove
public static event VoidDelegate GameStart\_Listener{add{if(value != null){if(GameStart\_List == null){GameStart\_List = new List(1);}GameStart\_List.Add(value);}}remove{if(value != null){for(int i = 0;i){if(GameStart\_List[i] != null && GameStart\_List[i].Equals(value)){GameStart\_List.RemoveAt(i);break;}}}}}
4.派发者,因为event只能在声明类内部Invoke,所以需要暴露给外部调用者接口
public static void GameStart\_Dispatch(){if(GameStart\_List == null || GameStart\_List.Count <= 0){return;}for(int i = 0;i < GameStart\_List.Count;i++){GameStart\_List[i]?.Invoke();}}
5.上文可看见我的事件叫GameStart,那么我想新增一个GameEnd的事件,岂不是又要写一遍?而且假如我的方法参数是float呢?是bool呢?是GameObject呢?岂不是改动很大?所以我在Unity做了一个自动生成事件的工具
5.1 定义ScriptObject作为配置文件,可随时修改以添加或者删除事件
[CreateAssetMenu]
public class EventHandlerSetting:ScriptableObject
{public List types;public List items;
}[System.Serializable]
public class EventItem
{public string eventName;public string typeName;
}[System.Serializable]
public class EventType
{public string typeName;public string typeDelegate;
}

5.2 自动生成脚本工具


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;public class CodeGen
{private static string SettingPath = @"EventHandleSetting";private static EventHandlerSetting SettingObj;private static string CodePath = @"Assets\EventHandler\";private static string CodeName = "EventHandler.cs";private static string LineFeed = "\r\n";private static string LineTable = "\t";[MenuItem("Tools/EventCodeGen")]public static void CreateEventCode(){if (File.Exists(CodePath+CodeName)){File.Delete(CodePath + CodeName);}File.Create(CodePath + CodeName).Dispose();LoadSetting();File.AppendAllText(CodePath + CodeName, CreateNamespace());File.AppendAllText(CodePath + CodeName, CreateDelegate());File.AppendAllText(CodePath + CodeName, CreateClass());AssetDatabase.Refresh();}/// /// 加载配置文件/// static void LoadSetting(){SettingObj = Resources.Load(SettingPath);}static string CreateNamespace(){StringBuilder result = new StringBuilder();result.Append("using System;" + LineFeed);result.Append("using System.Collections;" + LineFeed);result.Append("using System.Collections.Generic;" + LineFeed);result.Append("using UnityEngine;" + LineFeed+LineFeed);return result.ToString();}/// /// 创建委托类型/// /// static string CreateDelegate(){StringBuilder result = new StringBuilder();string commnPrefix = "public delegate void ";using (var e = SettingObj.types.GetEnumerator()){while (e.MoveNext()){result.Append(commnPrefix + e.Current.typeDelegate + ";" + LineFeed);}}result.Append(LineFeed);return result.ToString();}/// /// 创建EventHandler类/// /// static string CreateClass(){StringBuilder result = new StringBuilder();result.Append("public class EventHandler"+LineFeed);result.Append("{"+LineFeed);List ls = SettingObj.items;for (int i = 0; i < ls.Count; i++){string eventName = ls[i].eventName;string eventType = ls[i].typeName;result.Append(CreateEvent(eventName, eventType));}result.Append("}");return result.ToString();}/// /// 创建事件派发器/// /// /// /// static string CreateEvent(string eventName,string eventType){StringBuilder result = new StringBuilder();result.Append(LineFeed + "#region " + eventName + LineFeed);result.Append(MutiLineTable(1) + string.Format("private static List<{0}> {1}\_List;", eventType + "Delegate", eventName) + LineFeed);//创建Listresult.Append(CreateListener(eventName, eventType));result.Append(CreateDispatch(eventName, eventType));result.Append("#endregion"+LineFeed);return result.ToString();}/// /// 创建Listener/// /// /// /// static string CreateListener(string eventName,string eventType){StringBuilder result = new StringBuilder();result.Append(MutiLineTable(1) + string.Format("public static event {0} {1}\_Listener", eventType + "Delegate", eventName) + LineFeed);result.Append(MutiLineTable(1) + "{" + LineFeed);#region addresult.Append(MutiLineTable(2) + "add" + LineFeed);result.Append(MutiLineTable(2) + "{" + LineFeed);result.Append(MutiLineTable(3) + "if(value != null)" + LineFeed);result.Append(MutiLineTable(3) + "{" + LineFeed);result.Append(MutiLineTable(4) + string.Format("if({0}\_List == null)", eventName) + LineFeed);result.Append(MutiLineTable(4) + "{" + LineFeed);result.Append(MutiLineTable(5) + string.Format("{0}\_List = new List<{1}>(1);", eventName, eventType + "Delegate")+LineFeed);result.Append(MutiLineTable(4) + "}" + LineFeed);result.Append(MutiLineTable(4) + string.Format("{0}\_List.Add(value);", eventName) + LineFeed);result.Append(MutiLineTable(3) + "}" + LineFeed);result.Append(MutiLineTable(2) + "}" + LineFeed);#endregion#region removeresult.Append(MutiLineTable(2) + "remove" + LineFeed);result.Append(MutiLineTable(2) + "{" + LineFeed);result.Append(MutiLineTable(3) + "if(value != null)" + LineFeed);result.Append(MutiLineTable(3) + "{" + LineFeed);result.Append(MutiLineTable(4) + string.Format("for(int i = 0;i<{0}\_List.Count;i++)", eventName) + LineFeed);result.Append(MutiLineTable(4) + "{" + LineFeed);result.Append(MutiLineTable(5) + string.Format("if({0}\_List[i] != null && {1}\_List[i].Equals(value))",eventName,eventName) + LineFeed);result.Append(MutiLineTable(5) + "{" + LineFeed);result.Append(MutiLineTable(6) + string.Format("{0}\_List.RemoveAt(i);" ,eventName) + LineFeed);result.Append(MutiLineTable(6) + "break;" + LineFeed);result.Append(MutiLineTable(5) + "}" + LineFeed);result.Append(MutiLineTable(4) + "}" + LineFeed);result.Append(MutiLineTable(3) + "}" + LineFeed);result.Append(MutiLineTable(2) + "}" + LineFeed);#endregionresult.Append(MutiLineTable(1) + "}" + LineFeed);return result.ToString();}/// /// 创建Dispatch/// /// /// /// static string CreateDispatch(string eventName,string eventType){StringBuilder result = new StringBuilder();result.Append(MutiLineTable(1) + string.Format("public static void {0}\_Dispatch({1})", eventName, GetTypeParameter(eventType)) + LineFeed);result.Append(MutiLineTable(1) + "{" + LineFeed);result.Append(MutiLineTable(2) + string.Format("if({0}\_List == null || {1}\_List.Count <= 0)", eventName, eventName) + LineFeed);result.Append(MutiLineTable(2) + "{" + LineFeed);result.Append(MutiLineTable(3) + "return;" + LineFeed);result.Append(MutiLineTable(2) + "}" + LineFeed);result.Append(MutiLineTable(2) + string.Format("for(int i = 0;i < {0}\_List.Count;i++)", eventName) + LineFeed);result.Append(MutiLineTable(2) + "{" + LineFeed);if (!string.IsNullOrEmpty(GetTypeParameter(eventType))){result.Append(MutiLineTable(3) + string.Format("{0}\_List[i]?.Invoke(parameters[i]);", eventName) + LineFeed);}else{result.Append(MutiLineTable(3) + string.Format("{0}\_List[i]?.Invoke();", eventName) + LineFeed);}result.Append(MutiLineTable(2) + "}" + LineFeed);result.Append(MutiLineTable(1) + "}" + LineFeed);return result.ToString();}/// /// 获取字符串中括号中的内容/// /// /// static string GetTypeParameter(string typeName){if (string.IsNullOrEmpty(typeName)){return string.Empty;}using (var e=SettingObj.types.GetEnumerator()){while (e.MoveNext()){if (e.Current.typeName==typeName){string @delegate = e.Current.typeDelegate;string result = @delegate.Substring(@delegate.IndexOf("(") + 1, @delegate.IndexOf(")") - (@delegate.IndexOf("(") + 1));Debug.Log(result);return result;}}}return string.Empty;}/// /// 多个table/// /// /// static string MutiLineTable(int count){StringBuilder result = new StringBuilder();for (int i = 0; i < count; i++){result.Append(LineTable);}return result.ToString() ;}
}
View Code

6.总结
当然,我的这一套事件系统肯定还是有问题的。如果方法参数不止一个float呢?第二个参数是bool?组合起来呢?还有一个问题是,配置文件不够人性化,全部都是字符串,假如多大一个空格或者标点就废了。还有一个非常严重的问题,如果生成的脚本,在语法上有错,不能通过编译器,Unity就会报错,再次点击生成就不会生效。我认识的一个主程让我在Unity外部生成,不要依赖Unity,且使用类似Lua、Python这种脚本语言,目前还不会哈哈哈哈。
三、以前做的事件派发器
1.需要一个通用的参数类型,叫EventArgs,基础类型为Systen.Object
public class EventArgs
{private List parameters;public int Count{get{if (parameters!=null){return parameters.Count;}else{Debug.Log("parameters is not init");return 0;}}}public EventArgs(params System.Object[] parameters){if (this.parameters==null){this.parameters = new List
2.还是那句老话,事件派发器需要容器、监听、派发三部分。
public class EventDispatcher
{public delegate void Listener(EventArgs args);private static Dictionary cacheEvents = new Dictionary();public static void Attach(string tag,Listener listen){if (cacheEvents==null){cacheEvents = new Dictionary();}if (cacheEvents.ContainsKey(tag)){Debug.LogWarning("this tag already exsit in cache,please check agin,tag name:" + tag);return;}if (listen==null){Debug.LogWarning("listen is null,cache failed");return;}cacheEvents.Add(tag, listen);}public static void Detach(string tag){if (!cacheEvents.ContainsKey(tag)){Debug.LogWarning("tag is not exsit in cache,tag name:"+tag);return;}cacheEvents.Remove(tag);}public static void Dispatch(string tag,EventArgs args){if (!cacheEvents.ContainsKey(tag)){Debug.LogWarning("this tag does not exsit in cache,please check agin,tag name:" + tag);return;}if (cacheEvents[tag]==null){Debug.LogWarning("this listen is null,invoke failed");return;}cacheEvents[tag].Invoke(args);}
}
3.总结:
此事件派发器也存在缺陷,任何方法的参数类型都会被转换成System.Object类型,有多余的封装箱操作。且不能重复添加一个方法,至少他的tag不能一样。代码可读性差,报错了都不知道在哪儿。
四、关于事件派发器自己的看法
我相信没有完美的派发器这一说,好的派发器与坏的派发器区别在于,调用是否方便?会不会存在隐藏的危险bug?性能上如何?不同项目有不同的事件派发器,适合自己的才是最好的。如果强行将事件派发器做成那种万金油工具,且不论他是否真的是万金油,代码开发成本之大,耗费时间之长,也不是一般小游戏公司能够耗得起的。上文所述两个派发器,实际上都可以用,而且经历过实战的,并没有什么大问题。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
