亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

javascript高仿熱血傳奇游戲實現(xiàn)代碼

 更新時間:2018年02月22日 15:23:15   作者:C92  
這篇文章主要介紹了javascript高仿熱血傳奇游戲的實現(xiàn)代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

前言

游戲的第一個版本開發(fā)于14年,瀏覽器端使用html+css+js,服務端使用asp+php,通訊采用ajax,數(shù)據(jù)存儲使用access+mySql。不過由于一些問題(當時還不會用node,用asp寫復雜的邏輯真的會寫吐;當時對canvas寫的也少,dom渲染很容易達到性能瓶頸),已經(jīng)廢棄。后來用canvas重制了一版。本文寫于18年。

 

1.開發(fā)前的準備

為什么要用Javascript來實現(xiàn)一款比較復雜的PC端游戲

1.js實現(xiàn)PC端網(wǎng)游是可行的。隨著PC、手機硬件配置的升級和瀏覽器的更新?lián)Q代,以及H5各種庫的發(fā)展,js實現(xiàn)一款網(wǎng)游的難度越來越低。這里的難度主要是兩方面:瀏覽器的性能;js代碼是否足夠易于擴展,以滿足于一款邏輯極其復雜的游戲的迭代。

2.現(xiàn)階段的js游戲里,很少有規(guī)模較大的可供參考。涉及到多人聯(lián)機、服務端數(shù)據(jù)存儲、復雜交互的游戲,大多數(shù)(幾乎全部)都是用flash開發(fā)的。但是flash畢竟在衰落,而js發(fā)展迅速,并且只要有瀏覽器就可以運行。

為什么選擇了一款2001年的熱血傳奇游戲

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

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

可能的困難

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

2.防作弊:如何避免用戶直接調用接口或者篡改網(wǎng)絡請求數(shù)據(jù)?由于目標是用js實現(xiàn)比較復雜的游戲,并且任何網(wǎng)絡游戲都需要考慮這一點,一定會有相對成熟的方案。此處不是本文重點。

2.整體設計

瀏覽器端

畫面渲染使用canvas。

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

 

這時,如果希望實現(xiàn)“點擊動物、攻擊動物;點擊物品、撿起物品”的效果,那么需要對動物和物品進行事件監(jiān)聽。如果采用dom的方式,那么會出現(xiàn)幾點難于處理的問題:

a.渲染的順序和事件處理的順序不同(有時候z-index小的需要先處理事件),需要額外處理。例如這個上面的例子里:點擊怪物、點擊物品的時候也容易點到人物,那么需要給人物做“點擊事件穿透”的處理。而且事件處理的順序不固定:假如我有一個技能(例如游戲里的治療)需要點人物才可以釋放,那么這時人物又需要有事件監(jiān)聽。所以一個元素是否需要處理事件、處理事件的先后,是隨著游戲狀態(tài)的不同而變化的,而 dom的事件綁定已經(jīng)不能滿足需要 。

b.有關聯(lián)的元素難以放在同一個dom節(jié)點中:例如玩家的模型、玩家的名字和玩家身上的技能畫效,理想情況下是放在一個 <div> 或者 <section> 容器里,便于管理(這樣幾個元素的定位就可以繼承父元素,不用分別處理位置了)。但是這樣,z-index會很難處理。例如玩家A在玩家B的上面,那么A會被B遮擋,因此需要A的z-index小一些,但是又需要讓玩家A的名字不會被B的名字或者影子遮擋,就無法實現(xiàn)。簡單點說, dom結構的可維護性會犧牲畫面展示的效果,反之亦然 。

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

canvas渲染邏輯與項目邏輯分離

如果將canvas的各種渲染操作(如 drawImage 、 fillText 等)與項目代碼放在一起,那么勢必導致項目后期無法維護。翻了一下幾款現(xiàn)有的canvas庫,結合vue的數(shù)據(jù)綁定+調試工具的方式,搞了一個全新的canvas庫Easycanvas( github地址 ),并且像vue一樣支持通過一個插件來調試canvas中的元素。

這樣,整個游戲的渲染部分就容易很多,只需要管理游戲當前的狀態(tài)、并且根據(jù)服務端從socket傳回來的數(shù)據(jù)去更新數(shù)據(jù)就可以。 “數(shù)據(jù)的變化引起視圖的變化”這個環(huán)節(jié)由Easycanvas負責 。例如下圖的玩家包裹物品的實現(xiàn),我們只需要給出包裹容器的位置、背包里每個元素的排布規(guī)則,然后將每個包裹的物品綁定到一個array上,然后去管理這個array即可(數(shù)據(jù)映射到畫面的過程由Easycanvas負責)。

 

例如,5行8列共計40個物品的樣式可以通過如下的形式傳遞給Easycanvas(index為物品索引,物品x方向間距36,y方向間距32)。而這個邏輯是一成不變的,無論物品的數(shù)組怎樣變化、包裹被拖拽到什么位置,每個物品的相對位置都是固定的。至于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來呈現(xiàn),那么這個canvas的幀數(shù)40,每秒至少需要繪制40個屏幕面積。但是同一個坐標點很可能出現(xiàn)多個元素重疊的情況,例如底部的UI、血條、按鈕就是重疊放置,他們又共同遮擋了場景地圖。所以這些加在一起,每秒瀏覽器的繪制量很容易達到100個屏幕面積以上。

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

因為 游戲中幾乎不可能出現(xiàn)某一幀的畫面與上一幀毫無區(qū)別的情況 ,畫面永遠在更新。

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

 

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

例如UI層,因為很多UI平時是不動的,即使動也不會需要太精密的繪制,所以可以適當降低幀數(shù),例如降低到20。這樣假如玩家的體力從100降低到20,那么可以在50ms內(nèi)更新視圖,而50ms的切換是玩家感覺不出來的。因為像體力這種 UI層數(shù)據(jù)的變化很難在很短的時間內(nèi)連續(xù)變化多次,而50ms的延遲是人很難感知的,所以不需要頻繁的繪制 。假如我們每秒節(jié)約了20幀,那么很可能可以節(jié)約10個屏幕面積的繪制。

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

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

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

服務器端

由于目標是js實現(xiàn)一款多人網(wǎng)游,所以服務端使用Node,使用socket與瀏覽器通訊。這樣做還有一個好處,就是一些 公用的邏輯可以在兩端復用 ,例如判斷地圖上某個坐標點是否存在障礙物。

Node端的玩家、場景等游戲相關數(shù)據(jù)全部存儲與內(nèi)存中,定期同步至文件。每次Node服務啟動時,將數(shù)據(jù)從文件讀取至內(nèi)存。這樣可以玩家較多時,文件讀寫的頻率成指數(shù)級上升,從而引發(fā)的性能問題。(后來為了提高穩(wěn)定,為文件讀寫增加了一個緩沖,“內(nèi)存-文件-備份”的方式,以免讀寫過程中服務器重啟導致的文件損壞)。

Node端分接口、數(shù)據(jù)、實例等多層。“接口”負責和瀏覽器端交互?!皵?shù)據(jù)”是一些靜態(tài)數(shù)據(jù),例如某個藥品的名稱和效果、某個怪物的速度和體力,是游戲規(guī)則的一部分?!皩嵗笔怯螒蛑械漠斍盃顟B(tài),例如某個玩家身上的一個藥品,就是“藥品數(shù)據(jù)”的一個實例。再舉個例子,“鹿的實例”擁有“當前血量”這個屬性,鹿A可能是10,鹿B可能是14,而“鹿”本身只有“初始血量”。

3.場景地圖的實現(xiàn)

地圖場景

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

思考

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

這樣看起來是合理的,但是假如地圖中有一棵樹,那么“玩家的層次始終高于樹”就不對了。這時,有2種大的解決方案:

地圖分層,“地面”與“地上”拆開。將玩家處于兩層之間,例如下圖,左側是地上、右側是地面,然后重疊繪制,把人物夾在中間:

 

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

地圖不分層,“地面”與“地上”在一起繪制。當玩家處于樹后的時候,將玩家的透明度設置為0.5,例如下圖:

 

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

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

下面開始正題。

實現(xiàn)

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

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

 

總結

以上所述是小編給大家介紹的javascript高仿熱血傳奇游戲實現(xiàn)代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關文章

最新評論