【遊戲_忍者六道】狀態機設計與應用之lua篇

魚弦發表於2016-09-04

一 前言

上一篇圖文描述了C++實現遊戲中應用的狀態機,C++中的三大特性:封裝,繼承,多型得以應用,封裝資料引擎訪問的增,刪,查,改,狀態機基類以及多種狀態子類實現私有private或者公共public繼承它,但是發揮更大的作用是多型的使用,子類繼承實現基類的virtual虛擬函式,在不同的事件機制觸發下,遷移變換執行不同的狀態,雖然毫無標準順序可言,卻是按照指定規則軌道正確行使,這是狀態機設計的正確使用的最妙之處,起碼是當前使用是這樣,不同的需求會設計出不同高效的狀態機。

那麼,lua在狀態機方面是如何應用的呢?

眾所周知,lua是遊戲開發中效率頗高的指令碼語言,其設計目的是為了嵌入應用程式中,從而為應用程式提供靈活的擴充套件和定製功能。Lua由標準C編寫而成,幾乎在所有作業系統和平臺上都可以編譯,執行。Lua指令碼可以很容易的被C/C++程式碼呼叫,也可以反過來呼叫C/C++的函式。不僅僅作為擴充套件指令碼,也可以作為普通的配置檔案,代替XML,ini等檔案格式,並且更容易理解和維護。在目前所有指令碼引擎中,Lua的速度是最快的。這一切都決定了Lua是作為嵌入式指令碼的最佳選擇。現在很多遊戲(無論手遊,還是端遊),都為它的強大所折服,ps和魔獸都有使用lua,不得不承認,lua這方面做得確實很不錯,雖然lua是程式導向和函數語言程式設計,但是它的閉包和table特性使得它像C++一樣,很完美的實現OO——物件導向。

二 狀態機實現

這裡簡單的說下一個遊戲中,lua指令碼實現一個功能的場景:

十一活動簡單序列圖

舉個栗子,這是十一的活動,很多遊戲會選擇在傳統佳節,做一些活動,引起玩家的關注和參與,從而贏得訪問和流量,十一活動流程很簡單,玩家登陸游戲客戶端之後,如果當前時間不到十月一日,那麼就不會有一些玩法的Npc或者圖示之類,會選擇玩其他的一些活動或者打怪,即使如此,此時已經處在設計的狀態機中了,只是當前為“檢查活動開啟狀態”,這就有點像,不知不覺就進來套路一樣;當時間到來時,一般活動開始事件會設定為:2016年10月1日00:00:00,不得有絲毫時間的馬虎,那麼自然可以想到,結束時間為:2016年年10月5日24點了。

lua語法中,使用table儲存資料,我們在StateManagerByLua在活動開始時,事先new呼叫建構函式,使用tCurrentState的一個table將EDoing_Sate(進行狀態)和ECheckStart_Start(檢查是否開始狀態)作為Key,儲存AstateByLua,BstateByLua…狀態的物件。

在此活動自然會在“進行狀態”,釋出活動開始的一些廣播抑或一些郵件通知User知道,BginTime=< NowTime

tCurrentState[EDoing_Sate] = AstateByLua:new{ p_Object = self}

兩種狀態被儲存下來之後,時間的檢查需要使用timer定時器,可以自己封裝相關的定時器和相關回撥,當時間到達10.1,那麼活動開始了,此時開始的狀態可以參看如下用例:

用例1

品嚐到十一活動的樂趣之後,可不曾曉得,事件過得會如此之快,升級的也升了,高階裝備也拿了,錢也賺了,活動時間>10.5到0點就End了,npc或者活動圖示就會隨之消失。

三 lua原始碼實現狀態機

狀態機類圖先睹為快:

這裡寫圖片描述

下面簡單寫一些此次活動的程式碼結構:

----------------------------------------
--- Title: 管理器相關   StateManagerByLua
--- desc: 負責 管理各種狀態
---------------------------------------
//管理器屬性
StateManagerByLua = new{
        m_CurrentState = nil,// 當前狀態
        tCurrentState = {},  //儲存所有狀態的table
}

// 管理器 構造
// dest: 將所有狀態都儲存起來
function StateManagerByLua:ManagerConstructor()
    tCurrentState[EA_Sate] = AstateByLua:new{ p_Object = self}
    tCurrentState[EB_Sate] = BstateByLua:new{ p_Object = self}
    ...
end

// 狀態切換
function StateManagerByLua:GotoState(nState)
    // 離開當前狀態
    ...
    self.m_CurrentState:LeaveState()
    ...
    // 進入其他狀態
    self.m_CurrentState = tCurrentState[nState]
    self.m_CurrentState:EnterState()
end

// 檢測活動是否開啟
function StateManagerByLua:CheckState()
    if 10.1 =< time and time < 10.5 then
            self:GotoState(EBeginState)// 進入開始狀態
    else
        self:GotoState(ECheckActivityState) // 進入檢測狀態
    end
end

由此,可清晰的解構出,活動管理其實並沒有那麼複雜,manager耦合各個狀態物件和自身屬性,發揮著重要作用。

接下來看看開始狀態的程式碼結構:

---------------------------------------
-- Title:活動開始狀態
-- desc:活動開啟 - 事件xxx- 做法xxx- 
----------------------------------------
BeginState = new{
    m_pobj = nil, // 訪問管理器屬性
    ...
}

// 進入活動開始狀態
function BeginState:EnterState()
    ...
    //打怪
    //領取獎勵
    // 自定義活動邏輯
    ...
end

//離開狀態
function  BeginState:LeaveState()
    ...
end

so, 結束狀態也是同理,自己可以合理安排各個狀態所需的邏輯和玩法,異曲同工。

相關文章