[C#] 通过C#使用Advanced CSharp Messenger

Advanced CSharp Messenger 属于C#事件的一种。 维基百科中由详细的说明http://wiki.unity3d.com/index.php?title=Advanced_CSharp_Messenger上周的一天刚巧有朋友问到我这一块的知识,那么我研究出来将它贴在博客中,帮助了他也帮助我自己!哇咔咔。

Advanced CSharp Messenger的特点可以将游戏对象做为参数发送。到底Advanced CSharp Messenger有什么用呢?先创建一个立方体对象,然后把Script脚本绑定在这个对象中。脚本中有一个方法叫DoSomething()。写一段简单的代码,通常我们在调用方法的时候需要这样来写。

C#

private Script script;

void Awake()

{

GameObject cube = GameObject.Find("Cube");

script = cube.GetComponent<Script>();

}


void Update()

{

if(Input.GetMouseButtonDown(0))

{

script.DoSomething();

}

}


代码比较简单,我就不注释了。 原理就是先获取游戏对象,接着获取脚本组件对象,最后通过脚本组件对象去调用对应脚本中的方法,这样的调用方法我们称之为直接调用。

这个例子中我只调用了一个对象的方法,如果说有成千上万个对象,那么这样调用是不是感觉自己的代码非常的丑?因为你需要一个一个的获取对象然后获取脚本组件然后在调用方法。。。。。 (想想都恐怖!!)

下面我们在用Advanced CSharp Messenger来实现事件的调用。按照维基百科中首先把Message.cs 和Callback.cs拷贝在你的工程中。

CallBack.cs

C#

public delegate void Callback();

public delegate void Callback<T>(T arg1);

public delegate void Callback<T, U>(T arg1, U arg2);

public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3);

 Message.cs

C#

/*

* Advanced C# messenger by Ilya Suzdalnitski. V1.0

*

* Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger Extended".

*

* Features:

* Prevents a MissingReferenceException because of a reference to a destroyed message handler.

* Option to log all messages

* Extensive error detection, preventing silent bugs

*

* Usage examples:

1. Messenger.AddListener<GameObject>("prop collected", PropCollected);

   Messenger.Broadcast<GameObject>("prop collected", prop);

2. Messenger.AddListener<float>("speed changed", SpeedChanged);

   Messenger.Broadcast<float>("speed changed", 0.5f);

*

* Messenger cleans up its evenTable automatically upon loading of a new level.

*

* Don't forget that the messages that should survive the cleanup, should be marked with Messenger.MarkAsPermanent(string)

*

*/


//#define LOG_ALL_MESSAGES

//#define LOG_ADD_LISTENER

//#define LOG_BROADCAST_MESSAGE

#define REQUIRE_LISTENER


using System;

using System.Collections.Generic;

using UnityEngine;


static internal class Messenger {

#region Internal variables


//Disable the unused variable warning

#pragma warning disable 0414

//Ensures that the MessengerHelper will be created automatically upon start of the game.

static private MessengerHelper messengerHelper = ( new GameObject("MessengerHelper") ).AddComponent< MessengerHelper >();

#pragma warning restore 0414


static public Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();


//Message handlers that should never be removed, regardless of calling Cleanup

static public List< string > permanentMessages = new List< string > ();

#endregion

#region Helper methods

//Marks a certain message as permanent.

static public void MarkAsPermanent(string eventType) {

#if LOG_ALL_MESSAGES

Debug.Log("Messenger MarkAsPermanent \t\"" + eventType + "\"");

#endif


permanentMessages.Add( eventType );

}


static public void Cleanup()

{

#if LOG_ALL_MESSAGES

Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed.");

#endif


List< string > messagesToRemove = new List<string>();


foreach (KeyValuePair<string, Delegate> pair in eventTable) {

bool wasFound = false;


foreach (string message in permanentMessages) {

if (pair.Key == message) {

wasFound = true;

break;

}

}


if (!wasFound)

messagesToRemove.Add( pair.Key );

}


foreach (string message in messagesToRemove) {

eventTable.Remove( message );

}

}


static public void PrintEventTable()

{

Debug.Log("\t\t\t=== MESSENGER PrintEventTable ===");


foreach (KeyValuePair<string, Delegate> pair in eventTable) {

Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value);

}


Debug.Log("\n");

}

#endregion


#region Message logging and exception throwing

    static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded) {

#if LOG_ALL_MESSAGES || LOG_ADD_LISTENER

Debug.Log("MESSENGER OnListenerAdding \t\"" + eventType + "\"\t{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}");

#endif


        if (!eventTable.ContainsKey(eventType)) {

            eventTable.Add(eventType, null );

        }


        Delegate d = eventTable[eventType];

        if (d != null && d.GetType() != listenerBeingAdded.GetType()) {

            throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));

        }

    }


    static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved) {

#if LOG_ALL_MESSAGES

Debug.Log("MESSENGER OnListenerRemoving \t\"" + eventType + "\"\t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}");

#endif


        if (eventTable.ContainsKey(eventType)) {

            Delegate d = eventTable[eventType];


            if (d == null) {

                throw new ListenerException(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType));

            } else if (d.GetType() != listenerBeingRemoved.GetType()) {

                throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));

            }

        } else {

            throw new ListenerException(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType));

        }

    }


    static public void OnListenerRemoved(string eventType) {

        if (eventTable[eventType] == null) {

            eventTable.Remove(eventType);

        }

    }


    static public void OnBroadcasting(string eventType) {

#if REQUIRE_LISTENER

        if (!eventTable.ContainsKey(eventType)) {

            throw new BroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", eventType));

        }

#endif

    }


    static public BroadcastException CreateBroadcastSignatureException(string eventType) {

        return new BroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));

    }


    public class BroadcastException : Exception {

        public BroadcastException(string msg)

            : base(msg) {

        }

    }


    public class ListenerException : Exception {

        public ListenerException(string msg)

            : base(msg) {

        }

    }

#endregion


#region AddListener

//No parameters

    static public void AddListener(string eventType, Callback handler) {

        OnListenerAdding(eventType, handler);

        eventTable[eventType] = (Callback)eventTable[eventType] + handler;

    }


//Single parameter

static public void AddListener<T>(string eventType, Callback<T> handler) {

        OnListenerAdding(eventType, handler);

        eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;

    }


//Two parameters

static public void AddListener<T, U>(string eventType, Callback<T, U> handler) {

        OnListenerAdding(eventType, handler);

        eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler;

    }


//Three parameters

static public void AddListener<T, U, V>(string eventType, Callback<T, U, V> handler) {

        OnListenerAdding(eventType, handler);

        eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler;

    }

#endregion


#region RemoveListener

//No parameters

    static public void RemoveListener(string eventType, Callback handler) {

        OnListenerRemoving(eventType, handler);  

        eventTable[eventType] = (Callback)eventTable[eventType] - handler;

        OnListenerRemoved(eventType);

    }


//Single parameter

static public void RemoveListener<T>(string eventType, Callback<T> handler) {

        OnListenerRemoving(eventType, handler);

        eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;

        OnListenerRemoved(eventType);

    }


//Two parameters

static public void RemoveListener<T, U>(string eventType, Callback<T, U> handler) {

        OnListenerRemoving(eventType, handler);

        eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler;

        OnListenerRemoved(eventType);

    }


//Three parameters

static public void RemoveListener<T, U, V>(string eventType, Callback<T, U, V> handler) {

        OnListenerRemoving(eventType, handler);

        eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler;

        OnListenerRemoved(eventType);

    }

#endregion


#region Broadcast

//No parameters

    static public void Broadcast(string eventType) {

#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE

Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");

#endif

        OnBroadcasting(eventType);


        Delegate d;

        if (eventTable.TryGetValue(eventType, out d)) {

            Callback callback = d as Callback;


            if (callback != null) {

                callback();

            } else {

                throw CreateBroadcastSignatureException(eventType);

            }

        }

    }


//Single parameter

    static public void Broadcast<T>(string eventType, T arg1) {

#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE

Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");

#endif

        OnBroadcasting(eventType);


        Delegate d;

        if (eventTable.TryGetValue(eventType, out d)) {

            Callback<T> callback = d as Callback<T>;


            if (callback != null) {

                callback(arg1);

            } else {

                throw CreateBroadcastSignatureException(eventType);

            }

        }

}


//Two parameters

    static public void Broadcast<T, U>(string eventType, T arg1, U arg2) {

#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE

Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");

#endif

        OnBroadcasting(eventType);


        Delegate d;

        if (eventTable.TryGetValue(eventType, out d)) {

            Callback<T, U> callback = d as Callback<T, U>;


            if (callback != null) {

                callback(arg1, arg2);

            } else {

                throw CreateBroadcastSignatureException(eventType);

            }

        }

    }


//Three parameters

    static public void Broadcast<T, U, V>(string eventType, T arg1, U arg2, V arg3) {

#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE

Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");

#endif

        OnBroadcasting(eventType);


        Delegate d;

        if (eventTable.TryGetValue(eventType, out d)) {

            Callback<T, U, V> callback = d as Callback<T, U, V>;


            if (callback != null) {

                callback(arg1, arg2, arg3);

            } else {

                throw CreateBroadcastSignatureException(eventType);

            }

        }

    }

#endregion

}


//This manager will ensure that the messenger's eventTable will be cleaned up upon loading of a new level.

public sealed class MessengerHelper : MonoBehaviour {

void Awake ()

{

DontDestroyOnLoad(gameObject);

}


//Clean up eventTable every time a new level loads.

public void OnDisable() {

Messenger.Cleanup();

}

}

 然后就可以开始使用了,Messager.Broadcast()这样就好比我们发送了一条广播。

C#

void Update()

{

if(Input.GetMouseButtonDown(0))

{

Messenger.Broadcast("Send");

}

}

 在需要这条广播的类中来接受它,同样是刚刚说的Script类。接受广播的标志是 Messager.AddListener()参数1表示广播的名称,参数2表示广播所调用的方法。

C#

using UnityEngine;

using System.Collections;


public class Script : MonoBehaviour {


void Awake()

{

Messenger.AddListener( "Send", DoSomething );

}

public void DoSomething()

{

Debug.Log("DoSomething");

}

}

 这样一来,只要发送名称为”Send”的方法,就可以在别的类中接收它了。

我们在说说如何通过广播来传递参数,这也是那天那个哥们主要问我的问题。(其实是维基百科上写的不是特别特别的清楚,那哥们误解了)在Callback中可以看出参数最多可以是三个,参数的类型是任意类型,也就是说我们不仅能传递 int float bool 还能传递gameObject类型。

如下所示,发送广播的时候传递了两个参数,参数1是一个游戏对象,参数2是一个int数值。

C#

void Update()

{

if(Input.GetMouseButtonDown(0))

{

GameObject cube = GameObject.Find("Cube");

Messenger.Broadcast<GameObject,int>("Send",cube,1980);

}

}

 然后是接受的地方 参数用<>存在一起。游戏对象也可以完美的传递。

C#

using UnityEngine;

using System.Collections;


public class Script : MonoBehaviour {


void Awake()

{

Messenger.AddListener<GameObject,int>( "Send", DoSomething );

}

public void DoSomething(GameObject obj,int i)

{

Debug.Log("name " + obj.name + " id =" + i);

}

}

如果传递一个参数<T>

两个参数<T,T>

三个参数<T,T,T>   

怎么样使用起来还是挺简单的吧?

我觉得项目中最好不要大量的使用代理事件这类的方法(根据需求而定),虽然可以让你的代码非常的简洁,但是它的效率不高大概比直接调用慢5-倍左右吧,就好比美好的东西一定都有瑕疵一样。 还记得Unity自身也提供了一种发送消息的方法吗?,用过的都知道效率也非常低下,虽然我们看不到它具体实现的源码是如何实现的,但是我觉得原理可能也是这样的。 欢迎和大家一起讨论与学习。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,332评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,930评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,204评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,348评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,356评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,447评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,862评论 3 394
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,516评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,710评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,518评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,582评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,295评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,848评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,881评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,121评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,737评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,280评论 2 341

推荐阅读更多精彩内容