unity自帶消息通信功能SendMessage效率比較低(當場景中的對象和腳本組件比較多的時候),在幀裏面調用更是災難的.
試了c#的各種delegate, invoke,效果不好.
最終通過C#反射的方式寫了一個訂閱廣播消息系統.下面講解一下原理
1.反射:
看這裏 https://www.cnblogs.com/Stephenchao/p/4481995.html
2.觀察者模式:
看這裏 https://www.cnblogs.com/zhili/p/ObserverPattern.html
運行流程
1.通過反射的方式獲取到對象的MethodInfo
2.將對象和MethodInfo封裝成observer整潔點,打包到list中,再放到一個Dictionary中備用
3.發送消息的時候從Dictionary獲取到observer列表,反射調用即可,相信Dictionary的查詢效率應該很高
在幾個小項目裏面試了一下,效果還不錯,對於不怎麼使用puremvc和ECS,喜歡簡單的同學來說還是比較適合的,直接上代碼吧
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
public class MiniBroadCast {
public static MiniBroadCast Instance = new MiniBroadCast();
private Dictionary<string, List<Observer>> m_method_info = new Dictionary<string, List<Observer>>();
/**
* 註冊觀察者
*/
public void registListener(string key,string method_name,object instance)
{
if (!m_method_info.ContainsKey(key))
{
m_method_info[key] = new List<Observer>();
}
MethodInfo method = instance.GetType().GetMethod(method_name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
Observer _observer = new Observer();
_observer.method = method;
_observer.instance = instance;
m_method_info[key].Add(_observer);
//Debug.Log("regist observer success: instance:" + instance + " method:" + method);
}
/**
* 註冊觀察者
* 靜態
*/
public void registListener(string key, string method_name, Type type)
{
if (!m_method_info.ContainsKey(key))
{
m_method_info[key] = new List<Observer>();
}
MethodInfo method = type.GetMethod(method_name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
Observer _observer = new Observer();
_observer.method = method;
_observer.instance = null;
m_method_info[key].Add(_observer);
}
/**
* p_params:不定參數
*/
public void breadcast(string key,params object[] p_params)
{
//Debug.Log(key);
if (!this.m_method_info.ContainsKey(key))
{
Debug.LogWarning("observer not find");
return;
}
//反射調用(包括靜態方法)
List<Observer> _list = new List<Observer>();
//深度複製一份,避免在反射執行過程中刪除觀察者出現錯誤
foreach (Observer o in this.m_method_info[key]){
_list.Add(o);
}
foreach (Observer o in _list)
{
o.method.Invoke(o.instance, p_params);
}
}
public void removeListener(string key, string method_name, object instance)
{
if (!m_method_info.ContainsKey(key))
return;
List<Observer> _list= this.m_method_info[key];
for (int i = _list.Count-1; i>=0; i--)
{
if (_list[i].instance == instance && _list[i].method.Name == method_name)
{
_list.RemoveAt(i);
Debug.Log("observer remove success!");
}
}
if (_list.Count == 0)
this.m_method_info.Remove(key);
}
public void cleanAll()
{
m_method_info = new Dictionary<string, List<Observer>>();
}
private class Observer
{
public object instance;
public MethodInfo method;
}
}
使用方法很簡單
1.直接放到項目下面
2.MiniBroadCast.Instance.registListener訂閱消息
3. MiniBroadCast.Instance.breadcast廣播消息
4. MiniBroadCast.Instance.removeListener取消訂閱
5. MiniBroadCast.Instance.cleanAll清除所有訂閱
github地址:
https://github.com/baixin1228/unity-breadcast-framwork
後續還會放出Attribute版本的,用法上面更加簡易,不需要主動訂閱