unity 反射實現簡易廣播系統(腳本通信)

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版本的,用法上面更加簡易,不需要主動訂閱

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章