官术网_书友最值得收藏!

1.7 萬物皆Actor

合理分割功能是分布式模型的一大難點,我們需要尋找一種模式,它既能符合游戲邏輯的表達,又能讓計算機高效執(zhí)行。

游戲業(yè)界苦苦追尋著更適合當(dāng)代游戲的服務(wù)端模型,驀然回首,幾十年前就被提出的Actor并發(fā)模型就在燈火闌珊處。如圖1-25所示,在Actor模型中,每個Actor相互隔離,只通過消息通信,具有天然的并發(fā)性。

圖1-25 Actor模型的示意圖

想要借用Actor模型的理念,首先要了解什么是Actor模型。

1.7.1 靈感來自Erlang

Actor模型由來已久。早在1973年,Carl Hewitt提出了Actor并發(fā)計算的理論模型;1991年愛立信推出的編程語言Erlang將Actor模型融入語言里,并應(yīng)用在通信領(lǐng)域里。2009年前后,珠三角的一些游戲公司(四三九九、菲音、明朝網(wǎng)絡(luò))開始大規(guī)模地將Erlang語言應(yīng)用于游戲領(lǐng)域。2012年前后,云風(fēng)(吳云洋的網(wǎng)名)開源了C語言Actor模型框架Skynet,并稱之為游戲服務(wù)端引擎,且將其應(yīng)用在不少商業(yè)游戲上。

1.7.2 對世界的抽象

Actor模型的理念——萬物皆Actor,它是更進一步的面向?qū)ο螅窗咽篱g萬物都當(dāng)作Actor對象。Actor可以代表一個角色、一只動物,也可以代表整個游戲場景,圖1-26展示的是用Actor模型抽象的一個游戲世界,方括號代表Actor的類型,id代表Actor的標(biāo)識,中間文本代表名稱。

圖1-26 萬物皆Actor

說明:Skynet中將Actor對象稱為服務(wù),Erlang中將其稱為進程(不同于操作系統(tǒng)進程),為統(tǒng)一術(shù)語,在解釋Actor模型時,使用“Actor”一詞;在Skynet的語義下,使用“服務(wù)”一詞。

在圖1-27中,每個Actor都會包含自身狀態(tài)(HP、Coin),以及一個信箱(消息隊列),Actor通過給其他Actor“寄信”來實現(xiàn)通信。至于收到信件后的反應(yīng),取決于收信的Actor。

圖1-27 Actor示意圖

整個Actor系統(tǒng)可類比為“郵局”,它負(fù)責(zé)信件的傳送。如圖1-28所示,Actor1001給1002發(fā)送信件,請求“查詢登錄玩家數(shù)量”,寄出的“信件”經(jīng)由郵局,投遞到Actor1002的信箱。由于各個Actor相互獨立,計算機很容易讓它們并行工作。

圖1-28 Actor消息傳遞示意圖

如果理論解釋不易理解,不妨直接看代碼。代碼1-10用Lua定義了Role類型的Actor對象。代碼中的local Role=function()...end用定義方法的方式定義了一個類,這是Lua的特殊語法,只需記住這段代碼定義了Role類,它包含id、coin、hp這三個屬性和dispatch方法即可。dispatch是處理消息的方法,參數(shù)source代表消息發(fā)送方,參數(shù)msg代表消息內(nèi)容。如果收到“work”,角色會努力工作賺錢,并督促發(fā)送方努力工作(發(fā)送“work”給對方);如果收到“eat”,角色會吃美食恢復(fù)健康。

代碼1-10 定義一個Actor對象(Lua語言)


local Role = function()
    local M = {
        id = -1,    --Actor標(biāo)識
        coin = 100,
        hp = 200,
    }

    function M:dispatch(source, msg)
        if msg == "work" then
            self.coin = self.coin + 10
            print(self.id.." work, coin:"..self.coin)
            send(self.id, source, "work")
        elseif msg == "eat" then
            self.hp = self.hp + 5
            print(self.id.." eat, hp:"..self.hp)
            else
                --更多消息處理
            end
    end

    return M
end

Actor系統(tǒng)會提供newactor(創(chuàng)建Actor對象)、send(向Actor發(fā)送消息)之類的方法,如代碼1-11中,創(chuàng)建了4個Role類型的Actor,它們的id是從101到104,這里讓101給102發(fā)送“work”,103給104發(fā)送“eat”。

代碼1-11 創(chuàng)建Actor對象并發(fā)送消息(Lua語言)


newactor(101, Role())
newactor(102, Role())
newactor(103, Role())
newactor(104, Role())
send(101, 102, "work")
send(103, 104, "eat")

圖1-29是代碼1-11中4個Role對象的運行示意圖,其中101和102這兩個Actor在相互督促工作,努力賺錢;103讓104吃好喝好。圖1-30展示了代碼1-11的運行結(jié)果,盡管是101先向102發(fā)送“work”后,103再向104發(fā)送“eat”,但各個Actor是并行執(zhí)行的,因此消息處理的順序不確定。

圖1-29 代碼1-11運行的示意圖

圖1-30 代碼1-11的運行結(jié)果

1.7.3 為何適用

為什么說Actor模型適用于游戲開發(fā)呢?

回顧1.4.3節(jié)的多進程程序,從某種程度上說,Actor模型和傳統(tǒng)的多進程服務(wù)端結(jié)構(gòu)有很多相似之處。不同的是,一個操作系統(tǒng)進程會占用很多的系統(tǒng)資源,按照1.5.3節(jié)的分析,進程不僅會占用較多的內(nèi)存,操作系統(tǒng)在切換進程(線程)時也會占用較多的CPU時間,一臺物理機只能運行幾百個進程,這會限制游戲的業(yè)務(wù)分割。

舉個例子,假設(shè)要開發(fā)一款斗地主游戲(如圖1-31所示),每局游戲由3名玩家參與。那么,一種處理方式是開啟多個進程,每個進程處理多張桌子的邏輯(如圖1-32所示)。如果每個進程可以處理10張桌子,單臺物理機開啟100個進程就可以支撐3000名玩家。但是,進程A和進程B究竟是什么東西呢?是桌子集合?它其實是計算機系統(tǒng)的概念,因為在現(xiàn)實世界找不到對應(yīng)的事物,所以不容易理解。還有一種方法,即讓每個進程只處理一張桌子的邏輯,然而由于進程會占用很多的系統(tǒng)資源,因此這樣做會導(dǎo)致單臺物理機只能支持幾百名玩家。

圖1-31 《斗地主》游戲示意圖

圖1-32 傳統(tǒng)的多進程模型

Erlang、Skynet通過內(nèi)部處理,讓每個Actor都是輕量級的,可讓每張桌子獨立分開(如圖1-33所示),讓游戲邏輯更符合現(xiàn)實世界場景。同時,Actor與多進程模型同樣具備天然分布式屬性,在圖1-33中,不同的桌子可以運行在不同的物理機上。

圖1-33 Actor模型提供了更多的靈活性

對游戲服務(wù)端而言,Actor并發(fā)模型給游戲業(yè)務(wù)的分割提供了靈活性。

再回顧1.2.3節(jié)的簡單服務(wù)端程序,如果說Node.js提供了“單線事件模型”的運行環(huán)境,那么Skynet提供了Actor模型的運行環(huán)境(如圖1-34所示)。

圖1-34 類比Node.js和Skynet

現(xiàn)在,你已經(jīng)初步了解游戲服務(wù)端開發(fā)所需的關(guān)鍵技術(shù),以及一些需要注意的事項。本書分為“學(xué)以致用”“入木三分”“各個擊破”三大篇章,第一篇“學(xué)以致用”就是要讓讀者能夠以最快的速度做出成品。接下來我們使用Skynet引擎,先把游戲做出來!

主站蜘蛛池模板: 中山市| 获嘉县| 元阳县| 溧阳市| 寿宁县| 舟山市| 晋中市| 高雄县| 轮台县| 洛南县| 东兰县| 龙泉市| 久治县| 崇左市| 灌云县| 嘉兴市| 定南县| 方山县| 桐梓县| 调兵山市| 威海市| 佛学| 定边县| 岳西县| 马山县| 堆龙德庆县| 新乐市| 周口市| 科尔| 浠水县| 南投县| 靖江市| 永春县| 黄山市| 札达县| 卫辉市| 龙南县| 新野县| 墨竹工卡县| 侯马市| 正蓝旗|