搜尋
首頁web前端js教程JS仿經典傳奇遊戲

JS仿經典傳奇遊戲

Mar 17, 2018 pm 02:45 PM
javascript經典

這次帶給大家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)個圖片檔。

canvas的尺寸我們設定為800x600,這樣玩家只需要載入3x3共9張圖片就可以鋪滿整個畫布。 800/480=1.67,那為什麼不是2x2?因為有可能玩家目前的位置剛好導致有的圖片只展示了一部分。如下圖:

 

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

推薦閱讀:

webpack-dev-server怎麼設定遠端模式

在ES6中子元件怎麼呼叫父元件

以上是JS仿經典傳奇遊戲的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

從網站到應用程序:JavaScript的不同應用從網站到應用程序:JavaScript的不同應用Apr 22, 2025 am 12:02 AM

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python vs. JavaScript:比較用例和應用程序Python vs. JavaScript:比較用例和應用程序Apr 21, 2025 am 12:01 AM

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C/C在JavaScript口譯員和編譯器中的作用C/C在JavaScript口譯員和編譯器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

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

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

熱工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。