實戰(zhàn)記錄:羈絆派系計數(shù)系統(tǒng)(一))
最近正在開發(fā)一個PC游戲的羈絆計數(shù)系統(tǒng)和《金鏟鏟之戰(zhàn)》的羈絆計數(shù)邏輯相似。玩家獲得特定派系羈絆達一定數(shù)量時對應派系羈絆效果激活反之失效。本文是該系統(tǒng)的第一部分本文談這些系統(tǒng)的功能需求基礎系統(tǒng)骨架系統(tǒng)邏輯流程處理計數(shù)變化并將變化通知到業(yè)務腳本相關實戰(zhàn)代碼文章目錄1. 基礎支持計數(shù)的儲存和變化檢測2. 計數(shù)變化狀態(tài)的更新計數(shù)效果的開啟和關閉小結(jié)這個系統(tǒng)要實現(xiàn)這些功能計數(shù)效果的激活、失活計數(shù)文本狀態(tài)的更新激活的為亮色未激活的為暗色計數(shù)效果的閾值節(jié)點動態(tài)更新派系圖標的顯示實際已獲得的計數(shù)數(shù)量顯示鼠標懸浮Tips文本顯示以上效果對每個玩家各自獨立運行這個系統(tǒng)所處的上下文環(huán)境多人游戲單局制實時戰(zhàn)斗基于這些條件我們逐步整理思路。1. 基礎支持計數(shù)的儲存和變化檢測首先根據(jù)已有的需求我們確定這個系統(tǒng)是一個客戶端系統(tǒng)只需要全程在客戶端進行處理。同時這個系統(tǒng)是每個玩家數(shù)據(jù)各自獨立的因此該系統(tǒng)必須持有玩家狀態(tài)的引用。計數(shù)存到哪里我們要開發(fā)一個能對比計數(shù)變化并動態(tài)反應的系統(tǒng)自然涉及到計數(shù)數(shù)據(jù)的儲存。數(shù)據(jù)變化時要確保系統(tǒng)能識別到變化。我們需要兩處數(shù)據(jù)儲存位置玩家狀態(tài)數(shù)據(jù)未經(jīng)過處理的計數(shù)狀態(tài)外部的腳本和游戲內(nèi)的互動均直接操作玩家狀態(tài)數(shù)據(jù)。系統(tǒng)內(nèi)部狀態(tài)數(shù)據(jù)已經(jīng)經(jīng)過變化處理的計數(shù)狀態(tài)。計數(shù)變化的更新方式我們必須能夠在游戲過程中動態(tài)檢測流派計數(shù)的實時狀態(tài)這天然地有兩種方案任意計數(shù)變化時立即處理變化周期間隔時間處理變化在我當前負責的這個項目中游戲不需要特別實時的羈絆計數(shù)更新頻率在這里我可以采用方案2直接給定一個計時器每1秒獲取一次羈絆計數(shù)對比緩存中的計數(shù)后處理變化。綜上我們已經(jīng)確定數(shù)據(jù)怎樣儲存怎樣檢測變化?,F(xiàn)在我們可以著手整理系統(tǒng)的基本骨架。骨架流程我們按照系統(tǒng)需求整理出基本的邏輯流程。骨架腳本-- 流派羈絆系統(tǒng)---class FBS : ObjectlocalFBSoop.class()---注冊系統(tǒng)functionFBS:__init(plr)self._plrplrend---初始化系統(tǒng)functionFBS:setup()---流派計數(shù)緩存---type { [string]: integer }self._bonds{}-- 創(chuàng)建每1秒計時器self._updaterTimer.StartLoop(1,function()self:update()end)end---獲取羈絆計數(shù)緩存值functionFBS:get_bond(key)returnself._bonds[key]or0end---設置羈絆計數(shù)緩存值functionFBS:set_bond(key,value)self._bonds[key]valueend---檢測到羈絆計數(shù)變化functionFBS:on_bond_change(key,new_val)localold_valself:get_bond(key)localdeltanew_val-old_val--TODO ... 處理變化傳播邏輯-- 更新緩存self:set_bond(key,new_val)end---檢測一次變化functionFBS:update()localplr_bondsself._plr:get_bonds()-- 假設玩家類存在該方法獲取實時計數(shù)狀態(tài)fork,vinpairs(plr_bonds)doifself:get_bond(k)~vthenself:on_bond_change(k,v)endendend---注銷系統(tǒng)functionFBS:remove()self._updater:remove()self._updaternilself._bondsnilself._plrnilend2. 計數(shù)變化狀態(tài)的更新計數(shù)效果的開啟和關閉在上1節(jié)中我們已經(jīng)搭建好了檢測變化的系統(tǒng)骨架現(xiàn)在我們的系統(tǒng)已經(jīng)具備了獲取計數(shù)變化并更新緩存的能力。為了讓系統(tǒng)能真正影響游戲邏輯效果我們還需要這個系統(tǒng)能夠和游戲腳本交互。假設我們有這樣的游戲腳本邏輯-- 劍修-2每1秒增加1點攻擊力GameScript.Bond[劍修-2]{-- 開啟效果on_openfunction(plr,ctx)ctx.timerTimer.StartLoop(1,function()plr:setAttr(攻擊力,plr:getAttr(攻擊力)1)end)end,-- 關閉效果on_closefunction(plr,ctx)ctx.timer:remove()end,}GameScript.Bond[劍修-4]...GameScript.Bond[劍修-6]...GameScript.Bond[劍修-8]...現(xiàn)在我們需要通過我們的羈絆計數(shù)系統(tǒng)對其進行管理實現(xiàn)上1節(jié)中未完成的部分。我們需要預設各流派的激活節(jié)點并在計數(shù)變化時進行一次狀態(tài)更新并調(diào)用相關的腳本。FBS.ActivatePoints{[劍修]{2,4,6,8}}functionFBS:setup()-- ... 原有代碼---各節(jié)點激活狀態(tài)---type { [string]: { [integer]: boolean? } }self._activate_states{}end---動態(tài)為 activate_states 分配必要的子項functionFBS:protect_activate_state(key,point)-- 確保只處理被注冊了解鎖節(jié)點的部分ifnottable.find(FBS.ActivatePoints[key],point)thenreturnfalseendifnotself._activate_states[key]thenself._activate_states[key]{}endreturntrueend---設置羈絆計數(shù)節(jié)點的解鎖狀態(tài)緩存functionFBS:set_activate_state(key,point,new_state)ifnotself:protect_activate_state(key,point)thenreturnendself._activate_states[key][point]new_stateend---處理羈絆計數(shù)節(jié)點的解鎖狀態(tài)functionFBS:process_activate_state(key,point,new_state)ifnotself:protect_activate_state(key,point)thenreturnendlocalold_stateself._activate_states[key][point]ifold_statenew_statethenreturnendself._activate_states[key][point]new_state self:call_script(key,point,new_statetrueandon_openoron_close)end---調(diào)用羈絆計數(shù)效果腳本functionFBS:call_script(key,point,method_name)localscript_keystring.format(%s-%s,key,point)localscript_bodyGameScript.Bond[script_key]ifscript_bodythenlocalmethodscript_body[method_name]ifmethodthen---假設存在一個方法用于通過唯一string KEY獲取一個table用于bond腳本儲存臨時數(shù)據(jù)---type tablelocalctxself._plr:get_ctx(FBS-..script_key)method(self._plr,ctx)-- 如果調(diào)用的是失活邏輯運行完腳本后回收上下文表ifmethod_nameon_closethenself._plr:remove_ctx(FBS-..script_key)endendendend---檢測到羈絆計數(shù)變化functionFBS:on_bond_change(key,new_val)localold_valself:get_bond(key)localdeltanew_val-old_val-- 處理變化傳播邏輯逐節(jié)點處理localdire1ifnew_valold_valthendire-1endforiold_val,new_val,diredoself:process_activate_state(key,i,dire1)end-- 更新緩存self:set_bond(key,new_val)end小結(jié)數(shù)據(jù)部分我們是如何處理的我們將玩家的實時數(shù)據(jù)直接儲存到玩家狀態(tài)本身這個狀態(tài)屬于外部。我們在系統(tǒng)內(nèi)周期性檢測外部狀態(tài)變化并更新我們系統(tǒng)內(nèi)部的狀態(tài)。我們?nèi)绾翁幚碛嫈?shù)節(jié)點狀態(tài)我們使用self._activate_states以KEY 節(jié)點的方式儲存每一個流派的每一個計數(shù)節(jié)點的開啟/關閉狀態(tài)。在發(fā)現(xiàn)數(shù)據(jù)變化時我們通過檢查緩存中的節(jié)點狀態(tài)確認某個節(jié)點是否產(chǎn)生變化。以此確保我們只響應產(chǎn)生變化的部分無變化的部分則跳過邏輯。我們?nèi)绾卧O計業(yè)務腳本羈絆效果的業(yè)務腳本本質(zhì)上是一系列開關腳本。我們在對應節(jié)點進入開啟狀態(tài)時調(diào)用on_open邏輯。而在進入關閉狀態(tài)時我們調(diào)用on_close邏輯。后續(xù)談什么在第二部分中我們會討論如何讓這個系統(tǒng)和UI交互確保能夠正確渲染UI文本和Tips相關數(shù)據(jù)。