999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

新工科下軟件工程專業(yè)實踐案例構建研究
——以基于Cocos2d-x引擎的跨平臺游戲開發(fā)為例

2021-03-08 00:25:04于萬國胡宗森隋麗娜蔡永華傅冬穎
計算機技術與發(fā)展 2021年2期
關鍵詞:引擎游戲方法

于萬國,胡宗森,隋麗娜,遲 劍,蔡永華,傅冬穎

(河北民族師范學院 數(shù)學與計算機科學學院,河北 承德 067000)

0 引 言

目前,市面上占有率領先的移動游戲引擎有Cocos2d-x、Unity3D、Egret(白鷺)等。Unity3D最大的優(yōu)勢是腳本化和組件化,簡化了游戲開發(fā)工作流中的場景編輯[1-2]。而Cocos2d-x是一個基于MIT協(xié)議的開源游戲框架,具備游戲開發(fā)快速、簡易、跨平臺的特點,基于Cocos2d-x的CocosCreator更是包含了游戲引擎、資源管理、場景編輯、游戲預覽和發(fā)布等游戲開發(fā)所需的全套功能,并將所有的功能和工具鏈整合在一個統(tǒng)一的應用程序里[3-5]。

首先,它以數(shù)據(jù)驅(qū)動和組件化作為核心的游戲開發(fā)方式,無縫融合了引擎成熟的JavaScript API體系,一方面能夠適應Cocos系列引擎開發(fā)者習慣,另一方面為美術和策劃人員提供前所未有的內(nèi)容創(chuàng)作生產(chǎn)和即時預覽測試環(huán)境;其次,CocosCreator支持游戲的熱更新功能[6-7],引擎的C++代碼里,已經(jīng)留好了相應的接口,開發(fā)者只需理解其更新流程,便可以在此基礎上自定義熱更新,不需花費大量的精力重寫熱更新的功能;再次,CocosCreator構建的native工程,與原cocos2d-js的工程基本相同,所以在手機端功能的拓展依舊很方便(比如接游戲的SDK等等)。因此CocosCreator逐步開始流行起來[8-9]。

該文從軟件工程專業(yè)實踐課程應用典型案例庫的構建角度出發(fā),利用Cocos2d-x游戲引擎的CocosCreator作為開發(fā)工具,設計了一款跨平臺(web、Android) APP—躲避刺豚君。

1 游戲的開發(fā)技術簡介

游戲服務端使用Tomcat搭建服務器,用Java語言編寫應用,用MySQL作為存儲數(shù)據(jù)庫。游戲客戶端在UI方面使用CocosCreator,采用MVC模式進行設計,WebStorm進行程序編輯;安裝Chrome瀏覽器進行Web平臺的調(diào)試;安裝Python及原生平臺上安卓所需的NDK、SDK與ANT,用于安卓工程的構建與編譯;使用ADT或Android Studio對安卓工程進行管理。該游戲中的Data、Module、View模塊,分別對應MVC模式中的Controller、Model與View模塊。具體實現(xiàn)方式如下:Data模塊在獲取到服務端的數(shù)據(jù)后,將數(shù)據(jù)進行存儲。View模塊需要更新數(shù)據(jù)時,通過Module模塊將Data模塊已存儲的數(shù)據(jù)進行處理,處理結束后Module模塊將處理后的數(shù)據(jù)傳遞給View模塊。這種模塊劃分的優(yōu)點是能夠輕松地將游戲的邏輯與UI進行分離,在改進界面和改善用戶交互體驗的同時,不需要重新編寫業(yè)務邏輯[10]。

2 游戲的功能、玩法分析

2.1 服務端功能分析

游戲服務端的功能有兩個:一是玩家的注冊與登錄,二是在游戲結束后將玩家當前的分數(shù)進行存儲。

2.2 客戶端功能分析

客戶端分為四個場景:登錄場景、開始場景、主場景和結束場景。登錄場景中提供了玩家的注冊與登錄功能。開始場景中,玩家可以查看開發(fā)人員的一些信息,可以控制游戲的音樂和音效的開與關,并且能夠正式開始游戲。主場景中,玩家可以點擊動物獲得相應的分數(shù),界面上顯示了游戲的當前剩余時間與當前的分數(shù),另外玩家可以控制游戲的暫停與繼續(xù),還可以返回游戲主菜單(即開始場景)。結束場景中顯示了游戲當前分數(shù)與歷史最高分數(shù),玩家可以選擇重玩游戲或返回主菜單。

游戲功能的思維導圖如圖1所示。

圖1 游戲功能的思維導圖

2.3 游戲規(guī)則介紹

開始游戲后,游戲的剩余時間和總分數(shù)會在開始游戲的準備動畫后開始計算。在點擊兔子和熊的時候,游戲的分數(shù)會增加一定的值,而且游戲的時間也會增加。但如果點擊到了刺豚就會進行懲罰,游戲的分數(shù)會扣除相應的值,并扣除一定的游戲時間。在游戲時間為零后,游戲結束。

3 游戲的設計與實現(xiàn)

3.1 游戲服務端設計和實現(xiàn)

服務端設計:本游戲采用B/S(瀏覽器/服務器)架構[11]進行設計。服務端使用Tomcat作為服務器。當客戶端發(fā)出請求后,服務端收到請求并在GameServlet類的doPost方法中對請求的參數(shù)通過switch做篩選,然后調(diào)用DatabaseUtil類的方法將數(shù)據(jù)處理后,通過Writer類的一個實例化的對象將處理后的結果轉(zhuǎn)化為JSON結構并返回給客戶端。用到的數(shù)據(jù)庫的User表如表1所示。

表1 User表

服務端實現(xiàn):游戲的服務端使用Tomcat作為服務器。當客戶端發(fā)出請求后,服務端收到請求并在GameServlet類的doPost方法中對請求的參數(shù)通過switch做篩選,然后調(diào)用DatabaseUtil類的方法將數(shù)據(jù)處理后,通過Writer類的一個實例化的對象將處理后的結果轉(zhuǎn)化為JSON結構并返回給客戶端。

3.2 游戲客戶端設計

客戶端游戲結構例圖如圖2所示。

圖2 游戲結構例圖

其中:

(1)network模塊中,Http.js與HttpAciton.js結合使用,用來往服務端發(fā)送請求,這里使用POST請求。具體實現(xiàn)的代碼為:

sendRequest(succ,fail) {

var xhr=new XMLHttpRequest();

xhr.timeout=this.timeout;

this.url=encodeURI(this.url);

xhr.open("POST", this.url, true);

['abort' ,'error','timeout'].forEach(function(eventname){

xhr[`on${eventname}`]=function(){

fail(-1,'network error');

}

})

xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');

xhr.onreadystatechange=function(){

if(xhr.readyState===4 && (xhr.status>=200 && xhr.status<=207)){

let data=xhr.responseText;

let json;

try {

json=JSON.parse(data);

} catch(e){

} finally{

json=null;

}}}

xhr.send();

}

(2)module模塊用于對數(shù)據(jù)的處理。BaseMoudle是基類,BaseData是派生類。

(3)data模塊用于接收服務器請求,BaseData是基類,PlayerData是派生類。具體實現(xiàn)為:在派生類實例化的時候,添加一個本地監(jiān)聽NOTI_DATA_UPDATE,在網(wǎng)絡請求收到信息后,便執(zhí)行這個監(jiān)聽,并將收到的信息存儲到PlayerData中。

(4)utils模塊中,Notification.js和NotifyName.js這兩個腳本都是一個對象,其中postNoti方法為發(fā)消息,addOb為給一個對象添加某消息的監(jiān)聽,removeOb便是給一個對象移除某條監(jiān)聽。

具體實現(xiàn)的代碼為:

postNoti(name, msg) {

const cbs=this.noti[name];

if (!cbs) {

return;

}

for (let i=0;i

const dic=cbs[i];

const obj=dic.obj;

const cb=dic.cb;

obj[cb].call(obj, msg);

}

},

addOb(name,cb,obj) {

let cbs=this.noti[name];

if (!cbs) {

cbs=[{cb:cb, obj:obj}];

this.noti[name]=cbs;

} else {

for (let i=0;i

let dic=cbs[i];

if (dic.cb===cb && dic.obj===obj) { return; }

}

cbs.push({cb:cb, obj:obj});

}

}

TextureManager.js和ViewMannager.js結合使用,用于動態(tài)加載游戲中的Prefab和Sprite,并對Sprite進行管理,在需要的時候進行texture的清理。view模塊是游戲中各個具體功能的實現(xiàn)模塊,詳細介紹參見3.3節(jié)。

3.3 游戲客戶端的具體實現(xiàn)

先解釋一下Prefab的加載原理。開發(fā)人員在層級管理器中里編輯好一個Prefab(預制件)后,將它拖動到資源管理器,這時將會在PC的本地創(chuàng)建一個***.prefab和該預制件對應的meta文件,在用編輯器打開***.prefab文件后,會發(fā)現(xiàn)它就是一個包含節(jié)點信息的json結構。所以,加載預制件就是將本地的json結構文件加載并且解析出來,然后形成了node的結構,再通過addChild將這個節(jié)點添加到自己想要的節(jié)點上,便完成了該節(jié)點的創(chuàng)建[12]。而meta文件中主要存儲該文件的uuid,在引用資源的時候,便通過這個uuid去查找對應的文件,在內(nèi)存中該資源也是通過它的uuid作為Key存儲到相應的map中[13]。

游戲客戶端實現(xiàn)用到了三個場景,開始場景、主場景、結束場景。各場景分別配有該場景的腳本。該種做法的目的是:能夠在新場景加載完畢后,優(yōu)先做一些處理。比如一些大型游戲項目,如果是新手進入主場景,就要加載到該用戶當前的引導界面,而老用戶則需要加載主場景的第一個界面。

(1)開始場景。

在StartScene.js這個腳本onLoad執(zhí)行的時候,先通過cc.loader.loadRes加載LoadView這個預制件,并在load成功后,將這個預制件存到ViewManger中。這樣的目的是在加載其他預制件時,如果該預制件的節(jié)點過多,可能會很卡,也就是說消耗性能,這時再將這個預制件實例化出來,直接加載到場景上,相當于一個過渡的作用。當然開發(fā)人員也可以在這個預制件上,添加自定義的腳本,來做一些特殊處理。比如:如果想讓用戶在加載預制件的時候,不允許點擊屏幕上其他節(jié)點(防止造成誤操作),就可以在這個腳本里,給這個節(jié)點添加一個觸摸事件,并且阻止觸摸事件向下傳遞。存儲完之后,再加載StarView.prefab。

關鍵代碼如下:

cc.loader.loadRes('/Prefab/LoadView', function (err, loadViewPrefab) {

ViewManager._loadViewPrefab=loadViewPrefab;

ViewManager.createPrefabNode('/Prefab/Start/StartView', 'StartView', function (node) {

this.node.addChild(node);

}.bind(this));

}.bind(this))

StartView.prfab加載成功后,先添加一個監(jiān)聽?!瓹LOSE_ABOUT_VIEW’,該監(jiān)聽的作用是在關閉“關于我們”界面后,將節(jié)點的透明度設置成最大值。接著調(diào)用toggleState這個方法,先初始化當前的音樂與音效狀態(tài)。在該方法中使用cc.sys. localStorage存儲本地信息。像游戲中的音樂、音效的狀態(tài)通常是都需要保存的。但并不需要保存到服務端的數(shù)據(jù)庫中,這里用到了HTML5的本地存儲localStorage[7],如圖3所示。

圖3 localStorage的存儲實例

而native端則是分別調(diào)用ios和android設備原生讀寫文件的方法,將數(shù)據(jù)存儲到可讀寫區(qū)域。

關鍵代碼如下:

toggleState() {

let ls=cc.sys.localStorage;

if (ls.getItem('Music')=='ON') {

this.musicToggle.isChecked=true;

Common.playMusic('Start');

} else if (ls.getItem('Music')=='OFF') {

this.musicToggle.isChecked=false;

} else {

ls.setItem('Music', 'ON');

Common.playMusic('Start');

this.musicToggle.isChecked=true;

}

if (ls.getItem('Audio')=='ON') {

this.audioToggle.isChecked=true;

} else if (ls.getItem('Audio')=='OFF') {

this.audioToggle.isChecked=false;

} else {

ls.setItem('Audio', 'ON');

this.audioToggle.isChecked=true;

}

}

界面上的聲音和音效的開關按鈕則是通過給該節(jié)點綁定一個Toggle組件,并添加checkEvent事件,在點擊該開關的時候再將對開關的狀態(tài)改變,并把開或關的狀態(tài)值再次存起來。

點擊按鈕后,先加載AboutView.prefab,加載成功后,在其對應的腳本的onLoad里,去調(diào)用setContent方法。本游戲在該方法中將已存儲的開發(fā)者的個人信息的json文件讀取出來,并通過Label展示出來。

點擊開始游戲按鈕,則進入主場景。

(2)主場景。

該場景中用到的組件類型的腳本有MainView.js、Center.js、Animal.js、PauseView.js,邏輯類型的腳本(類)有GameEngine.js、UnitController.js、Unit.js、Progress.js。

MainView的作用是用來管理整個場景上的UI顯示,GameEngine的作用是控制場景中的游戲邏輯,如:管理UnitController和Progress并完成向MainView發(fā)送消息。Center和UnitController是一一對應關系,也就是UnitController是Center抽象出來的概念。Animal和Unit也是這樣的關系。UnitController用來管理它所屬的Unit,而Center對應的節(jié)點又是Animal對應節(jié)點的父節(jié)點,這樣便使這四者的關系統(tǒng)一起來。Progress是用來計算游戲時間類,在游戲時間結束后,便通過GameEngine發(fā)送游戲結束的消息,在MainView接收到消息后,便跳轉(zhuǎn)到結束場景。

游戲主場景具體實現(xiàn)過程如下:

①在MainView.js的OnLoad執(zhí)行的時候,實例化一個GameEngine,并開始初始化GameEngine。也就是調(diào)用GameEngine的init方法(相當于構造方法),并在其中實例化Progress。在初始化結束后,便向MainView發(fā)消息,執(zhí)行MainView中的init方法,該方法的作用是播放Ready和Go這兩個動畫。

②開始創(chuàng)建一組動物。首先實例化一個UnitController,然后執(zhí)行它的init方法,然后創(chuàng)建若干個unit,并設置這些unit的信息,如:類型(狗熊、兔字、刺豚)、分值及時間獎勵。創(chuàng)建完成后,引擎向view層發(fā)消息,view收到消息后,便通過對應的prefab創(chuàng)建對應的動物節(jié)點(綁定Animal組件),并把這個節(jié)點添加到它們對應的Center(該腳本中自定義了一個函數(shù),實現(xiàn)了加速度,使動物上拋和下落更加真實)上。這樣便實現(xiàn)了一組動物中,能夠以相同的運動規(guī)律進行運動。

③在玩家點擊動物節(jié)點時,該節(jié)點上的Animal.js中的點擊事件將會觸發(fā)。在點擊事件觸發(fā)后的回調(diào)方法中,將該Animal對應的Unit的狀態(tài)進行改變(包括計分、獎懲游戲時間等等),并播放該節(jié)點相應的動畫。如果是狗熊或兔子,就從它們的父節(jié)點脫離出來,直接添加到MainView背景對應的節(jié)點上,并開始向上飛,飛出屏幕后,移除該節(jié)點。而如果是刺豚的話,僅需要在執(zhí)行完點擊的動畫后,移除即可。如果Center對應的節(jié)點,下落到屏幕外以后,再通過UnitController執(zhí)行GameEngine的創(chuàng)建動物的方法,再創(chuàng)建新的一組。

④關于游戲的暫停方面,本游戲并沒有選擇使用cc.game.pause()這個引擎直接封裝好的用于暫停的方法。當然相對于這個小項目,使用該方法就行。本游戲沒有使用它的原因是,當使用這個方法的時候,整個項目中的游戲邏輯,渲染,事件處理,背景音樂和所有音效將會全部暫停,這樣導致的問題是:如果開發(fā)者想在暫停后彈出新的對話框,而該對話框中仍然需要有一些動態(tài)的效果(如:使用ScrollView組件進行滑動等等操作)將不可能實現(xiàn)。所以這里通過在MainView定義一個字段isPause,進而控制整個游戲過程中的動態(tài)效果。這樣做也有一點小的缺陷,比如使用repeatForever的這些動作,就必須使用AcitonMannager這個單例進行單獨處理,將這個節(jié)點的動作暫停,當然在游戲繼續(xù)時,還需要使用它將這個節(jié)點當前已暫停的動作繼續(xù)。

(3)結束場景。

在該場景中,主要是將本次游戲的分數(shù)和歷史最高分數(shù)通過Label顯示出來。其過程是客戶端計算本次的分數(shù),在游戲結束時,客戶端在將本次的分數(shù)發(fā)給服務端后,服務端經(jīng)過分數(shù)比較的運算,將歷史最高分數(shù)的數(shù)據(jù)返回給客戶端。

場景之間資源管理方面,資源的釋放是在切換場景的時候,在進入下一個場景前,先釋放掉前場景所用到的資源,這對于一個小型的游戲項目來說就足夠了,不需要去花費更多精力對游戲的資源進行管理[14]。

4 游戲的調(diào)試

躲避刺豚君游戲在Web和Native不同平臺進行了調(diào)試。在Web平臺,采用引擎的cc.log()的方法來輸出關鍵的變量并與使用Chrome進行斷點調(diào)試的方法相結合進行調(diào)試[13]。在Native(原生)平臺,是在編譯項目時使用Debug模式,并配合程序的cc.log()方法來輸出關鍵的變量,從而將設備連接后,可以在Eclipse的控制臺顯示這些變量的值,進而查找問題。

通過在兩個平臺的調(diào)試,發(fā)現(xiàn)兩個平臺其中的一些區(qū)別,具體如下:

首先,關于引擎的類中字段的權限問題。在web上,取到Sprite組件的spriteFrame對象之后,可以使用它的_name這個字段,而安卓調(diào)試時通過log輸出,發(fā)現(xiàn)該值為null,因此在使用該變量時就會報錯。因為C++的引擎中這個變量使用了private設置了它的訪問權限。因此需要謹慎地使用引擎內(nèi)部的帶有下劃線的私有變量。

其次,一個比較明顯的區(qū)別是在if的判斷上。在玩家首次打開游戲時,因為要設置音樂和音效開關的初始狀態(tài),所以就需要使用cc.sys.localStorage.getItem(arg)這個方法進行判斷。通過log的輸出,發(fā)現(xiàn)如果沒有返回值時,web端返回的是undefine而在安卓上返回值為null,因此需要區(qū)分開來進行判斷。

調(diào)試經(jīng)驗分享:

①在進行Android調(diào)試的時候,最好的辦法是看C++源碼,緊接著和HTML5的源碼進行對比,這樣才能更容易找到在web端沒有問題,但在native上卻出現(xiàn)問題的原因。

②在一個比較大的游戲開發(fā)過程中,開發(fā)人員應該要時常去打包,檢查native上新開發(fā)的模塊有沒有問題,否則集中解決的時候就會消耗太多的精力。

5 結束語

從新工科背景下軟件工程專業(yè)實踐課程應用典型案例庫的構建角度出發(fā)[15],以應用為導向,利用Cocos2d-x游戲引擎的最新開發(fā)工具CocosCreator,從游戲開發(fā)工具選用、開發(fā)環(huán)境、游戲功能、玩法介紹等入手,詳細介紹了實現(xiàn)跨平臺游戲—躲避刺豚君的設計和實現(xiàn)過程,經(jīng)測試,游戲跨平臺運行通暢,功能完備。該游戲的開發(fā)對基于Cocos2d-x引擎的CocosCreator跨平臺游戲開發(fā)的設計和實現(xiàn)具有一定的參考價值。

猜你喜歡
引擎游戲方法
藍谷: “涉藍”新引擎
商周刊(2017年22期)2017-11-09 05:08:31
可能是方法不對
數(shù)獨游戲
瘋狂的游戲
飛碟探索(2016年11期)2016-11-14 19:34:47
爆笑游戲
用對方法才能瘦
Coco薇(2016年2期)2016-03-22 02:42:52
四大方法 教你不再“坐以待病”!
Coco薇(2015年1期)2015-08-13 02:47:34
第八章直接逃出游戲
小學科學(2015年7期)2015-07-29 22:29:00
無形的引擎
河南電力(2015年5期)2015-06-08 06:01:46
捕魚
主站蜘蛛池模板: 亚洲国产欧美国产综合久久 | 亚洲成AV人手机在线观看网站| 一区二区三区成人| 国产欧美日韩综合在线第一| 日本精品αv中文字幕| 久久婷婷六月| 91黄视频在线观看| 一级毛片免费高清视频| 久久香蕉欧美精品| 波多野结衣亚洲一区| 久久久亚洲国产美女国产盗摄| 亚洲AⅤ无码日韩AV无码网站| 亚洲天堂.com| 99精品欧美一区| 国产精品亚欧美一区二区| 国产午夜小视频| 午夜精品一区二区蜜桃| 国产成人av一区二区三区| 亚洲人成网7777777国产| 114级毛片免费观看| 久久精品一品道久久精品| 国产91熟女高潮一区二区| 欧美精品亚洲精品日韩专区| 免费人成网站在线高清| 亚洲无码视频喷水| 最新无码专区超级碰碰碰| 国产免费久久精品99re丫丫一| 亚洲AV无码乱码在线观看代蜜桃 | 58av国产精品| 美女一级免费毛片| 亚洲首页在线观看| 999精品在线视频| 国内精品久久久久久久久久影视| 久久人妻xunleige无码| 免费看美女自慰的网站| 中文字幕资源站| 久久免费视频播放| 熟妇人妻无乱码中文字幕真矢织江 | 国产99精品久久| 国产一二三区在线| 毛片久久久| 国产精品美女网站| 精品视频福利| 亚洲第一在线播放| 国产成年女人特黄特色毛片免| 天堂网亚洲系列亚洲系列| 久久综合九九亚洲一区| 亚洲国产一区在线观看| 久久久久免费精品国产| 啪啪永久免费av| 一本二本三本不卡无码| 无码内射中文字幕岛国片| 91精品国产一区自在线拍| 国产精品视频系列专区| 国产成人高清精品免费| 波多野结衣一区二区三区四区视频| 久久综合亚洲色一区二区三区| 色婷婷成人| 免费 国产 无码久久久| 色呦呦手机在线精品| 在线观看视频99| 国产精品无码一区二区桃花视频| 久久综合国产乱子免费| 国内精自视频品线一二区| 又黄又湿又爽的视频| 国产精品黑色丝袜的老师| 最新日本中文字幕| 色综合久久综合网| 亚洲色精品国产一区二区三区| 亚洲欧美日韩天堂| 亚洲国产成熟视频在线多多| 2021国产精品自产拍在线观看| 国产一级精品毛片基地| 噜噜噜久久| 国产激情在线视频| 久久精品电影| 国产视频自拍一区| 99精品免费欧美成人小视频| 亚洲欧美天堂网| 四虎影视无码永久免费观看| 国产门事件在线| 久久情精品国产品免费|