用Lua实现基于观察者模型的游戏成就系统

用Lua实现基于观察者模型的游戏成就系统

博主进入游戏开发行业一年多,希望在有空之余能多理解几种开发模型,并运用到实际项目中,为了督促自己,会时不时写下博客。这是开发模型系列的第一篇博客,也是博主本人的第一篇博客,有误之处还望指出。

简单说明

具体定义可以查看其他资料。简单的来说,观察者模型可以实现:事件的发生与相应功能的执行解耦。具体咱们通过下面的例子理解这句话到底是啥意思。

适用的地方

当初我同事是如此实现游戏的成就系统的:例如玩家在成功升级仙友时,我需要在相应的代码处调用他给我的接口,以此来告诉他仙友升级了。其实从逻辑上来讲是很通顺的,但是这样有个问题。同事还开发了每日任务系统,也就是说我必须也在仙友升级那里调用他给我的每日任务接口。这种方式是事件的发生主动去关联那些对这个事件感兴趣的操作。这样不大好,应该是对这个事件感兴趣的操作主动去关联这个事件。因为前者是一对多,后者是一对一。后者对开发人员更加友好。这就是开头说的事件的发生和功能的执行耦合度过高了。实现各个模块的解耦一直是我们开发追究的,所以在这里我们可以尝试用观察者模型来重构这成就系统。

开始愉快的撸代码啦

大致从观察者和被观察者(主题)两个方面讲

被观察者

首先我们需要定义先撸出我们的游戏的事件系统,我们让这个事件系统来管理被观察者。

--这个是我们的事件的枚举,我们就以三种事件为例
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

成就系统(为了方便测试,把一些测试函数也写在了这里,并且和monoBehaviour绑定)

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个按钮:注册观察者(一共有三个观察者), 仙友升级, 仙友技能升级
三个按钮:注册观察者(一共有三个观察者), 仙友升级, 仙友技能升级

点击注册观察者:

点击仙友升级:

点击仙友技能升级:

总结一下

观察者模型在开发用到地方还是挺多的,具体是否用,用哪里,怎么用,我觉得需要程序猿自己把握了。在下认为开发模型固然重要,但是不能过分奉为圭臬。如果用了开发模型后,代码反而僵硬,这还不如不用。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值