观察者模式在射击游戏中的应用

1.游戏简要说明:

游戏中有一个玩家,他手上拿着一个炮弹,可以射击游戏中的物体,比如飞机。

游戏中有显示玩家信息的UI,比如血量,射击命中次数、丢失次数,击败的飞机数量等等。

游戏中有成就系统,当主角击落一些飞机时,会达成一些成就。

 

2.使用观察者模式的原因

游戏中有许多的事件,比如:

玩家射击事件:OnMarineShoot

玩家击落飞机事件:OnBulletHit

在这些事件发生之后,会有许多的东西发生变化,比如UI和成就系统。

假如不使用观察者模式,在该事件发生后需直接写ui和成就系统变化的代码,那么模块之间就会有很高的耦合。

而使用观察者模式,我们将执行以下操作:

-在游戏中声明主要的事件:OnMarineShoot,OnBulletHit......

-创建EventDispatcher:作为中介,接受和转发事件

-游戏开始时UI AchievementManager对象将向EventDispatcer 注册,以便在OnMarineShoot,OnBulletHit...等事件被引发时被通知

-玩家对象:当他的HP更改或杀死怪物时,他会向EventDispatcher发送消息,EventDispatcher会将其转发给收件人(UI和成就系统)

 

3.下面我们用部分代码来展示观察者模式

声明EventID,即游戏中会发生的事件:

public enum EventID
{
    None = 0,
    OnMarineShoot,
    OnBulletHit,
    OnHelicopterDead,
    OnHelicopterEscaped,
}

 

创建EventDispatcher,即观察者模式的subject,在其中可以注册和取消observer。

public class EventDispatcher : MonoBehaviour
{
    // Register to listen for eventID, callback will be invoke when event with eventID be raise
    public void RegisterListener (EventID eventID, Action<object> callback);

    // Post event, this will notify all listener which register to listen for eventID
    public void PostEvent (EventID eventID, Component sender, object param = null);

    // Use for Unregister, not listen for an event anymore.
    public void RemoveListener (EventID eventID, Action<object> callback);
}

/// An Extension class, declare some "shortcut" for using EventDispatcher
public static class EventDispatcherExtension
{
    /// Use for registering with EventDispatcher
    public static void RegisterListener (this MonoBehaviour listener, EventID eventID, Action<object> callback)
    {
        EventDispatcher.Instance.RegisterListener(eventID, callback);
    }

    /// Post event with param
    public static void PostEvent (this MonoBehaviour listener, EventID eventID, object param)
    {
        EventDispatcher.Instance.PostEvent(eventID, sender, param);
    }

    /// Post event with no param (param = null)
    public static void PostEvent (this MonoBehaviour listener, EventID eventID)
    {
        EventDispatcher.Instance.PostEvent(eventID, sender, null);
    }
}

 

ui注册监听示例:UITextManager需要监听游戏中的所有事件来更新ui

void Start ()
{
    // register to receive events
    this.RegisterListener(EventID.OnMarineShoot, (param) => OnMarineShoot());
    this.RegisterListener(EventID.OnBulletHit, (param) => OnBulletHit());
    this.RegisterListener(EventID.OnHelicopterDead, (param) => OnHelicopterDead());
    this.RegisterListener(EventID.OnHelicopterEscaped, (param) => OnHelicopterEscaped());
}

/// Will be invoke when OnMarineShoot event was raised
void OnMarineShoot()
{
    // some code to modify text on UI
}

/// Will be invoke when OnBulletHit event was raised
void OnBulletHit()
{
    // some code to modify text on UI
}

......other event

 

当不想再“监听”某个事件时,使用RemoveListener来取消监听

Action<object> _OnReceiveEventRef;

void Start ()
{
    // Reference to our OnReceiveEvent, we'll use it to RegisterListener() and RemoveListener()
    _OnReceiveEventRef = (param) => OnReceiveEvent();
    EventDispatcher.Instance.RegisterListener(EventID.OnBulletHit, _OnReceiveEventRef);
}
void UnregisterListener ()
{
    /// Dont use this way, it'll create a new Action, instead of a reference to our OnReceiveEvent(),
    /// then it still be invoked from EventDispatcher
    //EventDispatcher.Instance.RemoveListener(EventID.OnBulletHit, (para) => OnReceiveEvent());
    
    // Right way
    EventDispatcher.Instance.RemoveListener(EventID.OnBulletHit, _OnReceiveEventRef);
}

 

事件的触发

void Update ()
{
    if (Input.GetMouseButtonDown(0))//left mouse
    {
        // code to Instantiate the bullet
        // raise shoot event
        this.PostEvent(EventID.OnMarineShoot);
    }
}

 

4.总结:

使用观察者模式很好的降低观察者和被观察者的耦合程度,并且观察者模式满足了“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标非常地方便。

 

5.项目的github地址:

https://github.com/ThinhHB/UnityObserverPatternDemo/tree/01afc838bca310271988832b30b713d91ba721e3

posted @ 2018-10-26 13:42  Wakayo  阅读(419)  评论(0编辑  收藏  举报