搜尋
首頁web前端js教程JS仿熱血傳奇遊戲(附代碼)

這次帶給大家JS仿熱血傳奇遊戲(附程式碼),JS仿熱血傳奇遊戲的注意事項有哪些,以下就是實戰案例,一起來看一下。

遊戲的第一個版本開發於14年,瀏覽器端使用html css js,服務端使用asp php,通訊採用ajax,資料儲存使用access mySql。不過由於一些問題(當時還不會用node,用asp寫複雜的邏輯真的會寫吐;當時對canvas寫的也少,dom渲染很容易達到效能瓶頸),已經廢棄。後來用canvas重製了一版。本文寫於18年。

1.開發前的準備

# 為什麼要用Javascript來實作一款比較複雜的PC端遊戲

# 1.js實作PC端網遊是可行的。隨著PC、手機硬體配置的升級和瀏覽器的更新換代,以及H5各種庫的發展,js實現一款網遊的難度越來越低。這裡的難度主要是兩方面:瀏覽器的效能;js程式碼是否足夠易於擴展,以滿足於一款邏輯極其複雜的遊戲的迭代。

2.現階段的js遊戲裡,很少有規模較大的可參考。涉及到多人連線、服務端資料儲存、複雜互動的遊戲,大多數(幾乎全部)都是用flash開發的。但是flash畢竟在衰落,而js發展迅速,而且只要有瀏覽器就可以運作。

為什麼選擇了一款2001年的熱血傳奇遊戲

# 第一個原因是對舊遊戲的情懷; 當然更重要的另一個原因是,別的遊戲要嘛我不會玩、要嘛我會玩但沒有素材(圖片、音效等)。花很大精力去收集一個遊戲的地圖、人物怪物模型、物品和裝備圖,然後去處理、解析一遍再用於js開發,我覺得是浪費時間。

由於我以前蒐集了一些傳奇遊戲的素材,並且幸運地找到了提取熱血傳奇客戶端資源文件的方法( github地址 ),所以可以直接開始寫碼,省去了一些準備時間。

可能的困難

# 1.瀏覽器的運作效能:這應該是最困難的一點。假如遊戲要維持40幀,那麼每幀只有25ms的時間留給js計算。且由於渲染通常比計算耗性能,實際上留給js的時間只有10毫秒左右。

2.防作弊:如何避免使用者直接呼叫介面或篡改網路請求資料?由於目標是用js實現比較複雜的遊戲,並且任何網路遊戲都需要考慮這一點,一定會有相對成熟的方案。此處不是本文重點。

2.整體設計

瀏覽器端

# 畫面渲染使用canvas。

相較於dom(p) css,canvas可以處理比較複雜的場景渲染和事件管理,例如下面這個場景,涉及了四張圖片:玩家、動物、地上的物品、最下層的地圖圖片。 (實際上還有地上的影子,滑鼠指向人物、動物、物品時出現的相應名稱,以及地面上的陰影。為了方便讀懂,先不考慮這麼多內容。) 

######################################## 這時,如果希望實現「點擊動物、攻擊動物;點擊物品、撿起物品」的效果,那麼需要對動物和物品進行事件監聽。如果採用dom的方式,那麼會出現幾點難於處理的問題:####### a.渲染的順序和###事件處理###的順序不同(有時候z-index小的需要先處理事件),需要額外處理。例如這個上面的例子裡:點擊怪物、點擊物品的時候也容易點到人物,那麼需要給人物做「點擊事件穿透」的處理。而且事件處理的順序不固定:假如我有一個技能(例如遊戲裡的治療)需要點人物才可以釋放,那麼這時人物又需要有事件監聽。所以一個元素是否需要處理事件、處理事件的先後,是隨著遊戲狀態的不同而改變的,而 dom的事件綁定已經不能滿足需求 。 ###

b.有關聯的元素很難放在同一個dom節點中:例如玩家的模型、玩家的名字和玩家身上的技能畫效,理想情況下是放在一個

容器裡,方便管理(這樣幾個元素的定位就可以繼承父元素,不用分別處理位置了)。但是這樣,z-index會很難處理。例如玩家A在玩家B的上面,那麼A會被B遮擋,因此需要A的z-index小一些,但是又需要讓玩家A的名字不會被B的名字或影子遮擋,就無法實現。簡單點說, dom結構的可維護性會犧牲畫面所展現的效果,反之亦然 。

c.效能問題。即使犧牲了效果,用dom渲染,勢必出現很多嵌套關係,所有元素的style都在頻繁變化,連續觸發瀏覽器的repaint甚至reflow。

canvas渲染邏輯與專案邏輯分離

# 如果將canvas的各種渲染操作(如 drawImage 、 fillText 等)與專案程式碼放在一起,那麼勢必導致專案後期無法維護。翻了一下幾款現有的canvas庫,結合vue的資料綁定 調試工具的方式,搞了一個全新的canvas庫Easycanvas( github地址),並且像vue一樣支持通過一個插件來調試canvas中的元素。

這樣,整個遊戲的渲染部分就容易很多,只需要管理遊戲目前的狀態、並且根據服務端從socket傳回來的資料去更新資料就可以。 「資料的變化造成視圖的變化」這個環節由Easycanvas負責 。例如下圖的玩家包裹物品的實現,我們只需要給出包裹容器的位置、背包裡每個元素的排布規則,然後將每個包裹的物品綁定到一個array上,然後去管理這個array即可(資料映射到畫面的過程由Easycanvas負責)。 

# 例如,5行8列共40個物品的樣式可以透過如下的形式傳遞給Easycanvas(index為物品索引,物品x方向間距36,y方向間距32)。而這個邏輯是一成不變的,無論物品的陣列怎樣變化、包裹被拖曳到什麼位置,每個物品的相對位置都是固定的。至於canvas上的渲染則完全不需要專案本身來考慮,所以可維護性較好。

style: {
 tw: 30, th: 30,
 tx: function () {
 return 40 + index % 8 * 36;
 },
 ty: function () {
 return 31 + Math.floor(index / 8) * 32;
 }
}

canvas分層渲染

## 假設:遊戲需維持40幀,瀏覽器寬800高600,面積48萬(後面稱48萬為1個螢幕面積)。

如果用同一個canvas來呈現,那麼這個canvas的幀數40,每秒至少需要繪製40個螢幕面積。但是同一個座標點很可能出現多個元素重疊的情況,例如底部的UI、血條、按鈕就是重疊放置,他們又共同遮擋了場景地圖。所以這些加在一起,每秒瀏覽器的繪製量很容易達到100個螢幕面積以上。

這個繪製是很難優化的,因為整個canvas畫布的任何一個地方都在進行視圖的更新:可能是玩家和動物的移動、可能是按鈕的特效、可能是某個技能效果的變化。這樣的話,即使玩家不動,由於衣服「隨風飄飄」的效果(其實是精靈動畫播放到下一張圖),或者是地面上出現了一瓶藥水,都要引起整個canvas的重繪。因為 遊戲中幾乎不可能出現某一幀的畫面與上一幀毫無區別的情況,即使是遊戲畫面的一個局部,也很難保持不變 。整個遊戲的畫面永遠在更新。

因為 遊戲中幾乎不可能出現某一幀的畫面與上一幀毫無區別的情況 ,畫面永遠在更新。

因此,這次我採用了3個canvas重疊的排布方式。由於Easycanvas的事件處理支援傳遞,因此即使點到了最上面的canvas,如果沒有任何元素結束了某一次點擊,後面的canvas也可以接到這次事件。 3個canvas分別負責UI、地面(地圖)、精靈(人物、動物、技能特效等): 

這樣分層的好處是,每層最大幀數可以根據需要來調整:

# 例如UI層,因為很多UI平常是不動的,即使動也不會需要太精密的繪製,所以可以適當降低幀數,例如降低到20。這樣假如玩家的體力從100降到20,那麼可以在50ms內更新視圖,而50ms的切換是玩家感覺不出來的。因為像體力這種 UI層資料的變化很難在很短的時間內連續變化多次,而50ms的延遲是人很難感知的,所以不需要頻繁的繪製 。假如我們每秒節約了20幀,那麼很可能可以節省10個螢幕面積的繪製。

再如地面,只有玩家移動的時候,地圖才會改變。這樣,如果玩家不動,那麼每個畫面可以省去1個螢幕面積。由於需要確保玩家移動時的流暢感,地面的最大幀數不宜太低。假如地面為30幀,那麼玩家不動時,每秒就可以節約30個螢幕面積的繪製(這個項目中,地圖是幾乎繪滿螢幕的)。而且其它玩家、動物的移動不會改變地面,也不需要重繪地面這一層。

精靈層最大幀數不能降低,這層會展示遊戲的人物動作等核心部分,所以最大幀數設定為40.

這樣,每秒繪製的面積,玩家移動時可能是80~100個螢幕面積,而玩家不移動時可能只有50個螢幕面積。遊戲中,玩家停下來打怪、打字、整理物品、釋放技能都是站立不動的,因此 大量的時間裡都不會觸發地面的繪製,對性能的節約很大 。

伺服器端

由於目標是js實作一款多人網遊,所以服務端使用Node,使用socket與瀏覽器通訊。這樣做還有一個好處,就是一些 公用的邏輯可以在兩端重複使用 ,例如判斷地圖上某個座標點是否有障礙物。

Node端的玩家、場景等遊戲相關資料全部儲存與記憶體中,定期同步至檔案。每次Node服務啟動時,將資料從檔案讀取至記憶體。這樣可以玩家較多時,文件讀寫的頻率成指數上升,進而引發的效能問題。 (後來為了提高穩定,為檔案讀寫增加了一個緩衝,「記憶體-檔案-備份」的方式,以免讀寫過程中伺服器重新啟動導致的檔案損壞)。

Node端分介面、資料、實例等多層。 「介面」負責和瀏覽器端互動。 「數據」是一些靜態數據,例如某個藥品的名稱和效果、某個怪物的速度和體力,是遊戲規則的一部分。 「實例」是遊戲中的當前狀態,例如某個玩家身上的藥品,就是「藥品數據」的一個實例。再舉個例子,「鹿的實例」擁有「目前血量」這個屬性,鹿A可能是10,鹿B可能是14,而「鹿」本身只有「初始血量」。

3.場景地圖的實作

地圖場景

下面開始介紹地圖場景部分,仍然是依賴 Easycanvas 進行渲染。

思考

由於玩家是始終固定在螢幕中心的,所以玩家的移動,實際上是地圖的移動。例如玩家像左跑,地圖就向右平移即可。剛才已經提到,玩家處於3個canvas中的中間一層,而地圖屬於底層,因此玩家一定遮住地圖。

這樣看起來是合理的,但是假如地圖中有一棵樹,那麼「玩家的層次始終高於樹」就不對了。這時,有2種大的解:

# 地圖分層,「地面」與「地上」拆開。將玩家處於兩層之間,例如下圖,左側是地上、右側是地面,然後重疊繪製,把人物夾在中間:

這樣看似解決了問題,其實引入了2個新的問題:第一個是,玩家有時可能會被「地上」的東西遮擋(例如一棵樹),有時又需要能夠遮擋「地上」的東西(例如站在這棵樹的下方,頭部會遮擋住樹)。另一個問題是渲染的效能消耗會增加。由於玩家是時時刻刻在變的,「地上」這一層需要頻繁重繪。這樣做也打破了最初的設計——盡量節省地面大地圖的渲染,導致canvas的分層更加複雜。

地圖不分層,「地面」與「地上」在一起繪製。當玩家處於樹後的時候,將玩家的透明度設為0.5,例如下圖:

這樣做只有一個壞處:玩家的身體不是都不透明、就是都半透明(怪物在地圖上行走也會有這個效果),不會完全真實。因為理想的效果是存在玩家的身體被遮住一部分的場景的。但是這樣做對效能友好,而且程式碼易於維護,目前我也採用了這個方案。

那麼要如何判斷「地圖」這張圖片哪些地方是樹呢?遊戲通常會有一個大的地圖描述檔(其實就是一個Array),透過0、1、2這樣的數字來標識哪些地方可以通過、哪些地方有障礙物、哪些地方是傳送點等等。熱血傳奇中的這個「描述文件」就是48x32為最小單位進行描述的,所以玩家在傳奇中的行動會有一種「棋盤」的感覺。單位越小越流暢,但是佔用的體積越大、產生這個描述的過程就越耗時。

下面開始正題。

實作

我找了一個朋友幫我匯出熱血傳奇客戶端中「比奇省」的地圖,寬33600、高22400,是我電腦的幾百倍大。為了避免電腦爆炸,需要拆分成多塊載入。由於傳奇的最小單元是48x32,我們以480x320將地圖拆成了4900(70x70)個圖片檔。

相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

FileReader實作上傳圖片之前本地先預覽

vue-dplayer實作hls播放的步奏詳解

以上是JS仿熱血傳奇遊戲(附代碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript數據類型:瀏覽器和nodejs之間是否有區別?JavaScript數據類型:瀏覽器和nodejs之間是否有區別?May 14, 2025 am 12:15 AM

JavaScript核心數據類型在瀏覽器和Node.js中一致,但處理方式和額外類型有所不同。 1)全局對像在瀏覽器中為window,在Node.js中為global。 2)Node.js獨有Buffer對象,用於處理二進制數據。 3)性能和時間處理在兩者間也有差異,需根據環境調整代碼。

JavaScript評論:使用//和 / * * / * / * /JavaScript評論:使用//和 / * * / * / * /May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript:開發人員的比較分析Python vs. JavaScript:開發人員的比較分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要區別在於類型系統和應用場景。 1.Python使用動態類型,適合科學計算和數據分析。 2.JavaScript採用弱類型,廣泛用於前端和全棧開發。兩者在異步編程和性能優化上各有優勢,選擇時應根據項目需求決定。

Python vs. JavaScript:選擇合適的工具Python vs. JavaScript:選擇合適的工具May 08, 2025 am 12:10 AM

選擇Python還是JavaScript取決於項目類型:1)數據科學和自動化任務選擇Python;2)前端和全棧開發選擇JavaScript。 Python因其在數據處理和自動化方面的強大庫而備受青睞,而JavaScript則因其在網頁交互和全棧開發中的優勢而不可或缺。

Python和JavaScript:了解每個的優勢Python和JavaScript:了解每個的優勢May 06, 2025 am 12:15 AM

Python和JavaScript各有優勢,選擇取決於項目需求和個人偏好。 1.Python易學,語法簡潔,適用於數據科學和後端開發,但執行速度較慢。 2.JavaScript在前端開發中無處不在,異步編程能力強,Node.js使其適用於全棧開發,但語法可能複雜且易出錯。

JavaScript的核心:它是在C還是C上構建的?JavaScript的核心:它是在C還是C上構建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; sanInterpretedlanguagethatrunsonenginesoftenwritteninc.1)JavascriptwasdesignedAsignedAsalightWeight,drackendedlanguageforwebbrowsers.2)Enginesevolvedfromsimpleterterpretpretpretpretpreterterpretpretpretpretpretpretpretpretpretcompilerers,典型地,替代品。

JavaScript應用程序:從前端到後端JavaScript應用程序:從前端到後端May 04, 2025 am 12:12 AM

JavaScript可用於前端和後端開發。前端通過DOM操作增強用戶體驗,後端通過Node.js處理服務器任務。 1.前端示例:改變網頁文本內容。 2.後端示例:創建Node.js服務器。

Python vs. JavaScript:您應該學到哪種語言?Python vs. JavaScript:您應該學到哪種語言?May 03, 2025 am 12:10 AM

選擇Python還是JavaScript應基於職業發展、學習曲線和生態系統:1)職業發展:Python適合數據科學和後端開發,JavaScript適合前端和全棧開發。 2)學習曲線:Python語法簡潔,適合初學者;JavaScript語法靈活。 3)生態系統:Python有豐富的科學計算庫,JavaScript有強大的前端框架。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境