国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

【Unity3D技巧】在Unity中使用事件/委托機制(event/delegate)進行GameObject之間的通信

2019-11-17 03:06:25
字體:
來源:轉載
供稿:網友

【Unity3D技巧】在Unity中使用事件/委托機制(event/delegate)進行GameObject之間的通信

作者:王選易,出處:http://m.survivalescaperooms.com/neverdie/ 歡迎轉載,也請保留這段聲明。如果你喜歡這篇文章,請點【推薦】。謝謝!

QQ20140529123319_thumb1

引子

在前面兩篇文章:

  • 【Unity3D基礎教程】給初學者看的Unity教程(四):通過制作Flappy Bird了解Native 2D中的RigidBody2D和Collider2D
  • 【Unity3D基礎教程】給初學者看的Unity教程(三):通過制作Flappy Bird了解Native 2D中的SPRite,Animation

我們了解了2D中的Sprite,Animation,RigidBody和Collider,在繼續開發游戲的過程中,我們會遇到這樣的問題,如何處理GameObject之間的相互調用,比如說在FlappyBird中我們在小鳥撞倒管子的時候,要把這個消息通知給許多GameObject,管子接到這個消息之后需要停止運動,UI接到這個消息要彈出GameOver的字樣。接下來,我來講一下如何合理地解決這個問題。相關源碼參考:Flappy Bird的源碼。

為什么要進行GameObject之間的通訊?

在游戲開發中我們經常遇到這樣的問題,在游戲中發生了一個事件(event),我們如何把這個時間通知給其他GameObject:比如游戲中發生了爆炸,我們需要在一定范圍內的GameObject都感知到這一事件。有的時候,我們希望攝像機以外的物體不要接到我們這一事件的通知。游戲中豐富多彩的世界正是由通信機制構成的。

有一種方法是在發生事件的GameObject的Start方法里面把對該事件感興趣的所有GameObject當作成員變量保存在腳本組件里,那么我們把發生事件的object當作Subject,把對該事件感興趣的object當作Observer。

將Observer作為成員變量存儲在Subject中有一下缺點:

  • 難以變更,一旦要新增一個Observer就需要更改Subject中的代碼
  • 如果Observer被銷毀了,無法從Subject中移除掉這個成員變量,會發生NullReferernceException。
  • 在發生事件時,一個個去invoke不同Observer中的相應handle方法的代碼變得冗長繁雜。

還好的是,我們可以通過引入觀察者模式來解決這個問題,更好的是,C#內置有一個非常棒的事件/委托機制,能讓我們非常方便地進行觀察者模式的構建。

如果你還不了解觀察者模式和事件/委托機制,可以參考以下幾篇文章:

  • C# 中的委托和事件 -- 委托/事件入門
  • C#綜合揭秘——深入分析委托與事件 -- 委托/事件深入
  • 你可能不知道的陷阱:C#委托和事件的困惑 -- 委托/事件陷阱

C#中標準的委托類型

我們在構建事件/委托機制的時候,首先要定義委托類型,參考在Cocos2d-x中的CCCallback,我先定義了以下三種類型的委托:

// 該委托不傳任何參數public delegate void CallFunc();// 該委托會傳入發生事件的GameObject,即senderpublic delegate void CallFuncO(GameObject sender);// 該委托會傳入發生事件的GameObject,即sender。和一個變長參數列表public delegate void CallFuncOP(GameObject sender, EventArgs args);

但是我發現C#本身已經提供了一種比較好的委托類型:EventHandler,所以我就把游戲中的委托都替換成了這種委托。

public delegate void EventHandler(object sender, EventArgs e);

另一種更好的委托方式是使用泛型參數的委托類型:EventHandler<TEventArgs>,其簽名如下:

public delegate void EventHandler<TEventArgs>(    Object sender,    TEventArgs e)

采用 EventHandler 模式發布事件

如果這個事件不產生任何額外參數(即除了事件的發送者之外),則在在調用時,向EventHandler的第二個參數傳一個EventArgs.Empty即可。

如果產生額外參數,第二個參數是從 EventArgs 派生的類型并提供所有字段或屬性需要保存事件數據。使用 EventHandler<TEventArgs> 的優點在于,如果事件生成事件數據,則無需編寫自己的自定義委托代碼。

下面我們舉一個例子來證實EventHandler的用法:

using System;namespace Consoleapplication1{    class Program    {        static void Main(string[] args)        {            Counter c = new Counter(new Random().Next(10));            //向該事件添加了一個委托函數            c.ThresholdReached += c_ThresholdReached;            Console.WriteLine("press 'a' key to increase total");            while (Console.ReadKey(true).KeyChar == 'a')            {                Console.WriteLine("adding one");                c.Add(1);            }        }        static void c_ThresholdReached(object sender, ThresholdReachedEventArgs e)        {            Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold,  e.TimeReached);            Environment.Exit(0);        }    }    class Counter    {        private int threshold;        private int total;        public Counter(int passedThreshold)        {            threshold = passedThreshold;        }        public void Add(int x)        {            total += x;            if (total >= threshold)            {                ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();                args.Threshold = threshold;                args.TimeReached = DateTime.Now;                OnThresholdReached(args);            }        }        protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)        {            EventHandler<ThresholdReachedEventArgs> handler = ThresholdReached;            if (handler != null)            {                handler(this, e);            }        }        //添加了一個帶泛型參數的事件        public event EventHandler<ThresholdReachedEventArgs> ThresholdReached;    }    public class ThresholdReachedEventArgs : EventArgs    {        public int Threshold { get; set; }        public DateTime TimeReached { get; set; }    }}

在游戲中的應用

我們通過一個小鳥撞倒管子來作為事例說明如何進行通信:

bird1

在這個情景下,我們首先為小鳥設定兩個事件(event),分別是分數加一(ScoreAdd)和小鳥碰到管子游戲結束(GameOver) 如下:

using UnityEngine;using System.Collections;using System;public class BirdController : MonoBehaviour {    public event EventHandler GameOver;    public event EventHandler ScoreAdd;    //當離開Empty Trigger的時候,分發ScoreAdd事件    void OnTriggerExit2D(Collider2D col) {        if (col.gameObject.name.Equals("empty")) {            if (ScoreAdd != null)                ScoreAdd(this, EventArgs.Empty);        }    }    //當開始碰撞的時候,分發GameOver事件    void OnCollisionEnter2D(Collision2D col)    {        rigidbody2D.velocity = new Vector2(0, 0);        if (GameOver != null)            GameOver(this, EventArgs.Empty);        this.enabled = false;    }}

然后在對這個事件感興趣的GameObject會通過相應的Handler對該事件進行監聽,這樣就可以進行一對多的GameObject間的通信了。

using UnityEngine;using System.Collections;using System;public class TubeController : MonoBehaviour {    // Use this for initialization    void Start () {        GameObject.Find("bird").GetComponent<BirdController>().GameOver += OnGameOver;    }    void OnDestroy() {        if ( GameObject.Find("bird") )            GameObject.Find("bird").GetComponent<BirdController>().GameOver -= OnGameOver;    }    void OnGameOver(object sender, EventArgs e)    {        rigidbody2D.velocity = new Vector2(0, 0);    }}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 布拖县| 闸北区| 澜沧| 北京市| 武威市| 彰化市| 辉县市| 吉隆县| 池州市| 集安市| 武城县| 惠州市| 郴州市| 阳西县| 公安县| 阳信县| 嘉义县| 观塘区| 方正县| 太保市| 盘锦市| 江油市| 舒兰市| 怀集县| 兴文县| 尼木县| 石景山区| 中宁县| 凤山县| 观塘区| 青铜峡市| 奎屯市| 印江| 耒阳市| 凤山县| 民丰县| 玛多县| 图木舒克市| 贵德县| 柳江县| 改则县|