博主进入游戏开发行业一年多,希望在有空之余能多理解几种开发模型,并运用到实际项目中,为了督促自己,会时不时写下博客。这是开发模型系列的第一篇博客,也是博主本人的第一篇博客,有误之处还望指出。
具体定义可以查看其他资料。简单的来说,观察者模型可以实现:事件的发生与相应功能的执行解耦。具体咱们通过下面的例子理解这句话到底是啥意思。
当初我同事是如此实现游戏的成就系统的:例如玩家在成功升级仙友时,我需要在相应的代码处调用他给我的接口,以此来告诉他仙友升级了。其实从逻辑上来讲是很通顺的,但是这样有个问题。同事还开发了每日任务系统,也就是说我必须也在仙友升级那里调用他给我的每日任务接口。这种方式是事件的发生主动去关联那些对这个事件感兴趣的操作。这样不大好,应该是对这个事件感兴趣的操作主动去关联这个事件。因为前者是一对多,后者是一对一。后者对开发人员更加友好。这就是开头说的事件的发生和功能的执行耦合度过高了。实现各个模块的解耦一直是我们开发追究的,所以在这里我们可以尝试用观察者模型来重构这成就系统。
大致从观察者和被观察者(主题)两个方面讲
首先我们需要定义先撸出我们的游戏的事件系统,我们让这个事件系统来管理被观察者。
--这个是我们的事件的枚举,我们就以三种事件为例
ENUM_GameEvent = {
--仙友模块举例
Xianyou_Lvl_Up = 1, --仙友升级事件
Xianyou_Skill_Up = 2,--仙友技能升级事件
Xianyou_Star_Up = 3,--仙友升星
}
--这是上个table的反表
ENUM_GameEventInverse = {
--仙友模块举例
[1] = "Xianyou_Lvl_Up",
[2] = "Xianyou_Skill_Up",
[3] = "Xianyou_Star_Up",
}
--初始化事件系统
function IGameEventSystem:Init( )
CC.Print("init IGameEventSystem")
self.gameEvents = {}
end
--注册一个观察者
--enumGameEvent:事件名字, theKey:标记此观察者,为了更好的管理,theObserver:注册的观察者
function IGameEventSystem:RegisterObserver(enumGameEvent, theKey,theObserver)
local subject = self:RegisterEvent(enumGameEvent)
if subject then
subject:Attach(
{
key = theKey,
observer = theObserver
}
)
end
end
--注册一个事件
function IGameEventSystem:RegisterEvent(enumGameEvent)
if self.gameEvents[enumGameEvent] then
return self.gameEvents[enumGameEvent] --如果已经存在改事件就返回
end
local gameEventSubject = require "IGameEventSubject".IGameEventSubject:New()
gameEventSubject:Init() --没有该事件,所以就为改事件生成一个主题
if CC.ENUM.ENUM_GameEventInverse[enumGameEvent] then
self.gameEvents[enumGameEvent] = gameEventSubject
CC.Print("注册事件 " .. enumGameEvent)
return gameEventSubject
else
CC.Print("还没有该事件的ENUM")
end
end
--事件发生,提醒注册改事件的observer更新
function IGameEventSystem:NotifySubject(enumGameEvent)
if self.gameEvents[enumGameEvent] then
self.gameEvents[enumGameEvent]:Update()
else
CC.Print("该事件没有注册")
end
end
然后我们定义一个主题的类
--初始化事件主题
function IGameEventSubject:Init( )
self.gameEventObservers = {}
end
--在主题中加入一个观察者
function IGameEventSubject:Attach(tVal)
if self.gameEventObservers[tVal.key] == nil then
self.gameEventObservers[tVal.key] = tVal.observer
CC.Print("注册观察者 " .. tVal.key)
end
end
--移除一个观察者
function IGameEventSubject:Detach(key)
self.gameEventObservers[key] = nil
end
--通知主题下面的所有观察者更新
function IGameEventSubject:Update( )
for k,v in CC.pairs(self.gameEventObservers) do
v:Update()
end
end
--初始化一个观察者
function IGameEventObserver:Init(tVal)
self.key = tVal.key --改观察者的标记
self.enumEvent = tVal.enumEvent --事件名称
self.funcUpdate = tVal.funcUpdate --当被通知时所需要执行的函数
igameEventSystem:RegisterObserver(tVal.enumEvent, tVal.key, self) -- 告诉事件系统,观察者的名字,对什么主题感兴趣
end
--更新所需要执行的函数
function IGameEventObserver:Update( )
if self.funcUpdate then
self.funcUpdate()
end
end
function AchievementSystem:Start( )
CC.Print("observer begin")
igameEventSystem:Init()
self:C("Button"):Click("InitObserver")
self:C("Button2"):Click("XianyouLvlup")
self:C("Button3"):Click("SkillUp")
end
--测试创建了三个个观察者,主分别是仙友升级和仙友技能升级
function AchievementSystem:InitObserver( )
local xianyouLvlUpObserver = iGameEventObserver:New()
xianyouLvlUpObserver:Init({
enumEvent = CC.ENUM.ENUM_GameEvent.Xianyou_Lvl_Up,
key = "achievementSystem_ob1",
funcUpdate = function ( )
--当这个func执行的时候,说明该主题已经更新(仙友已经升级),然后更新之后需要做的相关操作写在这里
CC.Print("成就系统观察者捕捉到:仙友升级" )
end
})
local xianyouSkillUpObserver1 = iGameEventObserver:New()
xianyouSkillUpObserver1:Init({
enumEvent = CC.ENUM.ENUM_GameEvent.Xianyou_Skill_Up,
key = "achievementSystem_ob1",
funcUpdate = function ( )
--当这个func执行的时候,说明该主题已经更新(仙友技能已经升级),然后更新之后需要做的相关操作写在这里
CC.Print("成就系统观察者1捕捉到:仙友技能升级" )
end
})
local xianyouSkillUpObserver2 = iGameEventObserver:New()
xianyouSkillUpObserver2:Init({
enumEvent = CC.ENUM.ENUM_GameEvent.Xianyou_Skill_Up,
key = "achievementSystem_ob2",
funcUpdate = function ( )
--当这个func执行的时候,说明该主题已经更新(仙友技能已经升级),然后更新之后需要做的相关操作写在这里
CC.Print("成就系统观察者2捕捉到:仙友技能升级" )
end
})
end
--模拟仙友升级了
function AchievementSystem:XianyouLvlup( )
igameEventSystem:NotifySubject(CC.ENUM.ENUM_GameEvent.Xianyou_Lvl_Up)
end
--模拟仙友技能升级了
function AchievementSystem:SkillUp( )
igameEventSystem:NotifySubject(CC.ENUM.ENUM_GameEvent.Xianyou_Skill_Up)
end
unity中创建3个按钮:注册观察者(一共有三个观察者), 仙友升级, 仙友技能升级
观察者模型在开发用到地方还是挺多的,具体是否用,用哪里,怎么用,我觉得需要程序猿自己把握了。在下认为开发模型固然重要,但是不能过分奉为圭臬。如果用了开发模型后,代码反而僵硬,这还不如不用。