上一篇简单的写了观察者模式例子和实现思路,对观察者模式只是有了大致的了解,设计模式一般都比较抽象,实际运用需要根据需求来设计、重构,这篇就看看书上的例子,加深理解。
//游戏事件系统的实现
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方法传入,在内容增加计时器,提供给观察者查询。
//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
成就系统以游戏事件为基础,记录每个游戏事件发生的次数以及时间点,作为成就项目的判断依据。当一个游戏事件会触发时,可能存在其他系统需要使用同一个游戏事件。
因此,加入了以观察者模式为基础的游戏事件系统,就可以有效解除“游戏事件的发生”与有关的“系统功能调用”之间的绑定。
这样游戏事件发生时就不必理会后续的处理工作,而是交给游戏事件主题负责调用观察者/订阅者。此外,也能同时调用多个系统同时处理这个事件引发的后续操作。
双向和单向信息通知:
在社交网页上,如微博当通过关注、订阅来建立观察者和主题的关系。它们之间互相关注,就是双向的信息通知,同时扮演观察者和主题的角色。
类过多的问题:
一个主题一般会有多个观察者,如果一个观察者就是一个类时,随着项目的开发就会造成大量的类,这反而是个缺点。因此可以把类对象转换使用回调函数,然后将功能相似的“回调函数”以同一个类来管理,就能减少类过多的问题。
命令模式与观察者模式的关系
这两个模式都着重于“发生”与“执行”这两个操作消除解耦的模式。当观察者的主题只有一个观察者时,就非常像命令着模式。
但还是有一些差异可以分辨出两个模式应用的时机:
命令模式:重点是对命令的管理,应用的系统对于发出的命令有增、删、改、记录、排序、撤销 等操作的需求
观察者模式:对于“观察者/订阅者”进行管理,意思是观察者可以在 系统运行时间决定订阅或退订等操作,让“执行者(观察者/订阅者)”可以被管理。