摘要
創(chuàng)新互聯(lián)主要為客戶提供服務(wù)項目涵蓋了網(wǎng)頁視覺設(shè)計、VI標(biāo)志設(shè)計、營銷型網(wǎng)站建設(shè)、網(wǎng)站程序開發(fā)、HTML5響應(yīng)式網(wǎng)站建設(shè)公司、手機(jī)網(wǎng)站開發(fā)、微商城、網(wǎng)站托管及成都企業(yè)網(wǎng)站維護(hù)、WEB系統(tǒng)開發(fā)、域名注冊、國內(nèi)外服務(wù)器租用、視頻、平面設(shè)計、SEO優(yōu)化排名。設(shè)計、前端、后端三個建站步驟的完善服務(wù)體系。一人跟蹤測試的建站服務(wù)標(biāo)準(zhǔn)。已經(jīng)為成都純水機(jī)行業(yè)客戶提供了網(wǎng)站制作服務(wù)。
聽說過文字冒險游戲嗎? 如果你的年齡足夠大的話(就像我一樣),那么你可能聽說過、甚至玩過“back in the day”。在本文中,我將向你展示編寫的整個過程。這不僅僅是一個文本冒險游戲,而是一個能讓你和你的朋友們一起玩的,可以進(jìn)行任何劇情的文本冒險游戲引擎。 沒錯,我們將通過在添加多人游戲功能來增加它的趣味性。
文字冒險是最早的 RPG 形式的游戲之一,回到還沒有圖形畫面的時代,你只能通過閱讀 CRT 顯示器上黑色背景下的描述,并且依賴自己的想象力來推動游戲劇情的發(fā)展。
如果要懷舊的話,可能世界上第一個文字冒險游戲名叫 Colossal Cave Adventure(也許是叫 Adventure)。
文字冒險游戲 back in the day 的畫面
上圖是你實際看到的游戲畫面,這與我們現(xiàn)在的頂級 AAA 冒險游戲相差甚遠(yuǎn)。 盡管如此,但是他們玩起來卻很有趣,并會很容易的消磨你幾百個小時的時間,因為只有你自己自己坐在顯示器前,試圖找到打穿它的途徑。
可以理解的是,多年以來,文字冒險已經(jīng)被更好的視覺效果所取代,特別是在過去幾年里,游戲的協(xié)作性越強(qiáng),你可以和朋友們一起玩。 這是原始的文字冒險游戲所缺少的,同時也是我想在本文中提到的功能。
我們的目標(biāo)
可能你已經(jīng)從標(biāo)題中猜到了,本文的重點在于創(chuàng)建一個文字冒險引擎,并且讓你和朋友們一起玩,使你能夠與他們進(jìn)行協(xié)作,就像在玩“龍與地下城”這個游戲一樣。
在創(chuàng)建引擎時,聊天服務(wù)器和客戶端的工作了相當(dāng)大。 在本文中,我將向你展示設(shè)計思路、解釋引擎背后的架構(gòu)、客戶端如何與服務(wù)器交互以及這個游戲的規(guī)則。
為了讓你對我的目標(biāo)又一個直觀的感受,先上一張圖:
游戲客戶端的 UI 設(shè)計
這就是我們的目標(biāo)。 一旦達(dá)成這個目標(biāo),將會得到截圖而不是簡單和骯臟的模型。 所以,需要了解這個過程。首先要介紹的就是整體設(shè)計;然后介紹我將用來編碼的相關(guān)工具;最后我將向你展示一些核心代碼(當(dāng)然,還有指向完整代碼庫的鏈接)。
希望到最后,你能夠自己創(chuàng)造一個新的文字冒險游戲,并與朋友一起樂在其中!
設(shè)計階段
在設(shè)計階段,我將描述這個游戲的整體藍(lán)圖。 我會盡力不讓你覺得無聊,不過我認(rèn)為在給你展示第一行代碼之前,很有必要先搞清楚幕后的一些工作。
我想接下來介紹的這四個組件能夠提供相當(dāng)多的細(xì)節(jié):
引擎
游戲引擎或游戲服務(wù)器將會是REST API,并提供所有必需的功能。
我選擇REST API只是因為(對于這種類型的游戲)HTTP造成的延遲以及他的異步特性不會造成任何麻煩。 但是,我們必須為聊天服務(wù)器采用不同的路線。 在開始定義 API 之前,先需要定義引擎的功能。 所以,讓我們來看看吧。
特性 | 描述 |
---|---|
加入游戲 | 玩家可以通過指定的游戲ID來加入游戲。 |
創(chuàng)建一個新游戲 | 玩家還可以創(chuàng)建新的游戲?qū)嵗?引擎應(yīng)該返回一個ID,以便其他人可以使它來加入游戲。 |
返回場景 | 此功能應(yīng)返回玩家所在的當(dāng)前場景。 基本上,它將返回描述,包含所有相關(guān)信息(可能的操作、其中的對象等)。 |
與場景互動 | 這將是最復(fù)雜的一個,因為它將從客戶端獲取命令并執(zhí)行該操作——例如移動,攻擊,獲取,查看,讀取等等。 |
檢查庫存 | 雖然這是與游戲互動的一種方式,但它與場景并沒有直接關(guān)系。 因此,檢查每個玩家的庫存將被視為不同的操作。 |
關(guān)于移動
我們需要一種用來測量游戲中距離的方法,因為在游戲中玩家可以采取的核心行動之一就是移動。 我們需要用這個數(shù)字作為時間的衡量標(biāo)準(zhǔn),來簡化游戲的玩法。 考慮到這一類型的游戲具有基于回合的動作,例如戰(zhàn)斗,使用實際時鐘對時間進(jìn)行測量可能不是最好的。 所以我們將使用距離來測量時間(意味著距離為 8 比距離為 2 將需要更多的時間,從而允許我們做一些事情,例如為持續(xù)一定數(shù)量的“距離點”的玩家添加效果)。
考慮運動的另一個原因是不是一個人在玩這個游戲。 為簡單起見,引擎不會讓玩家隨意組隊(雖然這對未來可能是一個有趣的改進(jìn))。 該模塊的初始版本只允許個人朝著大多數(shù)參與者決定的地方移動。因此,必須以協(xié)商一致的方式進(jìn)行移動,這意味著每一步行動都將等待大多數(shù)人在行動之前提出請求。
戰(zhàn)斗
戰(zhàn)斗是這種游戲另一個非常重要的方面,我們不得不考慮將它添加到引擎中,否則我們最終會失去一些樂趣。
說實話,這并不需要重新發(fā)明輪子?;诨睾现频慕M隊對戰(zhàn)已經(jīng)存在了幾十年,所以在這里只實現(xiàn)這個機(jī)制的一個簡單版本。我們將把它與“龍與地下城”中的“主動性”這個概念混合起來,產(chǎn)生一個隨機(jī)數(shù)使戰(zhàn)斗更有活力。
換句話說,就是參與戰(zhàn)斗的每個人的行動順序?qū)浑S機(jī)化,其中包括敵人。
最后(雖然我將在下面詳細(xì)介紹這一點),你可以用設(shè)置的“攻擊力”值的物品。這些是你在戰(zhàn)斗中可以使用的道具;如果一個道具沒有這個屬性的話只能對敵人造成 0 點傷害。當(dāng)你試圖用這樣的道具進(jìn)行戰(zhàn)斗時,我們可能會添加一條消息,這樣你就能知道自己要做的事情是毫無意義的。
客戶端 - 服務(wù)器交互
現(xiàn)在來看看客戶端怎樣基于前面定義的功能與服務(wù)器進(jìn)行交互(目前還沒考慮端點,不過馬上就會講到這個):
客戶端與服務(wù)器之間的交互
客戶端和服務(wù)器之間的初始交互(從服務(wù)器的角度來看)是一個新游戲的開始,其步驟如下:
游戲的動作指令
一旦滿足了先決條件,玩家就可以開始游戲,通過聊天室分享他們的想法,并推動故事的發(fā)展。上圖顯示了所需的四個步驟。
以下步驟將作為游戲循環(huán)的一部分來運行,這意味著它們將會不斷重復(fù),一直到游戲結(jié)束。
作為額外步驟,雖然不是流程的一部分,但服務(wù)器將通知客戶端與它們相關(guān)的狀態(tài)的更新情況。
存在這個額外重復(fù)步驟的原因是玩家可以從其他玩家的動作中獲得更新。回想從一個地方移動另一個地方的需求;正如我之前所說那樣,一旦大多數(shù)玩家選擇了方向,那么所有玩家都會移動(不需要所有球員的輸入)。
不過 HTTP(前面已經(jīng)提到服務(wù)器為REST API)不允許這種類型的行為。所以,我們的選擇是:
根據(jù)我的經(jīng)驗,我傾向于選擇選項 2。實際上,我會(在本文中)使用redis來實現(xiàn)這種行為。
下圖演示了服務(wù)之間的依賴關(guān)系。
客戶端應(yīng)用程序與游戲引擎之間的交互
聊天服務(wù)器
我將把這個模塊的設(shè)計細(xì)節(jié)留給開發(fā)階段(本文不涉及這一部分)。話雖如此,我們?nèi)钥梢詻Q定一些事情。
我們可以確定的一件事是服務(wù)器的限制集合,這將簡化我們的工作。如果我們正確地玩牌,最終可能會有一個提供強(qiáng)大界面的服務(wù),從而允許我們?nèi)ミM(jìn)行擴(kuò)展甚至修改實現(xiàn),以提供更少的限制,而不會影響到游戲。
這就是聊天服務(wù)器。畢竟,它不會很復(fù)雜。在開始編碼之前還有很多工作要做,但是對于本文來說已經(jīng)足夠了。
客戶端
這是最后一個需要編碼的模塊,它將是最笨重的一個模塊。根據(jù)經(jīng)驗來看,我更喜歡讓客戶端笨重,使服務(wù)器輕巧。這樣為服務(wù)器開發(fā)新的客戶端會更加容易。
這是我們最終應(yīng)該采用的架構(gòu)。
最終架構(gòu)
我們要實現(xiàn)的ClI客戶端很簡單,不會實現(xiàn)任何非常復(fù)雜的東西。實際上,必須要解決的最復(fù)雜的部分是 UI,因為它是一個基于文本的界面。
客戶端應(yīng)用程序必須實現(xiàn)的功能如下:
稍后將詳細(xì)介紹客戶端的內(nèi)部結(jié)構(gòu)和設(shè)計。與此同時,讓我們完成設(shè)計階段的最后一部分:游戲文件。
游戲:JSON文件
這是它變得有趣的地方,因為到次為止,我已經(jīng)涵蓋了基本的微服務(wù)定義。其中一些可能會基于 REST,而另外一些可能會使用套接字,但本質(zhì)上它們都是一樣的:你定義并對它們編碼,然后它們提供服務(wù)。
我不打算對這個特定的組件做任何編碼,但我們?nèi)匀恍枰O(shè)計它?;旧衔覀兪窃趯崿F(xiàn)一種協(xié)議來定義游戲、它內(nèi)部的場景以及一切。
如果你想一想,文本冒險的核心基本上是一組相互連接的房間,里面是你可以與之互動的“事物”,所有這些都與一個引人入勝的故事聯(lián)系在一起?,F(xiàn)在我們的引擎不會處理最后一部分,這部分將取決于你。
現(xiàn)在回到相互連接的房間,對我來說這就像一個圖結(jié)構(gòu),如果我們還添加了前面提到的距離或移動速度的概念,還需要一個加權(quán)圖。這只是一組節(jié)點,它們具有權(quán)重(或只是一個數(shù)字 —— 不要糾結(jié)它的名稱),代表了它們之間的路徑。下面是一個示意圖(我喜歡通過觀察進(jìn)行學(xué)習(xí),所以只看圖,好嗎?):
這是一個加權(quán)圖 —— 就是這樣。我相信你已經(jīng)弄明白了,但為了完整起見,讓我告訴你一旦我們的引擎準(zhǔn)備就緒,你將會做些什么。
一旦開始設(shè)置游戲,你將創(chuàng)建地圖(就像你在下圖中左側(cè)看到的那樣)。然后將其轉(zhuǎn)換為加權(quán)圖,如圖所示。引擎將能夠接收它并讓你按正確的順序進(jìn)行瀏覽。
一個地牢的示例圖
通過上面的加權(quán)圖,可以確保玩家不能從入口一下子走到左翼。他們必須通過這兩者之間的節(jié)點,這樣做會消耗時間,可以用連接的權(quán)重來測量。
現(xiàn)在,進(jìn)入“有趣”的部分。來看看地圖在 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": "littorch2", "name": "Lit torch on the right", "triggers": [ { "action": "grab", //grab Lit torch on the right "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" }, { "id": "littorch3", "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 } } ] } } }
它看起來有很多內(nèi)容,但是如果你把它視為一個簡單的游戲描述,就會明白這是一個含有六個房間的地牢,每個房間都與其他房間相互連接,如上圖所示。
你的任務(wù)是穿越并探索它。你會發(fā)現(xiàn)有兩個地方可以找到武器(無論是在廚房還是在黑暗的房間,只要破壞掉椅子就能得到)。你也將面對一扇上鎖的門,所以,一旦找到鑰匙(位于類似辦公室的房間內(nèi)),就可以打開并用你收集到的武器和BOSS展開一場大戰(zhàn)。
你可以干掉它而獲勝,也可以被它殺死而輸?shù)簟?/p>
現(xiàn)在讓我們更詳細(xì)地了解整個 JSON 結(jié)構(gòu)及其中的三個部分。
Graph
這里包含節(jié)點之間的關(guān)系。基本上這一部分會直接轉(zhuǎn)換為我們之前看到的圖。
這部分的結(jié)構(gòu)非常簡單。它是一個節(jié)點列表,其中每個節(jié)點都包含以下屬性:
Game
本節(jié)包含常規(guī)設(shè)置和條件。特別是在上面的示例中,此部分包含輸贏條件。換句話說,在這兩個條件下,我們會讓游戲知道什么時候結(jié)束。
為了簡單起見,我添加了兩個條件:
Rooms
這一部分占了 JSON 文件很大的篇幅,也是最復(fù)雜的部分。在這里描述冒險中所有區(qū)域及其內(nèi)部所有房間。
每個房間都有一把鑰匙,使用我們之前定義的 ID。每個房間都有一個描述,一個物品列表,一個出口(或門)列表和一個非玩家角色(NPC)列表。在這些屬性中,唯一應(yīng)該被強(qiáng)制定義的屬性是描述,因為引擎需要這個屬性才能讓你明白所看到的內(nèi)容。如果有什么東西需要展示,它們只能在那里。
讓我們來看看這些屬性能為游戲做些什么。
description
這一項并不像想象的那么簡單,因為你看到的房間可能會根據(jù)不同的情況而變化。例如:如果你查看第一個房間的描述,就會注意到在默認(rèn)情況下,你將看不到任何東西,除非你有一個點亮的火炬。
因此,拾取物品并使用它們,可能會觸發(fā)影響游戲中其他部分的全局條件。
items
這些代表了你可以在房間內(nèi)找到的所有東西。每個項目都會共享與 graph 節(jié)點相同的 ID 和名稱。
它們還有“目標(biāo)”屬性,該屬性指示一旦拾取該道具應(yīng)放在哪里。這是有意義的,因為你手上只能裝備一個道具,而在背包中可以存放很多的道具。
最后,其中一些道具可能會觸發(fā)其他操作或者狀態(tài)更新,具體取決于玩家決定用它們做什么。其中一個例子就是從入口處點燃的火把。如果你拿著一個,將在游戲中觸發(fā)狀態(tài)更新,這反過來將使游戲向你顯示下一個房間的不同描述。
道具也可以有“子道具”,一旦原始道具被銷毀(例如通過“分解”操作)就會發(fā)揮作用。一個道具可以被分解為多個,并在“subitems”元素中定義。
本質(zhì)上,此元素只是一個新道具的數(shù)組,其中還包含可以觸發(fā)其創(chuàng)建的一組操作?;旧峡梢愿鶕?jù)你對原始道具執(zhí)行的操作創(chuàng)建不同的子道具。
最后,有些物品會有“傷害”屬性。所以如果你用某個道具擊中 NPC,該值用于從中減去生命。
exits
出口是與道具分開的實體,因為引擎需要知道你是否能夠根據(jù)其狀態(tài)去遍歷它們。否則被鎖定的出口無法讓你通過,除非你把它的狀態(tài)改為已解鎖。
NPC
最后,NPC 將成為另一個列表的一部分。它們是有狀態(tài)信息的項目,引擎將使用這些狀態(tài)信息來了解每個項目的行為方式。在我們的例子中定義的是 “hp”,它代表健康狀態(tài),還有“damage”,就像武器一樣,每次命中將從玩家的健康狀況中減去相應(yīng)的值。
這就是我創(chuàng)造的地牢。內(nèi)容很多,將來我可能會考慮寫一個編輯器,來簡化 JSON 文件的創(chuàng)建。但就目前而言還沒有必要。
你可能還沒有意識到,這樣在文件中定義游戲是有很大好處的,能夠像超級任天堂時代那樣切換 JSON 文件。只需加載一個新文件就能開始另一個游戲。非常簡單!
總結(jié)
感謝你能讀到這里。希望你能喜歡我所經(jīng)歷的設(shè)計過程,并將想法變?yōu)楝F(xiàn)實。我正在努力實現(xiàn)這一目標(biāo)。我們以后可能會意識到,今天定義的內(nèi)容可能會不起作用,出現(xiàn)這種情況時,我們將不得不回溯并修復(fù)它。
我敢肯定,有很多方法可以對這里提出的想法進(jìn)行改善,并創(chuàng)建一個地獄的引擎。但是這需要在本文中添加的更多的內(nèi)容,為了不讓讀者感到無聊,所以就先這樣吧。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對創(chuàng)新互聯(lián)的支持。
文章題目:使用Node.js實現(xiàn)一個多人游戲服務(wù)器引擎
標(biāo)題鏈接:http://jinyejixie.com/article46/ggcgeg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)建站、小程序開發(fā)、云服務(wù)器、App開發(fā)、商城網(wǎng)站、營銷型網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)