用 Node.js 寫一個多人遊戲服務器引擎

翻譯:瘋狂的技術宅
原文: https://www.smashingmagazine....

本文首發微信公衆號:jingchengyideng
歡迎關注,天天都給你推送新鮮的前端技術文章前端


摘要

據說過文字冒險遊戲嗎? 若是你的年齡足夠大的話(就像我同樣),那麼你可能據說過、甚至玩過「back in the day」。在本文中,我將向你展現編寫的整個過程。這不只僅是一個文本冒險遊戲,而是一個能讓你和你的朋友們一塊兒玩的,能夠進行任何劇情的文本冒險遊戲引擎。 沒錯,咱們將經過在添加多人遊戲功能來增長它的趣味性。node


文字冒險是最先的 RPG 形式的遊戲之一,回到尚未圖形畫面的時代,你只能經過閱讀 CRT 顯示器上黑色背景下的描述,而且依賴本身的想象力來推進遊戲劇情的發展。程序員

若是要懷舊的話,可能世界上第一個文字冒險遊戲名叫 Colossal Cave Adventure(也許是叫 Adventure)。面試

文字冒險遊戲 back in the day 的畫面

文字冒險遊戲 back in the day 的畫面json

上圖是你實際看到的遊戲畫面,這與咱們如今的頂級 AAA 冒險遊戲相差甚遠。 儘管如此,可是他們玩起來卻頗有趣,並會很容易的消磨你幾百個小時的時間,由於只有你本身本身坐在顯示器前,試圖找到打穿它的途徑。segmentfault

能夠理解的是,多年以來,文字冒險已經被更好的視覺效果所取代,特別是在過去幾年裏,遊戲的協做性越強,你能夠和朋友們一塊兒玩。 這是原始的文字冒險遊戲所缺乏的,同時也是我想在本文中提到的功能。windows

咱們的目標

可能你已經從標題中猜到了,本文的重點在於建立一個文字冒險引擎,而且讓你和朋友們一塊兒玩,使你可以與他們進行協做,就像在玩「龍與地下城」這個遊戲同樣。數組

在建立引擎時,聊天服務器和客戶端的工做了至關大。 在本文中,我將向你展現設計思路、解釋引擎背後的架構、客戶端如何與服務器交互以及這個遊戲的規則。服務器

爲了讓你對個人目標又一個直觀的感覺,先上一張圖:微信

遊戲客戶端的 UI 設計

遊戲客戶端的 UI 設計

這就是咱們的目標。 一旦達成這個目標,將會獲得截圖而不是簡單和骯髒的模型。 因此,須要瞭解這個過程。首先要介紹的就是總體設計;而後介紹我將用來編碼的相關工具;最後我將向你展現一些核心代碼(固然,還有指向完整代碼庫的連接)。

但願到最後,你可以本身創造一個新的文字冒險遊戲,並與朋友一塊兒樂在其中!

設計階段

在設計階段,我將描述這個遊戲的總體藍圖。 我會盡力不讓你以爲無聊,不過我認爲在給你展現第一行代碼以前,頗有必要先搞清楚幕後的一些工做。

我想接下來介紹的這四個組件可以提供至關多的細節:

  • 引擎
    這將成爲遊戲的主服務器。遊戲規則會在這裏實現,它將爲任何類型的客戶端提供技術無關接口。本項目中咱們將實現終端類型的客戶端,可是你能夠用Web客戶端或者你喜歡的任何其餘類型。
  • 聊天服務器
    由於它的複雜性足以再寫一篇文章了,因此這項服務也會擁有本身的模塊。聊天服務器負責讓玩家在遊戲的過程當中彼此通訊。
  • 客戶端
    如前文所述,這將是一個終端類型的客戶端,在理想狀況下,它看起來與以前的模型相似。它將利用引擎和聊天服務器所提供的服務。
  • 遊戲( JSON文件 )
    最後,我將介紹實際遊戲的定義。這部分的重點是建立一個能夠運行任何遊戲的引擎,只要你的遊戲文件符合引擎的要求便可。因此,即便這不須要編碼,我也將解釋如何構建冒險文件以便未來編寫咱們本身的冒險規則。

引擎

遊戲引擎或遊戲服務器將會是REST API,並提供全部必需的功能。

我選擇REST API只是由於(對於這種類型的遊戲)HTTP形成的延遲以及他的異步特性不會形成任何麻煩。 可是,咱們必須爲聊天服務器採用不一樣的路線。 在開始定義 API 以前,先須要定義引擎的功能。 因此,讓咱們來看看吧。

特性 描述
加入遊戲 玩家能夠經過指定的遊戲ID來加入遊戲。
建立一個新遊戲 玩家還能夠建立新的遊戲實例。 引擎應該返回一個ID,以便其餘人可使它來加入遊戲。
返回場景 此功能應返回玩家所在的當前場景。 基本上,它將返回描述,包含全部相關信息(可能的操做、其中的對象等)。
與場景互動 這將是最複雜的一個,由於它將從客戶端獲取命令並執行該操做——例如移動,攻擊,獲取,查看,讀取等等。
檢查庫存 雖然這是與遊戲互動的一種方式,但它與場景並無直接關係。 所以,檢查每一個玩家的庫存將被視爲不一樣的操做。
關於移動

咱們須要一種用來測量遊戲中距離的方法,由於在遊戲中玩家能夠採起的核心行動之一就是移動。 咱們須要用這個數字做爲時間的衡量標準,來簡化遊戲的玩法。 考慮到這一類型的遊戲具備基於回合的動做,例如戰鬥,使用實際時鐘對時間進行測量可能不是最好的。 因此咱們將使用距離來測量時間(意味着距離爲 8 比距離爲 2 將須要更多的時間,從而容許咱們作一些事情,例如爲持續必定數量的「距離點」的玩家添加效果)。

考慮運動的另外一個緣由是否是一我的在玩這個遊戲。 爲簡單起見,引擎不會讓玩家隨意組隊(雖然這對將來多是一個有趣的改進)。 該模塊的初始版本只容許我的朝着大多數參與者決定的地方移動。所以,必須以協商一致的方式進行移動,這意味着每一步行動都將等待大多數人在行動以前提出請求。

戰鬥

戰鬥是這種遊戲另外一個很是重要的方面,咱們不得不考慮將它添加到引擎中,不然咱們最終會失去一些樂趣。

說實話,這並不須要從新發明輪子。基於回合制的組隊對戰已經存在了幾十年,因此在這裏只實現這個機制的一個簡單版本。咱們將把它與「龍與地下城」中的「主動性」這個概念混合起來,產生一個隨機數使戰鬥更有活力。

換句話說,就是參與戰鬥的每一個人的行動順序將會被隨機化,其中包括敵人。

最後(雖然我將在下面詳細介紹這一點),你能夠用設置的「攻擊力」值的物品。這些是你在戰鬥中可使用的道具;若是一個道具沒有這個屬性的話只能對敵人形成 0 點傷害。當你試圖用這樣的道具進行戰鬥時,咱們可能會添加一條消息,這樣你就能知道本身要作的事情是毫無心義的。

客戶端 - 服務器交互

如今來看看客戶端怎樣基於前面定義的功能與服務器進行交互(目前還沒考慮端點,不過立刻就會講到這個):

客戶端與服務器之間的交互

客戶端與服務器之間的交互

客戶端和服務器之間的初始交互(從服務器的角度來看)是一個新遊戲的開始,其步驟以下:

  1. 建立一個新遊戲
    客戶端請求向服務器建立新遊戲。
  2. 建立聊天室
    雖然沒有明確說明,可是服務器不僅是在聊天服務器中建立聊天室,並且還設置好了所需的一切,能夠容許一組玩家進行遊戲。
  3. 返回遊戲的元數據
    一旦服務器爲玩家建立好了遊戲和聊天室,那麼客戶端會在後續請求用到這個信息。這是客戶端能夠用來標識本身和將要加入的遊戲實例的一組ID。
  4. 手動分享遊戲ID
    這一步必須由玩家本身完成。咱們能夠提出某種共享機制,但我會將它留在願望清單上等待未來改進。
  5. 加入遊戲
    這個很是簡單。每一個人都有一個 ID,客戶端經過這個 ID 加入遊戲。
  6. 加入聊天室
    最後,玩家的客戶端程序將經過遊戲的元數據加入對應的聊天室。這是遊戲開始前的最後一步。一旦完成全部操做,玩家就能夠開始在遊戲中冒險了!

遊戲的動做指令

遊戲的動做指令

一旦知足了先決條件,玩家就能夠開始遊戲,經過聊天室分享他們的想法,並推進故事的發展。上圖顯示了所需的四個步驟。

如下步驟將做爲遊戲循環的一部分來運行,這意味着它們將會不斷重複,一直到遊戲結束。

  1. 請求場景。
    客戶端程序將請求當前場景的元數據。這是循環每次迭代的第一步。
  2. 返回元數據。
    服務器將發回當前場景的元數據。這些信息中包括通常描述,從中能夠找到的對象以及它們彼此之間的關係。
  3. 發送命令。
    好戲開始。這是玩家的主要輸入方式。它包括玩家想要執行的操做,以及可選的操做目標(例如吹蠟燭、抓住岩石等)。
  4. 對發來的命令作出響應

    這應該屬於第二步,但爲了清楚起見,我把它做爲額外步驟。主要區別在於第二步能夠被認爲是這個循環的開始,而這一步考慮到你已經開始進行遊戲了,所以,服務器須要瞭解這個動做將影響誰(單個或全部玩家)。

做爲額外步驟,雖然不是流程的一部分,但服務器將通知客戶端與它們相關的狀態的更新狀況。

存在這個額外重複步驟的緣由是玩家能夠從其餘玩家的動做中得到更新。回想從一個地方移動另外一個地方的需求;正如我以前所說那樣,一旦大多數玩家選擇了方向,那麼全部玩家都會移動(不須要全部球員的輸入)。

不過 HTTP(前面已經提到服務器爲REST API)不容許這種類型的行爲。因此,咱們的選擇是:

  1. 每隔 X 秒從客戶端輪詢,
  2. 使用某種與客戶端-服務器鏈接通訊機制並行工做的通知系統。

根據個人經驗,我傾向於選擇選項 2。實際上,我會(在本文中)使用Redis來實現這種行爲。

下圖演示了服務之間的依賴關係。

客戶端程序和遊戲引擎之間的交互

客戶端應用程序與遊戲引擎之間的交互

聊天服務器

我將把這個模塊的設計細節留給開發階段(本文不涉及這一部分)。話雖如此,咱們仍能夠決定一些事情。

咱們能夠肯定的一件事是服務器的限制集合,這將簡化咱們的工做。若是咱們正確地玩牌,最終可能會有一個提供強大界面的服務,從而容許咱們去進行擴展甚至修改實現,以提供更少的限制,而不會影響到遊戲。

  • 每一個組隊只有一個房間。
    咱們不會建立子組隊。這和不讓組隊分裂是相輔相成的。也許一旦之後咱們實現了這個加強功能,容許建立子組和自定義聊天室或許是一個好主意。
  • 沒有私信功能。
    這純粹是爲了簡化,可是隻有羣聊並不夠好。目前咱們不須要私信。請記住,任什麼時候候只研究你的最小化可行產品,儘可能避免掉進沒必要要功能的陷阱;這是一條危險的道路,很難從困境中擺脫出來。
  • 不會保存留言。
    換句話說,若是你離開組隊,將會丟失這些信息。這將極大地簡化咱們的任務,由於咱們沒必要處理任何類型的數據存儲,也沒必要浪費時間來優化存儲和恢復舊消息的數據結構。它們都存在於內存中,只要聊天室處於活動狀態,就會一直存在。一旦關閉,就會簡單地對它們說Goodbye!
  • 經過網絡套接字進行通訊
    可悲的是,咱們的客戶將不得不處理雙重溝通渠道:遊戲引擎的 RESTful 和聊天服務器的套接字。這可能會增長客戶端的複雜性,但與此同時,它將爲每一個模塊使用最佳通訊方法。 (在聊天服務器上強制 REST 或在遊戲服務器上強制使用套接字沒有任何意義。這種方法會增長服務器端代碼的複雜性,這也是處理業務邏輯的代碼,因此讓咱們關注目前的問題。)

這就是聊天服務器。畢竟,它不會很複雜。在開始編碼以前還有不少工做要作,可是對於本文來講已經足夠了。

客戶端

這是最後一個須要編碼的模塊,它將是最笨重的一個模塊。根據經驗來看,我更喜歡讓客戶端笨重,使服務器輕巧。這樣爲服務器開發新的客戶端會更加容易。

這是咱們最終應該採用的架構。

最終架構

最終架構

咱們要實現的ClI客戶端很簡單,不會實現任何很是複雜的東西。實際上,必需要解決的最複雜的部分是 UI,由於它是一個基於文本的界面。

客戶端應用程序必須實現的功能以下:

  1. 建立一個新遊戲
    由於我但願儘量保持簡單,因此這隻能經過 CLI 界面完成。實際用戶界面只會在加入遊戲後被用到,這把咱們帶到下一個問題。
  2. 加入現有遊戲
    玩家能夠根據由上一條返回的遊戲編號來加入遊戲。另外,這件事應該可以在沒有 UI 的狀況下完成,所以這個功能將成爲開始使用文本 UI 所需的過程的一部分。
  3. 解析遊戲定義文件
    咱們將對這點進行的討論,客戶端應該可以理解這些文件,以便可以理解要顯示的內容,並知道應該如何使用這個數據。
  4. 與冒險互動。
    基本上,這使玩家可以在任什麼時候間與給出描述的環境進行交互。
  5. 爲每位玩家維護揹包內容
    客戶端的每一個實例都將在內存中包含一份道具列表。此列表將被備份。
  6. 支持聊天
    客戶端程序還須要鏈接到聊天服務器,並使用戶登陸到組隊的聊天室。

稍後將詳細介紹客戶端的內部結構和設計。與此同時,讓咱們完成設計階段的最後一部分:遊戲文件。

遊戲:JSON文件

這是它變得有趣的地方,由於到次爲止,我已經涵蓋了基本的微服務定義。其中一些可能會基於 REST,而另一些可能會使用套接字,但本質上它們都是同樣的:你定義並對它們編碼,而後它們提供服務。

我不打算對這個特定的組件作任何編碼,但咱們仍然須要設計它。基本上咱們是在實現一種協議來定義遊戲、它內部的場景以及一切。

若是你想想,文本冒險的核心基本上是一組相互鏈接的房間,裏面是你能夠與之互動的「事物」,全部這些都與一個引人入勝的故事聯繫在一塊兒。如今咱們的引擎不會處理最後一部分,這部分將取決於你。

如今回到相互鏈接的房間,對我來講這就像一個圖結構,若是咱們還添加了前面提到的距離或移動速度的概念,還須要一個加權圖。這只是一組節點,它們具備權重(或只是一個數字 —— 不要糾結它的名稱),表明了它們之間的路徑。下面是一個示意圖(我喜歡經過觀察進行學習,因此只看圖,好嗎?):

加權圖表示例

這是一個加權圖 —— 就是這樣。我相信你已經弄明白了,但爲了完整起見,讓我告訴你一旦咱們的引擎準備就緒,你將會作些什麼。

一旦開始設置遊戲,你將建立地圖(就像你在下圖中左側看到的那樣)。而後將其轉換爲加權圖,如圖所示。引擎將可以接收它並讓你按正確的順序進行瀏覽。

一個地牢的示例圖

一個地牢的示例圖

經過上面的加權圖,能夠確保玩家不能從入口一會兒走到左翼。他們必須經過這二者之間的節點,這樣作會消耗時間,能夠用鏈接的權重來測量。

如今,進入「有趣」的部分。來看看地圖在 JSON 格式中的樣子。這個JSON將包含不少信息:

{
    "graph": [
            { "id": "entrance", "name": "Entrance", "north": { "node": "1stroom", "distance": 1 } },
     { "id": "1st room", "name": "1st Room", "south": {"node": "entrance", "distance": 1} , "north": { "node": "bigroom", "distance": 1} } ,
     { "id": "bigroom",
       "name": "Big room",
       "south": { "node": "1stroom", "distance": 1},
       "north": { "node": "bossroom", "distance": 2},
       "east":  { "node": "rightwing", "distance": 3} ,
       "west":  { "node": "leftwing", "distance": 3}
     },
     { "id": "bossroom", "name": "Boss room", "south": {"node": "bigroom", "distance": 2} }
     { "id": "leftwing", "name": "Left Wing", "east": {"node": "bigroom", "distance": 3} }
     { "id": "rightwing", "name": "Right Wing", "west": { "node": "bigroom", "distance": 3 } }
    ],
    "game": {
     "win-condition": {
       "source": "finalboss",
       "condition": {
         "type": "comparison",
         "left": "hp",
         "right": "0",
         "symbol": "<="
       }
     },
     "lose-condition": {
       "source": "player",
       "condition": {
         "type": "comparison",
         "left": "hp",
         "right": "0",
         "symbol": "<="
       }
     }
    },
    "rooms": {
     "entrance": {
       "description": {
         "default": "You're at the entrance of the dungeon. There are two lit torches on each wall (one on your right and one on your left). You see only one path: ahead."
       },
       "items": [
         {
           "id": "littorch1",
           "name": "Lit torch on the right",  
           "triggers": [
             {
               "action": "grab", //grab Lit torch on the right
               "effect":{
                 "statusUpdate": "has light",
                 "target": "game",
               }
             }
           ] ,
           "destination": "hand"
         },
         {
           "id": "littorch2",
           "name": "Lit torch on the left",  
           "triggers": [
             {
               "action": "grab", //grab Lit torch on the left
               "effect":{
                 "statusUpdate": "has light",
                 "target": "game",
               }
             }
           ] ,
           "destination": "hand"
         
         }
       ]
     },
     "1stroom": {
       "description": {
         "default": "You're in a very dark room. There are no windows and no source of light, other than the one at the entrance. You get the feeling you're not alone here.",
         "conditionals": {
           "has light": "The room you find yourself in appears to be empty, aside from a single chair in the right corner. There appears to be only one way out: deeper into the dungeon."
         }
       },
       "items": [
         {
           "id": "chair",
           "name": "Wooden chair",
           "details": "It's a wooden chair, nothing fancy about it. It appears to have been sitting here, untouched, for a while now.",
           "subitems": [
             {    "id": "woodenleg",  
               "name": "Wooden leg",
               "triggeractions": [
                 { "action": "break", "target": "chair"},  //break 
                 { "action": "throw", "target": "chair"} //throw 
               ],
               "destination": "inventory",
               "damage": 2
             }
           ]
         }
       ]
     },
     "bigroom": {
       "description": {
         "default": "You've reached the big room. On every wall are torches lighting every corner. The walls are painted white, and the ceiling is tall and filled with painted white stars on a black background. There is a gateway on either side and a big, wooden double door in front of you."
       },
       "exits": {
         "north": { "id": "bossdoor",  "name": "Big double door", "status": "locked", "details": "A aig, wooden double door. It seems like something big usually comes through here."}
       },
       "items": []
     },
     "leftwing": {
       "description": {
         "default": "Another dark room. It doesn't look like it's that big, but you can't really tell what's inside. You do, however, smell rotten meat somewhere inside.",
         "conditionals": {
           "has light":  "You appear to have found the kitchen. There are tables full of meat everywhere, and a big knife sticking out of what appears to be the head of a cow."
         }
       },
       "items": [
         { "id": "bigknife", "name": "Big knife", "destination": "inventory", "damage": 10}
       ]
     },
     "rightwing": {
       "description": {
         "default": "This appear to be some sort of office. There is a wooden desk in the middle, torches lighting every wall, and a single key resting on top of the desk."
       },
       "items": [
         {     "id": "key",
           "name": "Golden key",
           "details": "A small golden key. What use could you have for it?",
           "destination": "inventory",
           "triggers": [{
             "action": "use", //use  on north exit (contextual)
             "target": {
               "room": "bigroom",
               "exit": "north"
             },
             "effect": {
               "statusUpdate": "unlocked",
               "target": {
                 "room": "bigroom",
                 "exit": "north"
               }
             }
           }
         ]
         }
       ]
     },
     "bossroom": {
       "description": {
         "default": "You appear to have reached the end of the dungeon. There are no exits other than the one you just came in through. The only other thing that bothers you is the hulking giant looking like it's going to kill you, standing about 10 feet from you."
       },
       "npcs": [
         {
           "id": "finalboss",
           "name": "Hulking Ogre",
           "details": "A huge, green, muscular giant with a single eye in the middle of his forehead. It doesn't just look bad, it also smells like hell.",
           "stats":  {
             "hp": 10,
             "damage": 3
           }
         }
       ]
     }
    }
}

它看起來有不少內容,可是若是你把它視爲一個簡單的遊戲描述,就會明白這是一個含有六個房間的地牢,每一個房間都與其餘房間相互鏈接,如上圖所示。

你的任務是穿越並探索它。你會發現有兩個地方能夠找到武器(不管是在廚房仍是在黑暗的房間,只要破壞掉椅子就能獲得)。你也將面對一扇上鎖的門,因此,一旦找到鑰匙(位於相似辦公室的房間內),就能夠打開並用你收集到的武器和BOSS展開一場大戰。

你能夠幹掉它而獲勝,也能夠被它殺死而輸掉。

如今讓咱們更詳細地瞭解整個 JSON 結構及其中的三個部分。

Graph

這裏包含節點之間的關係。基本上這一部分會直接轉換爲咱們以前看到的圖。

這部分的結構很是簡單。它是一個節點列表,其中每一個節點都包含如下屬性:

  • 一個標識遊戲中全部其餘節點的惟一 ID;
  • 一個名稱,其實是給玩家看到的 ID 版本;
  • 一組指向其餘節點的連接。這能夠經過四個可能的 key 來描述:north, south, east 和 west.。咱們能夠經過添加這四個組合來增長更多方向。每一個連接都包含相關節點的 ID 以及該關係的距離(或權重)。
Game

本節包含常規設置和條件。特別是在上面的示例中,此部分包含輸贏條件。換句話說,在這兩個條件下,咱們會讓遊戲知道何時結束。

爲了簡單起見,我添加了兩個條件:

  • 要麼經過殺死 BOSS 獲勝,
  • 或者由於被殺而輸掉。
Rooms

這一部分佔了 JSON 文件很大的篇幅,也是最複雜的部分。在這裏描述冒險中全部區域及其內部全部房間。

每一個房間都有一把鑰匙,使用咱們以前定義的 ID。每一個房間都有一個描述,一個物品列表,一個出口(或門)列表和一個非玩家角色(NPC)列表。在這些屬性中,惟一應該被強制定義的屬性是描述,由於引擎須要這個屬性才能讓你明白所看到的內容。若是有什麼東西須要展現,它們只能在那裏。

讓咱們來看看這些屬性能爲遊戲作些什麼。

description

這一項並不像想象的那麼簡單,由於你看到的房間可能會根據不一樣的狀況而變化。例如:若是你查看第一個房間的描述,就會注意到在默認狀況下,你將看不到任何東西,除非你有一個點亮的火炬。

所以,拾取物品並使用它們,可能會觸發影響遊戲中其餘部分的全局條件。

items

這些表明了你能夠在房間內找到的全部東西。每一個項目都會共享與 graph 節點相同的 ID 和名稱。

它們還有「目標」屬性,該屬性指示一旦拾取該道具應放在哪裏。這是有意義的,由於你手上只能裝備一個道具,而在揹包中能夠存放不少的道具。

最後,其中一些道具可能會觸發其餘操做或者狀態更新,具體取決於玩家決定用它們作什麼。其中一個例子就是從入口處點燃的火把。若是你拿着一個,將在遊戲中觸發狀態更新,這反過來將使遊戲向你顯示下一個房間的不一樣描述。

道具也能夠有「子道具」,一旦原始道具被銷燬(例如經過「分解」操做)就會發揮做用。一個道具能夠被分解爲多個,並在「subitems」元素中定義。

本質上,此元素只是一個新道具的數組,其中還包含能夠觸發其建立的一組操做。基本上能夠根據你對原始道具執行的操做建立不一樣的子道具。

最後,有些物品會有「傷害」屬性。因此若是你用某個道具擊中 NPC,該值用於從中減去生命。

exits

出口是與道具分開的實體,由於引擎須要知道你是否可以根據其狀態去遍歷它們。不然被鎖定的出口沒法讓你經過,除非你把它的狀態改成已解鎖。

NPC

最後,NPC 將成爲另外一個列表的一部分。它們是有狀態信息的項目,引擎將使用這些狀態信息來了解每一個項目的行爲方式。在咱們的例子中定義的是 「hp」,它表明健康狀態,還有「damage」,就像武器同樣,每次命中將從玩家的健康情況中減去相應的值。

這就是我創造的地牢。內容不少,未來我可能會考慮寫一個編輯器,來簡化 JSON 文件的建立。但就目前而言尚未必要。

你可能尚未意識到,這樣在文件中定義遊戲是有很大好處的,可以像超級任天堂時代那樣切換 JSON 文件。只需加載一個新文件就能開始另外一個遊戲。很是簡單!

總結

感謝你能讀到這裏。但願你能喜歡我所經歷的設計過程,並將想法變爲現實。我正在努力實現這一目標。咱們之後可能會意識到,今天定義的內容可能會不起做用,出現這種狀況時,咱們將不得不回溯並修復它。

我敢確定,有不少方法能夠對這裏提出的想法進行改善,並建立一個地獄的引擎。可是這須要在本文中添加的更多的內容,爲了避免讓讀者感到無聊,因此就先這樣吧。


歡迎繼續閱讀本專欄其它高贊文章:


本文首發微信公衆號:jingchengyideng

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

相關文章
相關標籤/搜索