Unity3D设计模式学习之观察者模式(二)

上一篇简单的写了观察者模式例子和实现思路,对观察者模式只是有了大致的了解,设计模式一般都比较抽象,实际运用需要根据需求来设计、重构,这篇就看看书上的例子,加深理解。

实现说明
游戏事件系统的实现
//游戏事件系统的实现
public enum ENUM_GameEvent{
    None = 0,
    EnemyKilled = 1, //敌方单位阵亡
    SoldierKilled = 2,//玩家单位阵亡
    SoldierUpgate = 3,//玩家单位升级
    NewStage = 4 //新关卡
}

//游戏事件系统
public class GameEventSystem:IGameSystem{
    private Dictionary m_GameEvents
    = new Dictoinary();

    public GameEventSystem(PBaseDefenseGame PBDGameObject):base(PBDGame){
        Initialize();
    }

    //释放
    public override void Release(){
        m_GameEvents.Clear();
    }
    //为某一主题注册一个观察者
    public void RegisterObserver(ENUM_GameEvent emGameEvent,IGameEeventObserver Observer){
        //获取事件
        IGameEventSubject Subject = GetGameEventSubject(emGameEvent);
        if(Subject != null){
            Subject.Attach(Observer);
        }
    }
    //注册一个事件
    private IGameEventSubject GetGameEventSubject(ENUM_GameEvent emGameEvent) {
        //是否已经存在
        if(m_GameEvents.ContainKey(emGameEvent())
            return m_GameEvents(emGameEvent);

        //产生对应的GameEvent
        IGameEventSystem pSubJect = null;
        switch(emGameEvent){
            case ENUM_GameEvent.EnemyKilled:
            pSubject = new EnemyKilledSubject();
            break;

            case ENUM_GameEvent.SoldierKilled:
            pSubject = new EnemyKilledSubject();
            break;
            //...根据枚举,确定主题事件
            default:
            Debug.LogWarning("还没有针对" +emGameEvent+"指定要产生的Subject类");
            return null;
        }
        //加入后并返回
        m_GameEvents.Add(emGameEvent,pSubject);
        return pSubject;
    }

    //通知一个GameEvent更新
    public void NotifySubject(ENUM_GameEvent emGameEvent,System.Object Param){
        //是否存在
        if(m_GameEvents.ContainsKey(emGameEvent) == false){
            return;
        }
        m_GameEvents[emGameEvent].SetParam(Param);
    }
}

游戏事件系统类使用了字典来管理所有游戏事件主题
新增时,针对每一个游戏事件产生对应的主题后加入容器内容,并保证一个游戏事件只存在一个主题对象。

RegisterObserver 方法用于其他系统向游戏事件系统订阅主题,
调用时传入指定的游戏事件及观察者类对象。

当某游戏事件触发时,通过通知主题更新NotifySystem方法,就能通知所有订阅该游戏事件主题的观察者

游戏事件主题,观察者接口
public abstract class IGameEventSubject{
    //观察者
    private List m_Observer = 
    new List>IGameEventObserver>(); 

    //发生事件时附加的参数
    //加入
    public void Attach(IGameEventObserver theObserver){
        m_Observers.Add(theObserver);
    }
    //取消
    public void Detach(IGameEventObserver theObserver){
        m_Observer.Remove(theObserver);
    }
    //通知
    public void Notify(){
        foreach(IGameEventObserver theObserver in m_Observers)
            theObserver.Update();
    }
    //设置参数
    public virtual void SetParam(System.Object Param){
        m_Param = Param;
    }
}

public abstract class IGameEventObserver{
    public abstract void Update();
    public abstract void SetSubject( IGameEventSubject Subject );
}

类中定义一个List泛型容器,来管理订阅该主题的观察者,并提供SetParam方法来设置每一个游戏事件所需提供的内容。

观察者接口也只是简单的设置主题,和更新操作。
这样,观察者设置好主题,当主题更新时,主题就会通知它的每个观察者也进行对应的更新。

完成各个游戏事件主题及观察者

以下是4个游戏事件主题

1、敌人角色阵亡
2、玩家角色阵亡
3、玩家角色升级
4、进入新关卡

敌人角色阵亡
public class EnemyKilledSubject  : IGameEventSubject {
    private int m_KilledCount = 0;
    private IEnemy = m_Enemy = null;
    public EnemyKilledSubject(){}
    //获取对象
    public IEnemy GetEnemy(){
        return m_Enemy;
    }
    //当前敌人单位阵亡数
    public int GetKilledCount(){
        return m_KilledCount;
    }
    //通知敌人单位阵亡
    public override void SetParam(System.Object Param){
        base.SetParam(Param);
        m_Enemy = Param as IEnemy;
        m_KilledCount ++;
        //通知
        Notify();
    }
}

敌人角色阵亡,会发出通知,将阵亡的而敌人角色使用SetParam方法传入,在内容增加计时器,提供给观察者查询。

对应的3个观察者
//UI观察者Enemy阵亡事件
public class EnemyKilledObserverUI : IGameEventObserver {
    private EnemyKilledSubject m_Subject = null;
    private PBaseDefenseGame m_PBDGame = null;
    //设置观察者
    public EnemyKilledObserverUI(PBaseDefenseGame PBDGame){
        m_PBDGame = PBDGame;
    }
    //通知Subject被更新
    public override void SetSubject(IGameEventSubject Subject){
        m_Subject = Subject as EnemyKilledSubject;
    }
    //通知Subject被更新
    public override void Update(){
        m_PBDGame.ShowGameMsg("敌方单位阵亡");
    }
}

//成就观察Enemy阵亡事件
public class EnemyKilledObserAchievement : IGameEventObserver{
    private EnemyKilledSubject m_Subject = null;
    private AchievementSystem m_AchievementSystem = null;

    public EnemyKilledObserAchievement(AchievementSystem theAchieventSystem){
        m_AchievementSystem  = theAchieventSystem;
    }
    //设置观察者的主题
    public override void SetSubject(IGameEventSubject Subject){
        m_Subject = Subject as EnemyKilledSubject;
    }
    //通知主题更新
    public override void Update(){
        m_AchievementSystem.AddEnemyKilledCount();
    }
}
//关卡分数观察者
public class EnemyKilledObserverStageScore:IGameEventObserver{
    private EnemyKilledSubject m_Subject = null;
    private StageSystem m_StageSystem = null;

    public EnemyKilledObserverStageScore(StageSystem theStageSystem){
        m_StageSystem = theStageSystem;
    }
    //设置观察者...
    //设置主题更新
    public override void Update(){
        m_StageSystem.SetEnemyKilledCount(m_Subject.GetKilledCount());
    }
}

以上即为一个主题与观察的实现,可以看到,每个观察者都有不同游戏系统的对象引用,
但是它们都被同一个主题(敌人被击杀事件)管理,主题一旦更新(信息更新),
就通知每个观察者,让它们(不同的游戏系统)做不同的功能操作。
当需要更多具体的细节功能时 如:if条件限制,可以在Update的方法里添加

剩下的事件主题和观察者根据上面的设计模板和思路实现即可,这里不再一一列出了,有兴趣可以购买作者的这本书来看看,或者去github下载源码= 。 =
https://github.com/sttsai/PBaseDefense_Unity3D

使用观察者模式的优点

成就系统以游戏事件为基础,记录每个游戏事件发生的次数以及时间点,作为成就项目的判断依据。当一个游戏事件会触发时,可能存在其他系统需要使用同一个游戏事件。

因此,加入了以观察者模式为基础的游戏事件系统,就可以有效解除“游戏事件的发生”与有关的“系统功能调用”之间的绑定。

这样游戏事件发生时就不必理会后续的处理工作,而是交给游戏事件主题负责调用观察者/订阅者。此外,也能同时调用多个系统同时处理这个事件引发的后续操作。

注意的

双向和单向信息通知:
在社交网页上,如微博当通过关注、订阅来建立观察者和主题的关系。它们之间互相关注,就是双向的信息通知,同时扮演观察者和主题的角色。

类过多的问题:
一个主题一般会有多个观察者,如果一个观察者就是一个类时,随着项目的开发就会造成大量的类,这反而是个缺点。因此可以把类对象转换使用回调函数,然后将功能相似的“回调函数”以同一个类来管理,就能减少类过多的问题。

命令模式与观察者模式的关系
这两个模式都着重于“发生”与“执行”这两个操作消除解耦的模式。当观察者的主题只有一个观察者时,就非常像命令着模式。
但还是有一些差异可以分辨出两个模式应用的时机:
命令模式:重点是对命令的管理,应用的系统对于发出的命令有增、删、改、记录、排序、撤销 等操作的需求
观察者模式:对于“观察者/订阅者”进行管理,意思是观察者可以在 系统运行时间决定订阅或退订等操作,让“执行者(观察者/订阅者)”可以被管理。

你可能感兴趣的:(设计模式与游戏完美开发)