




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
-
-
摘要本文主要闡述網(wǎng)絡(luò)回合制戰(zhàn)斗系統(tǒng)的實(shí)現(xiàn),包括回合制戰(zhàn)斗系統(tǒng)的整個(gè)流程(戰(zhàn)斗開始、玩家下達(dá)戰(zhàn)斗指令、戰(zhàn)斗動(dòng)畫、戰(zhàn)斗結(jié)束),和網(wǎng)絡(luò)回合制戰(zhàn)斗系統(tǒng)常見的主要功能(攻擊、使用技能、使用物品)的實(shí)現(xiàn),并且在游戲項(xiàng)目《PKer》中進(jìn)行實(shí)踐和測(cè)試。在開發(fā)環(huán)境方面,服務(wù)器采用國(guó)內(nèi)IOCP高性能開源框架“HP-Socket”(Windows平臺(tái)),并且連接到Mysql數(shù)據(jù)庫(kù),客戶端采用近年比較火熱的強(qiáng)大跨平臺(tái)引擎Unity3D(2D,C#)并且使用Sqlite作為嵌入式數(shù)據(jù)庫(kù)。除此以外,本文還對(duì)戰(zhàn)斗系統(tǒng)實(shí)現(xiàn)所涉及到的相關(guān)技術(shù)如Unity引擎的協(xié)同程序、單例設(shè)計(jì)模式和分包算法進(jìn)行簡(jiǎn)要介紹。
關(guān)鍵詞回合制戰(zhàn)斗系統(tǒng),網(wǎng)絡(luò)游戲,Unity3D,協(xié)同程序,游戲編程
ABSTRACTThisarticlemainlyexpoundstheimplementationofnetworkturn-basedcombatsystem,includingthewholeprocessofturn-basedcombatsystem(battlebegins,theplayersfightinginstructions,combatanimation,battleends),andthemainfunctionofcommon(attack,usingskills,usingitems)implementation,andinthegame"PKer"inpracticeandtestoftheproject.Intheaspectofdevelopmentenvironment,C++IOCPserverwithWindowsplatformandconnecttotheMysqldatabase,theclientusesUnity3D(2D,C#)andusesSqliteasanembeddeddatabase.Inadditiontothis,thisarticlealsoinvolvedtocombatsystemrelatedtechnologiessuchastheUnityenginecoroutines,singletondesignpatternandthesubcontractalgorithmarebrieflyintroduced.
KEYWORDSTurn-basedcombatsystem,onlinegame,Unity3D,Coroutine,Singletondesignpattern
目錄
TOC\f\h\z\t"樣式1,1,樣式2,2,樣式3,3"
前言
1
第1章緒論
2
1.1回合制游戲簡(jiǎn)介
2
1.1.1廣義上的回合制游戲
2
1.1.2狹義上的回合制游戲
2
1.2回合制戰(zhàn)斗系統(tǒng)簡(jiǎn)介
2
1.3實(shí)踐項(xiàng)目《PKer》簡(jiǎn)介
2
第2章開發(fā)環(huán)境和部分涉及技術(shù)簡(jiǎn)介
4
2.1開發(fā)環(huán)境簡(jiǎn)介
4
2.2Unity協(xié)同程序
4
2.2.1Unity協(xié)同程序簡(jiǎn)介
4
2.2.2Unity協(xié)同程序的運(yùn)用
4
2.3單例設(shè)計(jì)模式
5
2.3.1單例設(shè)計(jì)模式
5
2.3.2單例設(shè)計(jì)模式的運(yùn)用
6
2.3.3單例設(shè)計(jì)模式在Unity中的運(yùn)用
7
2.4分包算法
7
2.4.1分包的原因
7
2.4.2環(huán)形緩沖區(qū)(CircularBuffer)
7
2.4.3分包算法流程圖
9
第3章戰(zhàn)斗動(dòng)畫實(shí)現(xiàn)方案的研究與對(duì)比
11
3.1研究背景
11
3.2實(shí)現(xiàn)方案一:有限狀態(tài)機(jī)
11
3.3實(shí)現(xiàn)方案二:協(xié)同程序(Coroutine)
12
3.4方案抉擇結(jié)論
13
第4章回合制網(wǎng)游戰(zhàn)斗系統(tǒng)設(shè)計(jì)
14
4.1回合制網(wǎng)游戰(zhàn)斗系統(tǒng)設(shè)計(jì)
14
4.2回合制網(wǎng)游戰(zhàn)斗系統(tǒng)服務(wù)器實(shí)現(xiàn)思路
15
4.3回合制網(wǎng)游戰(zhàn)斗系統(tǒng)客戶端實(shí)現(xiàn)思路
15
4.4戰(zhàn)斗系統(tǒng)框架結(jié)構(gòu)(以實(shí)踐項(xiàng)目《PKer》為例)
16
第5章回合制戰(zhàn)斗系統(tǒng)實(shí)現(xiàn)
18
5.1戰(zhàn)斗系統(tǒng)相關(guān)消息結(jié)構(gòu)體的定義
18
5.2服務(wù)器戰(zhàn)斗系統(tǒng)的實(shí)現(xiàn)
20
5.2.1服務(wù)器戰(zhàn)斗系統(tǒng)相關(guān)結(jié)構(gòu)體
20
5.2.2初始化一個(gè)戰(zhàn)局
23
5.2.3接收客戶端的戰(zhàn)斗指令消息
23
5.2.4處理戰(zhàn)斗指令并發(fā)送動(dòng)畫消息
24
5.2.5說明
27
5.3客戶端戰(zhàn)斗系統(tǒng)的實(shí)現(xiàn)
27
5.3.1客戶端戰(zhàn)斗系統(tǒng)功能模塊
28
5.3.2客戶端戰(zhàn)斗系統(tǒng)相關(guān)數(shù)據(jù)類型
28
5.3.3進(jìn)入戰(zhàn)斗后初始化戰(zhàn)斗角色
29
5.3.4下達(dá)戰(zhàn)斗指令并發(fā)送戰(zhàn)斗指令消息
29
5.3.5接收戰(zhàn)斗動(dòng)畫消息
30
5.3.6收到戰(zhàn)斗動(dòng)畫播放消息并播放戰(zhàn)斗動(dòng)畫
30
5.3.7戰(zhàn)斗動(dòng)畫實(shí)現(xiàn)細(xì)述
35
第6章項(xiàng)目測(cè)試
39
6.1進(jìn)入戰(zhàn)斗測(cè)試
39
6.2普通攻擊功能測(cè)試
39
6.3使用技能功能測(cè)試
40
6.4使用物品功能測(cè)試
41
結(jié)論
43
參考文獻(xiàn)
44
致謝
45
前言
隨著網(wǎng)絡(luò)的普及與迅速發(fā)展,網(wǎng)絡(luò)游戲已經(jīng)成為電子游戲中的主流。網(wǎng)絡(luò)游戲的戰(zhàn)斗系統(tǒng)主要可分為即時(shí)制和回合制兩種模式。即時(shí)制戰(zhàn)斗系統(tǒng)側(cè)重于刺激、反應(yīng)、操作性,戰(zhàn)斗節(jié)奏快。而回合制戰(zhàn)斗系統(tǒng)側(cè)重于休閑、戰(zhàn)略、配合,戰(zhàn)斗節(jié)奏慢。早期的電子游戲由于設(shè)備硬件條件有限,大多采用回合制戰(zhàn)斗系統(tǒng)。
隨著科技的發(fā)展和設(shè)備硬件的提升,如今回合制游戲的數(shù)量比例有所下降,但回合制游戲在國(guó)內(nèi)游戲市場(chǎng)依舊占據(jù)著想當(dāng)大的份額,仍有大量玩家熱衷這種戰(zhàn)斗模式,幾款眾所周知的國(guó)產(chǎn)單機(jī)游戲如《軒轅劍》系列、《仙劍奇?zhèn)b傳》系列、《古劍奇譚》系列以及國(guó)內(nèi)在線人數(shù)最多的MMORPG《夢(mèng)幻西游》均是采用回合制戰(zhàn)斗系統(tǒng)的游戲。
本人也是熱衷于回合制戰(zhàn)斗系統(tǒng)的玩家之一,可縱觀近年來(lái)國(guó)內(nèi)回合制游戲的發(fā)展,國(guó)內(nèi)回合制游戲一直止步不前,《軒轅劍》系列、《仙劍奇?zhèn)b傳》系列、《古劍奇譚》銷量下降,MMORPG《夢(mèng)幻西游》也有降溫跡象,甚至國(guó)內(nèi)大多回合制網(wǎng)游只是復(fù)制在《夢(mèng)幻西游》或“換皮”,缺少創(chuàng)新和突破。本人希望在研究和實(shí)現(xiàn)回合制戰(zhàn)斗系統(tǒng)的基礎(chǔ)上,能夠找到突破和創(chuàng)新點(diǎn)。同時(shí)在游戲編程方面,即時(shí)制邏輯較為簡(jiǎn)單明了清晰,而回合制則比較繁瑣復(fù)雜,而且在Unity引擎上實(shí)現(xiàn)回合制戰(zhàn)斗系統(tǒng)的相關(guān)資料較少。綜上原因,本人撰寫本文,希望能對(duì)志同道合者有所啟發(fā),同時(shí)也尋求學(xué)術(shù)交流。
項(xiàng)目將在2017年6月開源,開源地址
/share/home?uk=1017424337
By江正覬
2016.6
第1章緒論
1.1回合制游戲簡(jiǎn)介
1.1.1廣義上的回合制游戲
凡“我方與敵方在單個(gè)回合內(nèi)輪流行動(dòng),只有輪到我方(自己)的回合或者是新的回合開始時(shí),才可進(jìn)行行動(dòng)”的游戲,都可歸類為廣義上的回合制游戲,而且絕大大多數(shù)情況下,單個(gè)回合內(nèi)敵我雙方行動(dòng)次數(shù)相同。從廣義上來(lái)說,回合制游戲范圍非常廣,棋牌、卡牌、戰(zhàn)棋策略、回合制戰(zhàn)斗模式都能歸為此類。
1.1.2狹義上的回合制游戲
狹義上的回合制游戲,是從廣義回合制游戲中細(xì)分,特指回合制戰(zhàn)斗模式的游戲,與即時(shí)制戰(zhàn)斗模式相對(duì)。
1.2回合制戰(zhàn)斗系統(tǒng)簡(jiǎn)介
在回合制戰(zhàn)斗模式下,每個(gè)回合開始時(shí),敵我雙方各自為雙方戰(zhàn)斗角色下達(dá)戰(zhàn)斗指令,待雙方所有角色下達(dá)完戰(zhàn)斗指令或超過時(shí)限(回合制網(wǎng)絡(luò)游戲均會(huì)設(shè)置下達(dá)戰(zhàn)斗指令的時(shí)間限制)后雙方所有角色開始行動(dòng),一般根據(jù)角色自身“速度(敏捷)”的屬性數(shù)值輪流行動(dòng),期間若果某一方符合戰(zhàn)敗條件(某方全體陣亡或全體人物陣亡)則戰(zhàn)斗結(jié)束,如所有角色行動(dòng)完后敵我雙方均沒符合戰(zhàn)敗條件,則進(jìn)入下回合,如此循環(huán)。
1.3實(shí)踐項(xiàng)目《PKer》簡(jiǎn)介
《PKer》是一個(gè)以回合制競(jìng)技和社交為賣點(diǎn)的PC和移動(dòng)端跨平臺(tái)網(wǎng)絡(luò)游戲,是游戲與社交APP的融合體。游戲主要玩法是玩家與玩家之間的回合制戰(zhàn)斗PK。
通過移動(dòng)設(shè)備GPS定位功能(PC端無(wú)法使用),能夠快速搜索在你身邊的游戲房間和玩家,與其開展戰(zhàn)斗。
游戲提供一定數(shù)量的基礎(chǔ)裝備和寵物讓玩家任意領(lǐng)取,故玩家可以隨時(shí)更換裝備和寵物,新玩家也能以此為基礎(chǔ)投入到戰(zhàn)斗中。更強(qiáng)的裝備和稀有的寵物通過合成、付費(fèi)租用等渠道獲得。角色的屬性和職業(yè)也可以隨時(shí)更改,以便隨時(shí)改變戰(zhàn)術(shù)和改變?cè)陉?duì)伍里中的定位。
第2章開發(fā)環(huán)境和部分涉及技術(shù)簡(jiǎn)介
2.1開發(fā)環(huán)境簡(jiǎn)介
本文實(shí)踐項(xiàng)目的服務(wù)器采用國(guó)內(nèi)IOCP開源框架“HP-Socket”,并且連接到Mysql數(shù)據(jù)庫(kù),客戶端采用近年比較火熱的強(qiáng)大跨平臺(tái)引擎Unity3D(2D,C#)并且使用Sqlite作為嵌入式數(shù)據(jù)庫(kù)。
2.2Unity協(xié)同程序
2.2.1Unity協(xié)同程序簡(jiǎn)介
協(xié)同程序(Coroutine),通常簡(jiǎn)稱“協(xié)程”,顧名思義,是一段協(xié)助的程序(方法),很多人以為它是另開一個(gè)線程執(zhí)行一段程序,其不然,實(shí)際上它是從主線程每幀或每隔一定時(shí)間調(diào)用的程序。當(dāng)協(xié)程創(chuàng)建后,主線程中創(chuàng)建協(xié)程的語(yǔ)句后面的代碼塊會(huì)“掛起”,直到協(xié)同程序結(jié)束后,才會(huì)繼續(xù)執(zhí)行創(chuàng)建協(xié)程語(yǔ)句后面的代碼。當(dāng)協(xié)程中的代碼執(zhí)行完或者使用yieldbreak語(yǔ)句時(shí),協(xié)程才會(huì)結(jié)束,并且返回到主線程中的創(chuàng)建該協(xié)程的語(yǔ)句的位置,繼續(xù)執(zhí)行后面的代碼。協(xié)程中使用yieldreturn幀數(shù)/newWaitForSeconds(秒數(shù))語(yǔ)句可以實(shí)現(xiàn)隔多少幀或多少秒后再執(zhí)行后續(xù)代碼。另外協(xié)程可以嵌套協(xié)程,利用協(xié)程的特點(diǎn)和嵌套,可以實(shí)現(xiàn)很多復(fù)雜和有趣的功能,十分強(qiáng)大。
2.2.2Unity協(xié)同程序的運(yùn)用
協(xié)程廣泛地運(yùn)用在計(jì)時(shí)、延遲、控制物體運(yùn)動(dòng)、等待物體狀態(tài)的改變、有順序地讓物體執(zhí)行一系列動(dòng)作等方面上。
根據(jù)協(xié)程的特點(diǎn),協(xié)程中可以使用“循環(huán)+條件判斷+yieldreturn幀數(shù)/newWaitForSeconds(秒數(shù))”實(shí)現(xiàn)每隔多少幀或者多少秒后再次執(zhí)行條件判斷語(yǔ)句,當(dāng)判斷語(yǔ)句滿足跳出循環(huán),從而讓協(xié)程代碼執(zhí)行完結(jié)束,回到主線程繼續(xù)執(zhí)行后續(xù)代碼,利用此功能,可以很方便地實(shí)現(xiàn)某物體達(dá)到某狀態(tài)后再執(zhí)行程序。
示例代碼:
主線程代碼
StartCoroutine(actor.Move(getAttackPosition(nTargetIndex)));
Inti=0;
協(xié)同程序Move方法
publicIEnumeratorMove(Vector3destPos)
{
while(transform.localPosition!=destPos)
{
transform.localPosition=Vector3.MoveTowards(transform.localPosition,destPos,GlobalData.BATTLE_SPRITE_MOVE_SPEED*Time.deltaTime);
yieldreturn0;
}
}
上述代碼中,主線程StartCoroutine方法創(chuàng)建一個(gè)協(xié)程Move,待協(xié)程Move執(zhí)行完成返回后,主線程才執(zhí)行i=0語(yǔ)句??墒菂f(xié)程Move的返回條件有點(diǎn)特殊,yieldreturn0語(yǔ)句表示協(xié)程運(yùn)行到此處掛起,等下一幀再?gòu)谋菊Z(yǔ)句繼續(xù)運(yùn)行,由于yieldreturn0在while循環(huán)內(nèi),所以不管yieldreturn0多少次,都依然在while循環(huán)內(nèi),而且while循環(huán)每一幀循環(huán)一次。直到while不滿足循環(huán)條件(即transform.localPosition==destPos),則協(xié)程能夠完成使命執(zhí)行完成并返回??梢圆聹y(cè)到,所控制的物體可能通過Update方法或者其他方法每幀在移動(dòng),直到移動(dòng)到目標(biāo)地點(diǎn),才執(zhí)行主線程后面的代碼。
2.3單例設(shè)計(jì)模式
2.3.1單例設(shè)計(jì)模式
單例設(shè)計(jì)模式是常見和簡(jiǎn)單的一種軟件設(shè)計(jì)模式,單例模式下的類能確保在整個(gè)工程項(xiàng)目中只允許存在最多一個(gè)實(shí)例對(duì)象。單例模式下的類通常不能直接通過構(gòu)造函數(shù)new出一個(gè)實(shí)例,而且構(gòu)造函數(shù)通常會(huì)設(shè)置成私有,外部不可訪問。只有通過類的一個(gè)靜態(tài)方法可以創(chuàng)建或獲取有且只有唯一一個(gè)的對(duì)象實(shí)例。
2.3.2單例設(shè)計(jì)模式的運(yùn)用
單例設(shè)計(jì)模式下的類往往具有通用性或全局性,用于保存全局?jǐn)?shù)據(jù),需要訪問其變量的外部類只需要通過單例類的靜態(tài)方法即可獲得其唯一的實(shí)例,從而訪問其內(nèi)部變量。
示例代碼:
publicclassClientSocket//單例類ClientSocket
{
privatestaticClientSocket_client_socket;//保存本類的唯一實(shí)例
privatestaticMessageManager_msg_mgr;
privatestaticSocket_socket;
privatestring_web="";
privateIPAddress_ip;
privateint_port=8000;
privatebyte[]_buf=newbyte[1024];
privateCircularBuffer_circularBuf=newCircularBuffer(4096);
privateClientSocket(){
}
publicstaticClientSocketgetInstance()
{
if(_client_socket==null)//若實(shí)例不存在則創(chuàng)建,實(shí)例已存在則直接返回
{
_client_socket=newClientSocket();
_msg_mgr=MessageManager.getInstance();
}
return_client_socket;
}
……
}
2.3.3單例設(shè)計(jì)模式在Unity中的運(yùn)用
在Unity中使用單例設(shè)計(jì)模式,有時(shí)會(huì)有額外的意義。在Unity中,通常一個(gè)類會(huì)作為一個(gè)組件掛載到一個(gè)物體上,可是如果進(jìn)行了場(chǎng)景切換,當(dāng)前場(chǎng)景所有物體以及物體所掛載的所有組件都會(huì)被銷毀,這時(shí)如果想要所有場(chǎng)景都能訪問一個(gè)類的實(shí)例以及其變量,則需要把該類設(shè)置成單例類,并且不能繼承MonoBehaviour,而且不能掛載到任何一個(gè)物體上,這樣就可以保證整個(gè)游戲中不管場(chǎng)景怎么切換,該類的實(shí)例一直存在,而且只有唯一一個(gè)實(shí)例,所有場(chǎng)景均可通過靜態(tài)方法獲取實(shí)例并且訪問。
2.4分包算法
2.4.1分包的原因
TCP在發(fā)送消息時(shí),如果發(fā)送間隔太短,發(fā)送方有可能出現(xiàn)一次發(fā)送多個(gè)消息的情況,而接收方便會(huì)一次接收到多個(gè)消息,另外,很多時(shí)候網(wǎng)絡(luò)情況較差或不穩(wěn)定,接收方遲遲收不到消息,過一段時(shí)間有可能一次過接收到此前該接收的多個(gè)消息,這些現(xiàn)象就是粘包。
由于粘包現(xiàn)象的存在,如果不進(jìn)行分包,接收方可能只能讀取到粘包數(shù)據(jù)的頭一個(gè)消息,后面的消息將會(huì)丟失,甚至影響后續(xù)接收的消息,嚴(yán)重影響程序的運(yùn)行。因此,我們需要一個(gè)分包算法來(lái)檢測(cè)粘包情況并進(jìn)行分包,以便正確讀取所有來(lái)自發(fā)送方的消息。
2.4.2環(huán)形緩沖區(qū)(CircularBuffer)
在分包算法中,我們需要用到一個(gè)數(shù)據(jù)結(jié)構(gòu)——環(huán)形緩沖區(qū)。之所以要用到環(huán)形緩沖區(qū),是因?yàn)榉职拔覀円呀邮盏降乃袛?shù)據(jù)存放在一個(gè)緩沖區(qū)中,分包時(shí)根據(jù)消息的長(zhǎng)度從緩沖區(qū)里取數(shù)據(jù),取出后緩沖區(qū)相應(yīng)地移除對(duì)應(yīng)的數(shù)據(jù),這樣反復(fù)頻繁進(jìn)行添加和刪除操作如果只用普通的緩沖區(qū)(byte數(shù)組/char數(shù)組)的話,則要頻繁地移動(dòng)數(shù)據(jù)存放的位置,十分影響程序運(yùn)行效率,為此需要引入環(huán)形緩沖區(qū)。
下面是環(huán)形緩沖區(qū)(CricularBuffer)類的UML類圖(C++):
圖2-1環(huán)形緩沖區(qū)UML類圖
說明:
buf:指向緩沖區(qū)的指針
front:緩沖區(qū)的數(shù)據(jù)的起始位置
rear:緩沖區(qū)的數(shù)據(jù)的尾部位置
lock:用于多線程同步的臨界區(qū),防止多個(gè)線程訪問修改緩沖區(qū)數(shù)據(jù)
size:緩沖區(qū)數(shù)據(jù)的長(zhǎng)度
capacity:緩沖區(qū)的容量
CircularBuffer:構(gòu)造函數(shù),創(chuàng)建一個(gè)指定容量為參數(shù)大小的緩沖區(qū)
~CircularBuffer:析構(gòu)函數(shù),釋放緩沖區(qū)
clear:清空緩沖區(qū)
pushBuf:往緩沖區(qū)尾部添加數(shù)據(jù)
popBuf:從緩沖區(qū)頭部得到數(shù)據(jù),并且緩沖區(qū)移除相應(yīng)數(shù)據(jù)
getBuf:從緩沖區(qū)頭部得到數(shù)據(jù),但緩沖區(qū)不移除數(shù)據(jù)
isEmpty:判斷緩沖區(qū)是否為空
由于環(huán)形緩沖區(qū)使用front起始位置和rear尾部位置記錄數(shù)據(jù)存放的位置,添加或移除數(shù)據(jù)不需要移動(dòng)數(shù)據(jù)的儲(chǔ)存位置,運(yùn)行效率高。
2.4.3分包算法流程圖
圖2-2分包算法流程圖
說明:
定義或獲得一個(gè)容量足夠大的環(huán)形緩沖區(qū)(具體大小請(qǐng)根據(jù)實(shí)際情況考慮);
接收到數(shù)據(jù)后,往環(huán)形緩沖區(qū)尾部添加數(shù)據(jù);
判斷緩沖區(qū)的數(shù)據(jù)長(zhǎng)度是否大于等于4(消息頭的長(zhǎng)度),本文實(shí)踐項(xiàng)目的結(jié)構(gòu)體消息頭由消息類型(ushort)和消息長(zhǎng)度(ushort)組成,共4字節(jié)。若緩沖區(qū)數(shù)據(jù)長(zhǎng)度小于消息頭長(zhǎng)度,則無(wú)法獲取第一條消息的長(zhǎng)度,也無(wú)法分包,也表明數(shù)據(jù)接收不全,回到(2)繼續(xù)接收數(shù)據(jù),不進(jìn)行任何操作;若緩沖區(qū)數(shù)據(jù)長(zhǎng)度大于等于消息頭長(zhǎng)度,則開始進(jìn)行分包;
從緩沖區(qū)頭部getBuf獲得4字節(jié)數(shù)據(jù)(消息頭),不改變緩沖區(qū)數(shù)據(jù),從消息頭得到第一包消息的長(zhǎng)度;
判斷緩沖區(qū)數(shù)據(jù)長(zhǎng)度,若緩沖區(qū)數(shù)據(jù)長(zhǎng)度大于第一包消息的長(zhǎng)度,則第一包消息接收完并可取,根據(jù)第一包消息的長(zhǎng)度popBuf得到第一包消息數(shù)據(jù),并且緩沖區(qū)移除相應(yīng)數(shù)據(jù),處理該消息;若緩沖區(qū)數(shù)據(jù)長(zhǎng)度小于第一包消息長(zhǎng)度(此時(shí)緩沖區(qū)數(shù)據(jù)長(zhǎng)度大于等于4,但是小于第一包消息的長(zhǎng)度),則說明第一包消息還沒接收完,回到(2)繼續(xù)接收,不進(jìn)行任何操作;
回到(4),一直循環(huán)。
第3章戰(zhàn)斗動(dòng)畫實(shí)現(xiàn)方案的研究與對(duì)比
3.1研究背景
筆者認(rèn)為回合制戰(zhàn)斗系統(tǒng)實(shí)現(xiàn)的最大難點(diǎn),那無(wú)疑就是客戶端戰(zhàn)斗動(dòng)畫的實(shí)現(xiàn)。回合制戰(zhàn)斗系統(tǒng)的戰(zhàn)斗動(dòng)畫,不是平常我們說的“動(dòng)畫”,它是由數(shù)據(jù)轉(zhuǎn)換過來(lái)的動(dòng)態(tài)動(dòng)畫,動(dòng)畫內(nèi)容由數(shù)據(jù)決定。例如動(dòng)畫數(shù)據(jù)是“某個(gè)角色攻擊某個(gè)角色”,動(dòng)畫內(nèi)容便是“攻擊者移動(dòng)到目標(biāo)面前并播放攻擊動(dòng)作動(dòng)畫,目標(biāo)播放受傷動(dòng)作動(dòng)畫,顯示扣血,攻擊者返回到本來(lái)的位置”;再例如動(dòng)畫數(shù)據(jù)是“某個(gè)角色使用技能攻擊某個(gè)角色”,動(dòng)畫內(nèi)容便是“施法者播放施法動(dòng)作動(dòng)畫,然后在目標(biāo)身上播放技能特效,等到特效播放到某一幀,目標(biāo)播放受傷動(dòng)作動(dòng)畫,顯示扣血”。
上述例子已經(jīng)是回合制戰(zhàn)斗系統(tǒng)最基本的戰(zhàn)斗動(dòng)畫,更不用說多次攻擊或者多次施法,可謂難上加難。更何況,戰(zhàn)斗動(dòng)畫的質(zhì)量極大影響回合制游戲的可玩性,而且回合制戰(zhàn)斗除了玩家下達(dá)戰(zhàn)斗指令外,其余時(shí)間全部都在播放戰(zhàn)斗動(dòng)畫上。
所以,筆者認(rèn)為客戶端的戰(zhàn)斗動(dòng)畫實(shí)現(xiàn)是整個(gè)回合制戰(zhàn)斗系統(tǒng)的重點(diǎn)和難點(diǎn),為了正確謹(jǐn)慎地抉擇實(shí)現(xiàn)方案,少走彎路,額外對(duì)戰(zhàn)斗動(dòng)畫的實(shí)現(xiàn)展開了研究。
3.2實(shí)現(xiàn)方案一:有限狀態(tài)機(jī)
這是筆者最先想到的方案,畢竟有限狀態(tài)機(jī)在游戲編程中實(shí)在極為常用,相信不少讀者也會(huì)率先聯(lián)想到這種方式。
有限狀態(tài)機(jī)代碼示例:
voidUpdate(){
switch(state)
{
case(int)State.Standby:
……
break;
case(int)State.Attack:
……
break;
case(int)State.Magic:
……
break;
……
}
……
}
有限狀態(tài)機(jī)方案下,Update方法每幀檢測(cè)記錄狀態(tài)的變量state,從而決定究竟要執(zhí)行何種行為,當(dāng)行為執(zhí)行完或者某個(gè)事件觸發(fā)時(shí),通過修改狀態(tài)變量state,改變執(zhí)行的行為。
優(yōu)點(diǎn):
(1)靈活可變,隨時(shí)隨地都可以改變狀態(tài),從而改變執(zhí)行的行為
缺點(diǎn):
每幀都需要檢測(cè)狀態(tài)變量,影響程序運(yùn)行效率;
如果要實(shí)現(xiàn)一系列復(fù)雜的行為,代碼量大而且凌亂,邏輯復(fù)雜,例如要實(shí)現(xiàn)“攻擊者移動(dòng)到目標(biāo)面前,攻擊者播放攻擊動(dòng)畫,目標(biāo)播放受傷動(dòng)畫,顯示扣血,攻擊者返回原本位置”這樣的功能,可能需要不止一個(gè)狀態(tài)機(jī);
Update方法變得臃腫,很多時(shí)候Update還需要做其他事情;
3.3實(shí)現(xiàn)方案二:協(xié)同程序(Coroutine)
在感覺方案一可行性不高的情況下,本人開始尋求其他實(shí)現(xiàn)方案,直到發(fā)現(xiàn)協(xié)同程序(Coroutine)。若讀者不了解協(xié)程可閱本文第二章,協(xié)同程序在本文第2章有簡(jiǎn)略介紹,此處不再作介紹。個(gè)人感覺Unity的協(xié)同程序跟Cocos2d的CCSequence動(dòng)作有點(diǎn)像,都能很方便實(shí)現(xiàn)按順序執(zhí)行一系列動(dòng)作,而且協(xié)同程序比CCSequence更靈活。
協(xié)同程序示例代碼:
IEnumeratorAttack(BATTLE_ANIMbattleAnim)
{
BattleSpritesprite=battleSprites[battleAnim.nActorIndex-1];
yieldreturnStartCoroutine(sprite.Move(getAttackPosition(nTargetIndex)));
yieldreturnStartCoroutine(sprite.AttackAnim());
yieldreturnStartCoroutine(sprite.Back(actor.vOrigPos));
}
協(xié)程Attack()能夠簡(jiǎn)單清晰實(shí)現(xiàn)“控制某個(gè)物體移動(dòng)到某個(gè)地點(diǎn),完成后進(jìn)行攻擊,攻擊完成后返回到某個(gè)地點(diǎn)”,一步完成之后再進(jìn)行下一步。
優(yōu)點(diǎn):
不需要在Update每幀調(diào)用,協(xié)程開啟后根據(jù)代碼邏輯等待(每)n幀/n秒自動(dòng)調(diào)用;
代碼邏輯清晰
能夠很好實(shí)現(xiàn)按順序執(zhí)行一系列復(fù)雜的行為
缺點(diǎn):
如要中途終止協(xié)程,有一定的終止條件
沒狀態(tài)機(jī)靈活,難以臨時(shí)轉(zhuǎn)變行為
3.4方案抉擇結(jié)論
顯而易見,經(jīng)過筆者的研究和深思熟慮,本文采用了協(xié)同程序方案。理由很簡(jiǎn)單,回合制戰(zhàn)斗動(dòng)畫幾乎全避開了協(xié)程的缺點(diǎn),體現(xiàn)了協(xié)程的優(yōu)點(diǎn)。因?yàn)榛睾现茟?zhàn)斗動(dòng)畫是由服務(wù)器發(fā)來(lái)的動(dòng)畫數(shù)據(jù)轉(zhuǎn)換的,數(shù)據(jù)自始至終沒有改變,客戶端只負(fù)責(zé)播放動(dòng)畫效果,并沒有修改數(shù)據(jù),那么從數(shù)據(jù)轉(zhuǎn)化為動(dòng)畫那刻開始,動(dòng)畫就不可能有變化或者中途終止,并不需要程序的靈活性。這么看來(lái),協(xié)程用在回合制戰(zhàn)斗動(dòng)畫的播放很是適合。
另外,讀者也可考慮行為樹方案,由于筆者時(shí)間和能力有限,截至目前未能深入學(xué)習(xí)研究行為樹,不知是否能比協(xié)程更好地實(shí)現(xiàn)戰(zhàn)斗動(dòng)畫,所以本文未能提及,懇請(qǐng)?bào)w諒。
第4章回合制網(wǎng)游戰(zhàn)斗系統(tǒng)設(shè)計(jì)
4.1回合制網(wǎng)游戰(zhàn)斗系統(tǒng)設(shè)計(jì)
我們從回合制網(wǎng)游中不難發(fā)現(xiàn),在客戶端面前,我們可以看到一場(chǎng)回合制戰(zhàn)斗可劃分為以下階段:進(jìn)入戰(zhàn)斗-玩家下達(dá)戰(zhàn)斗指令(很多情況下需要下達(dá)人物和寵物兩個(gè)戰(zhàn)斗指令)-戰(zhàn)斗動(dòng)畫-戰(zhàn)斗結(jié)束。經(jīng)過本人對(duì)上述戰(zhàn)斗過程的研究、思考和分析,得出個(gè)人的回合制戰(zhàn)斗系統(tǒng)實(shí)現(xiàn)思路,戰(zhàn)斗系統(tǒng)流程圖如圖:
圖4-1回合制網(wǎng)游戰(zhàn)斗系統(tǒng)流程圖
4.2回合制網(wǎng)游戰(zhàn)斗系統(tǒng)服務(wù)器實(shí)現(xiàn)思路
(1)戰(zhàn)斗開始發(fā)送“戰(zhàn)斗初始化消息”給客戶端,“戰(zhàn)斗初始化消息”包含所有戰(zhàn)斗角色(人物和寵物)的名字、圖形及動(dòng)畫控制器ID、最大Hp、當(dāng)前Hp、最大Mp、當(dāng)前Mp等數(shù)據(jù),客戶端只關(guān)心需要顯示的內(nèi)容;
(2)根據(jù)戰(zhàn)斗角色的數(shù)量,等待接收并保存數(shù)量與角色數(shù)量想當(dāng)?shù)膩?lái)自客戶端的“戰(zhàn)斗指令消息”,即等待所有戰(zhàn)斗角色都下達(dá)完戰(zhàn)斗指令;
(3)接收到足夠數(shù)量的“角色戰(zhàn)斗指令消息”后,根據(jù)角色的“速度”屬性數(shù)值,倒序(即速度高者先行動(dòng))處理每一個(gè)戰(zhàn)斗指令所造成的影響,并更新服務(wù)器各個(gè)角色的數(shù)值,發(fā)送對(duì)應(yīng)的“動(dòng)畫消息”給所有客戶端,每處理一個(gè)“戰(zhàn)斗指令消息”就發(fā)送一個(gè)對(duì)應(yīng)的“動(dòng)畫消息”;
(4)待所有“戰(zhàn)斗指令消息”處理完并發(fā)送完對(duì)應(yīng)的“動(dòng)畫消息”后,發(fā)送“動(dòng)畫播放消息”;
(5)判斷雙方角色的陣亡情況,若一方符合戰(zhàn)敗條件(所有角色陣亡或者所有人物陣亡,具體根據(jù)游戲設(shè)定決定)則結(jié)束戰(zhàn)斗,否則回到步驟(2)進(jìn)入下一個(gè)回合,如此循環(huán)。
4.3回合制網(wǎng)游戰(zhàn)斗系統(tǒng)客戶端實(shí)現(xiàn)思路
(1)若接收到“戰(zhàn)斗初始化消息”,則進(jìn)入戰(zhàn)斗,并根據(jù)消息里的數(shù)據(jù),初始化每一個(gè)戰(zhàn)斗角色;
(2)顯示人物戰(zhàn)斗指令菜單,根據(jù)玩家的操作,發(fā)送“戰(zhàn)斗指令消息”,人物戰(zhàn)斗指令菜單消失;
(3)若沒有參戰(zhàn)寵物,直接跳到下一步,若有參戰(zhàn)寵物,顯示寵物戰(zhàn)斗指令菜單,根據(jù)玩家的操作,發(fā)送“戰(zhàn)斗指令消息”,寵物戰(zhàn)斗指令菜單消失;
(4)接收并保存每一個(gè)來(lái)自服務(wù)器的“動(dòng)畫消息”或“動(dòng)畫播放消息”;
(5)收到“動(dòng)畫播放消息”后,讀取并處理每一個(gè)“動(dòng)畫消息”,轉(zhuǎn)化為對(duì)應(yīng)等量的戰(zhàn)斗動(dòng)畫類并用容器保存,遍歷容器播放所有動(dòng)畫;
(6)當(dāng)前回合所有戰(zhàn)斗動(dòng)畫播放完后,判斷雙方陣亡情況,若一方符合戰(zhàn)敗條件(所有角色陣亡或者所有人物陣亡,具體根據(jù)游戲設(shè)定決定)則結(jié)束戰(zhàn)斗,否則回到步驟(2)進(jìn)入下一個(gè)回合,如此循環(huán)。
4.4戰(zhàn)斗系統(tǒng)框架結(jié)構(gòu)(以實(shí)踐項(xiàng)目《PKer》為例)
雖然本文主要闡述戰(zhàn)斗系統(tǒng),但是由于戰(zhàn)斗系統(tǒng)較為復(fù)雜,為了讓讀者更好的理解本文所述戰(zhàn)斗系統(tǒng)的實(shí)現(xiàn)思路,以實(shí)踐項(xiàng)目《PKer》為例,對(duì)游戲的戰(zhàn)斗系統(tǒng)架構(gòu)和相關(guān)功能架構(gòu)進(jìn)行簡(jiǎn)單的羅列和說明。
圖4-2實(shí)踐項(xiàng)目《PKer》戰(zhàn)斗系統(tǒng)及相關(guān)功能架構(gòu)
說明:
玩家從游戲大廳可以搜索加入房間或創(chuàng)建房間,與房間內(nèi)其他玩家進(jìn)行對(duì)戰(zhàn)進(jìn)入戰(zhàn)斗(讀者可根據(jù)自身游戲玩法考慮如何觸發(fā)戰(zhàn)斗,如常見的回合制游戲通過暗雷或明雷遇敵、玩家對(duì)點(diǎn)觸發(fā)戰(zhàn)斗);
《PKer》中玩家可以直接在玩家屬性配置界面中通過點(diǎn)選“寵物庫(kù)”內(nèi)的寵物即可獲得對(duì)應(yīng)寵物,并且通過設(shè)置“參戰(zhàn)寵物”可讓寵物在戰(zhàn)斗中出戰(zhàn)(讀者可根據(jù)自身游戲玩法考慮寵物的獲得途徑,常見的途徑有戰(zhàn)斗捕捉、任務(wù)獎(jiǎng)勵(lì)等);
《PKer》中玩家可以直接在玩家屬性配置界面中通過點(diǎn)選“裝備物品庫(kù)”內(nèi)的裝備/物品即可獲得對(duì)應(yīng)裝備/物品,裝上裝備將提高角色的屬性使角色更具戰(zhàn)斗力,放在物品欄的物品可以在戰(zhàn)斗中使用回復(fù)Hp/Mp(讀者可根據(jù)自身游戲玩法考慮裝備物品的獲得途徑,常見的途徑有戰(zhàn)斗獎(jiǎng)勵(lì)、任務(wù)獎(jiǎng)勵(lì)等);
《PKer》中玩家可以直接在玩家屬性配置界面中更改職業(yè),從而獲得在戰(zhàn)斗中使用的技能;
進(jìn)入戰(zhàn)斗后,參戰(zhàn)角色的屬性由各個(gè)玩家配置(屬性加點(diǎn)、裝備、參戰(zhàn)寵物、職業(yè)等)所決定;
各玩家通過指令菜單下達(dá)指令從而控制自身角色和自身參戰(zhàn)寵物(如有設(shè)置參戰(zhàn)寵物)的行動(dòng)。其中,使用技能能夠選擇使用自身職業(yè)的技能,使用物品能夠選擇使用物品欄的物品;
戰(zhàn)斗動(dòng)畫主要由多個(gè)協(xié)同程序組成來(lái)實(shí)現(xiàn),根據(jù)各個(gè)參戰(zhàn)人物/寵物的下達(dá)的戰(zhàn)斗指令對(duì)應(yīng)的播放其行為的動(dòng)畫,不同的行為動(dòng)畫內(nèi)容不一樣;
而在技能動(dòng)畫中,為了增加回合制戰(zhàn)斗的畫面感和可玩性,對(duì)不同的技能配置了不同的特效播放方式,讓特效動(dòng)畫更華麗更多元化,詳細(xì)實(shí)現(xiàn)方式在本文5.3.6和5.3.7小節(jié)有闡述;
第5章回合制戰(zhàn)斗系統(tǒng)實(shí)現(xiàn)
5.1戰(zhàn)斗系統(tǒng)相關(guān)消息結(jié)構(gòu)體的定義
結(jié)構(gòu)體消息是服務(wù)器與客戶端溝通的“共同語(yǔ)言”,結(jié)構(gòu)體消息在客戶端和服務(wù)器分別定義并且數(shù)據(jù)結(jié)構(gòu)是相同,發(fā)送方填寫結(jié)構(gòu)體數(shù)據(jù),接收方先獲取消息頭的消息類型ID和長(zhǎng)度,再判斷是哪種類型的消息并轉(zhuǎn)換成該類型結(jié)構(gòu)體,然后讀取內(nèi)容并進(jìn)行對(duì)應(yīng)的處理。
以下是Unity客戶端(C#)中定義的相關(guān)結(jié)構(gòu)體消息,服務(wù)器的定義與其一致,但由于服務(wù)器(C++)與客戶端(C#)的編程語(yǔ)言不同所以定義語(yǔ)法有些許區(qū)別。
//戰(zhàn)斗初始化消息
publicstructMSG_BATTLE_INIT
{
publicushortnMsgID;//消息類型ID
publicushortnLen;//結(jié)構(gòu)體長(zhǎng)度
publicuintnBattleID;//戰(zhàn)局ID
[MarshalAs(UnmanagedType.ByValArray,SizeConst=20)]
publicBATTLE_SPRITE_INIT[]battleSprites;//所有戰(zhàn)斗角色基本數(shù)據(jù)
};
//戰(zhàn)斗角色(人物/寵物)基本數(shù)據(jù)消息
publicstructBATTLE_SPRITE_INIT
{
publicuintnID;//玩家/寵物ID(客戶端用此值判斷是否有戰(zhàn)斗成員,可能還會(huì)用來(lái)標(biāo)識(shí)自身和自身寵物)
[MarshalAs(UnmanagedType.ByValArray,SizeConst=22)]
publicbyte[]strName;//戰(zhàn)斗人物/寵物名字
publicuintnImageID;//圖形及動(dòng)畫控制器ID
publicshortnHpMax;//最大HP
publicshortnHp;//當(dāng)前HP
publicshortnMpMax;//最大MP
publicshortnMp;//當(dāng)前MP
};
//戰(zhàn)斗指令消息
publicstructMSG_BATTLE_COMMAND
{
publicushortnMsgID;//消息類型ID
publicushortnLen;//結(jié)構(gòu)體長(zhǎng)度
publicushortnActorType;//行動(dòng)者類型(1=人物2=寵物,由服務(wù)端賦值,客戶端不需要賦值)
publicushortnActorIndex;//行動(dòng)者索引(由服務(wù)端賦值,客戶端不需要賦值)
publicushortnActionType;//行動(dòng)的類型
publicushortnTargetIndex;//目標(biāo)索引
publicushortnParam;//參數(shù)(根據(jù)行動(dòng)類型而定,如使用技能則是技能ID,使用物品則是物品欄位置,切換戰(zhàn)寵則是寵物欄位置)
};
//戰(zhàn)斗動(dòng)畫播放消息(客戶端收到此消息后開始播放戰(zhàn)斗動(dòng)畫)
publicstructMSG_BATTLE_ANIM_BEGIN
{
publicushortnMsgID;//消息類型ID
publicushortnLen;//結(jié)構(gòu)體長(zhǎng)度
};
//戰(zhàn)斗動(dòng)畫消息
publicstructMSG_BATTLE_ANIM
{
publicushortnMsgID;//消息類型ID
publicushortnLen;//結(jié)構(gòu)體長(zhǎng)度
publicushortnActorIndex;//行動(dòng)者索引
[MarshalAs(UnmanagedType.ByValArray,SizeConst=10)]
publicushort[]nTargetIndex;//目標(biāo)索引(初始值為0,0則無(wú)目標(biāo))
publicushortaction_type;//行動(dòng)類型
publicushortnParam;//參數(shù)(根據(jù)行動(dòng)類型而定,如使用技能則是技能ID,使用物品則是物品欄位置)
[MarshalAs(UnmanagedType.ByValArray,SizeConst=10)]
publicshort[]nActorHp;//行動(dòng)者Hp影響(<0:扣血,>0:加血,0=無(wú)任何影響)
[MarshalAs(UnmanagedType.ByValArray,SizeConst=10)]
publicshort[]nActorMp;//行動(dòng)者M(jìn)p影響
[MarshalAs(UnmanagedType.ByValArray,SizeConst=10)]
publicshort[]nTargetHp;//目標(biāo)Hp影響(<0:扣血,>0:加血,0=無(wú)任何影響)
[MarshalAs(UnmanagedType.ByValArray,SizeConst=10)]
publicshort[]nTargetMp;//目標(biāo)Mp影響
};
5.2服務(wù)器戰(zhàn)斗系統(tǒng)的實(shí)現(xiàn)
5.2.1服務(wù)器戰(zhàn)斗系統(tǒng)相關(guān)結(jié)構(gòu)體
圖5-1服務(wù)器戰(zhàn)斗系統(tǒng)的相關(guān)結(jié)構(gòu)體的關(guān)系圖
//戰(zhàn)局結(jié)構(gòu)體
structBATTLE
{
UINTnBattleID;//戰(zhàn)局ID
BATTLE_SPRITEbattleSprites[20];//戰(zhàn)斗角色(人物/寵物)數(shù)組
std::list<USHORT>team1_survivors_index;//隊(duì)伍1存活者索引list
std::list<USHORT>team2_survivors_index;//隊(duì)伍2存活者索引list
USHORTnSpriteTotal;//參戰(zhàn)角色總數(shù)
std::multimap<float,MSG_BATTLE_COMMAND*>m_CommandMap;//玩家戰(zhàn)斗指令字典,用于保存每回合接收的戰(zhàn)斗指令消息,Key為角色速度數(shù)值
}
說明:
battleSprites數(shù)組的索引0-4位置分別為隊(duì)伍1人物1、隊(duì)伍1人物2……,索引5-9位置分別為隊(duì)伍1人物1的寵物、隊(duì)伍1人物2的寵物……,類似的,索引10-14為隊(duì)伍2人物,索引15-19為隊(duì)伍2寵物,人物索引+5即該人物的寵物的索引,根據(jù)人物索引即可獲得其寵物索引
//戰(zhàn)斗角色(人物/寵物)
structBATTLE_SPRITE
{
BATTLE_SPRITE_DATA*pBattleData;//戰(zhàn)斗數(shù)據(jù)(包含Hp、Mp、攻擊力等)
CLIENT_DATA*pClient;//客戶端數(shù)據(jù)(CLIENT_DATA包含客戶端IOCP完成鍵、套接字、玩家游戲數(shù)據(jù)等數(shù)據(jù),本文不闡述,請(qǐng)讀者根據(jù)自身游戲服務(wù)器架構(gòu)設(shè)計(jì))
USHORTnType;//角色類型,1=人,2=寵物
USHORTbCommand;//是否已經(jīng)下命令,0=沒下命令,1=已下命令
}
//戰(zhàn)斗數(shù)據(jù)(人物與寵物通用)
structBATTLE_SPRITE_DATA
{
UINTnID;//玩家/寵物ID
charstrName[22];//戰(zhàn)斗人物/寵物名字
UINTnImageID;//圖形及動(dòng)畫控制器ID
USHORTnLevel;//等級(jí)
shortnHpMax;//最大Hp
shortnHp;//當(dāng)前Hp
shortnMpMax;//最大Mp
shortnMp;//當(dāng)前Mp
shortnAtk;//物理攻擊力
shortnDef;//物理防御力
shortnMat;//魔法攻擊力
shortnMdf;//魔法防御力
shortnSpd;//速度
}
5.2.2初始化一個(gè)戰(zhàn)局
即初始化一個(gè)戰(zhàn)局BATTLE實(shí)例,并根據(jù)參戰(zhàn)玩家的數(shù)據(jù)對(duì)戰(zhàn)局所有成員進(jìn)行賦值,由于成員較多,并且游戲邏輯復(fù)雜,讀者可根據(jù)自身游戲邏輯賦值,此處初始化的方式不影響后續(xù)的實(shí)現(xiàn),所以這里不進(jìn)行詳細(xì)闡述,最后在客戶端數(shù)據(jù)存放指向戰(zhàn)局的指針,以便后面通過完成鍵以及客戶端數(shù)據(jù)能夠獲得戰(zhàn)局?jǐn)?shù)據(jù)。
本文假設(shè)一個(gè)戰(zhàn)局最多能5位玩家對(duì)戰(zhàn)5位玩家,包括每位玩家可出戰(zhàn)的一個(gè)戰(zhàn)斗寵物,所以一個(gè)戰(zhàn)局最多能有20個(gè)戰(zhàn)斗角色,故battleSprites戰(zhàn)斗角色數(shù)組長(zhǎng)度為20,讀者可根據(jù)自身游戲邏輯進(jìn)行變更。另外,如果戰(zhàn)局不足20個(gè)戰(zhàn)斗角色,本文通過檢查battleSprites中的戰(zhàn)斗數(shù)據(jù)pBattleData是否為NULL進(jìn)行判斷并計(jì)數(shù),用成員nSpriteTotal保存戰(zhàn)斗角色總數(shù)。戰(zhàn)局初始時(shí)所有戰(zhàn)斗角色理應(yīng)是存活的,battleSprites中所有存在的戰(zhàn)斗角色的索引相應(yīng)的存放到隊(duì)伍1存活索引list或隊(duì)伍2存活索引list中,本文假設(shè)索引0-9為隊(duì)伍1(0-4隊(duì)伍1人物,5-9隊(duì)伍1寵物),10-19為隊(duì)伍2(10-14為隊(duì)伍2人物,15-19為隊(duì)伍2寵物),并且人物和其寵物的索引相差5(即0索引上的人物其寵物索引為5,如此類推)。
5.2.3接收客戶端的戰(zhàn)斗指令消息
圖5-2接收戰(zhàn)斗指令消息流程圖
收到客戶端發(fā)來(lái)的消息后,通過消息頭和消息類型ID(可用宏定義或定義枚舉類型)的判斷,確定是否為戰(zhàn)斗指令消息后,然后處理戰(zhàn)斗指令消息。
本文實(shí)踐項(xiàng)目從IOCP完成鍵可以獲取玩家數(shù)據(jù)進(jìn)而獲取玩家所在的戰(zhàn)局指針(BATTLE*)。收到戰(zhàn)斗指令消息后,通過消息中的行動(dòng)者索引nActorIndex從戰(zhàn)局的戰(zhàn)斗角色battleSprites數(shù)組中獲取該角色,根據(jù)該角色的“速度”數(shù)值和戰(zhàn)斗指令消息一并加入到戰(zhàn)斗指令字典m_CommandMap中,以便之后遍歷逐個(gè)處理。檢測(cè)m_CommandMap的元素個(gè)數(shù)是否等于戰(zhàn)斗角色總數(shù)(即檢測(cè)是否所有戰(zhàn)斗角色都已下達(dá)戰(zhàn)斗指令,若等于則開始遍歷m_CommandMap處理本回合的所有戰(zhàn)斗指令。
5.2.4處理戰(zhàn)斗指令并發(fā)送動(dòng)畫消息
圖5-3服務(wù)器處理戰(zhàn)斗指令消息流程圖
當(dāng)本回合所有戰(zhàn)斗角色都下達(dá)戰(zhàn)斗指令(服務(wù)器收到的指令數(shù)等于角色數(shù)量)后,開始倒序遍歷(倒序的原因是,multimap按從小到大排序,而我們的邏輯是速度高的先行動(dòng))處理每一條戰(zhàn)斗指令。
倒序遍歷時(shí),先判斷發(fā)出該戰(zhàn)斗指令消息的角色是否已經(jīng)死亡,若死亡則不能進(jìn)行任何行為,直接跳出進(jìn)入下一次循環(huán)。若發(fā)出指令的角色沒有死亡,根據(jù)指令的nActionType行動(dòng)類型(攻擊、使用技能、使用物品、更換寵物等)進(jìn)行不同的處理,計(jì)算每一條戰(zhàn)斗指令所造成的影響,并發(fā)送對(duì)應(yīng)的動(dòng)畫消息。
若在計(jì)算完每一條戰(zhàn)斗指令所造成的影響后,檢查雙方存活狀況,若一方全員陣亡則戰(zhàn)斗結(jié)束,并發(fā)送戰(zhàn)斗動(dòng)畫播放消息,客戶端開始播放動(dòng)畫。
若計(jì)算完所有戰(zhàn)斗指令后戰(zhàn)斗并沒有結(jié)束,發(fā)送戰(zhàn)斗動(dòng)畫播放消息,客戶端開始播放動(dòng)畫,進(jìn)入下一回合,清空m_CommandMap所有元素,服務(wù)器繼續(xù)接收新回合的來(lái)自客戶端的戰(zhàn)斗指令消息。
下面是不同的行動(dòng)類型的不同處理方式:
攻擊:
聲明一個(gè)戰(zhàn)斗動(dòng)畫消息,nActionType賦值為“攻擊(枚舉或自定義宏)”,根據(jù)戰(zhàn)斗指令消息里的nActorIndex行動(dòng)者索引和nTargetIndex目標(biāo)索引從戰(zhàn)局中獲取對(duì)應(yīng)的戰(zhàn)斗角色(battleSprites[nActorIndex]和battleSprites[nTargetIndex]),若目標(biāo)死亡則從對(duì)應(yīng)隊(duì)伍的存活者列表中隨機(jī)一個(gè)新目標(biāo)索引。把目標(biāo)索引值賦值給戰(zhàn)斗動(dòng)畫消息的nTargetIndex[0]。
根據(jù)行動(dòng)者攻擊和目標(biāo)防御力計(jì)算傷害值,賦值給nTargetHp[0],并且目標(biāo)扣除對(duì)應(yīng)Hp,判斷目標(biāo)扣除Hp后Hp是否小于等于0(即死亡),若死亡則從對(duì)應(yīng)隊(duì)伍的存活者列表中移除。
最后,發(fā)送戰(zhàn)斗動(dòng)畫消息給戰(zhàn)局所有玩家。
使用技能:
本文的實(shí)踐項(xiàng)目通過數(shù)據(jù)庫(kù)配置所有技能的相關(guān)數(shù)據(jù),包括技能的ID、作用目標(biāo)數(shù)量、特效動(dòng)畫ID、特效播放方式(僅在客戶端使用)、Mp消耗、傷害公式等。
聲明一個(gè)戰(zhàn)斗動(dòng)畫消息,nActionType賦值為“使用技能(枚舉或自定義宏)”,通過戰(zhàn)斗指令消息的nParam查找數(shù)據(jù)庫(kù)的技能表獲取該技能的相關(guān)數(shù)據(jù),根據(jù)戰(zhàn)斗指令消息的nActorIndex和nTargetIndex從戰(zhàn)局獲得對(duì)應(yīng)的角色battleSprites[nActorIndex]和battleSprites[nTargetIndex]),根據(jù)技能Mp消耗數(shù)值賦值到動(dòng)畫消息的nActorMp上,并且減少使用者角色的Mp。如果目標(biāo)角色死亡,則從目標(biāo)角色的隊(duì)伍存活者列表中隨機(jī)得到新的目標(biāo)索引,并賦值多體戰(zhàn)斗動(dòng)畫消息nTargetIndex數(shù)組的對(duì)應(yīng)索引位置上,再根據(jù)行動(dòng)者的魔法攻擊力和目標(biāo)的魔法防御力以及技能的傷害公式計(jì)算傷害數(shù)值,賦值nTargetHp數(shù)組對(duì)應(yīng)的索引位置上,并且目標(biāo)扣除對(duì)應(yīng)Hp,判斷目標(biāo)扣除Hp后Hp是否小于等于0(即死亡),若死亡則從對(duì)應(yīng)隊(duì)伍的存活者列表中移除。之后再隨機(jī)目標(biāo),并計(jì)算傷害數(shù)值,直到目標(biāo)數(shù)量達(dá)到技能的作用目標(biāo)數(shù)量。
最后,發(fā)送戰(zhàn)斗動(dòng)畫消息給戰(zhàn)局所有玩家。
使用物品:
本文的實(shí)踐項(xiàng)目通過數(shù)據(jù)庫(kù)配置所有物品的相關(guān)數(shù)據(jù),包括物品的ID、作用于單體/全體、Hp回復(fù)量、Mp回復(fù)量等。
聲明一個(gè)戰(zhàn)斗動(dòng)畫消息,nActionType賦值為“使用物品(枚舉或自定義宏)”,通過戰(zhàn)斗指令消息的nParam獲取玩家對(duì)應(yīng)物品欄位置的物品ID(物品欄的實(shí)現(xiàn)本文不闡述),把物品ID賦值給戰(zhàn)斗動(dòng)畫消息的nParam,根據(jù)物品ID查找數(shù)據(jù)庫(kù)的物品表獲取該物品的相關(guān)數(shù)據(jù),根據(jù)戰(zhàn)斗指令消息的nActorIndex賦值給動(dòng)畫消息的nActorIndex,根據(jù)物品是作用于單體還是全體對(duì)動(dòng)畫消息的nTargetIndex數(shù)組進(jìn)行賦值,根據(jù)物品的Hp回復(fù)量和Mp回復(fù)量分別對(duì)nTargetHp和nTargetMp數(shù)組進(jìn)行賦值。根據(jù)物品回復(fù)量更新目標(biāo)的Hp/Mp,注意不能超出最大Hp上限/最大Mp上限。
最后,發(fā)送戰(zhàn)斗動(dòng)畫消息給戰(zhàn)局所有玩家。
5.2.5說明
本文為了盡量簡(jiǎn)短地闡述實(shí)現(xiàn)方式,戰(zhàn)斗指令消息的行動(dòng)者索引nActorIndex由客戶端直接賦值,服務(wù)器并沒有對(duì)此進(jìn)行檢查而直接使用。若客戶端并沒有使用自身角色的索引賦值而對(duì)nActorIndex進(jìn)行修改作弊,則能夠冒充其他玩家角色下達(dá)戰(zhàn)斗指令,存在隱患,所以戰(zhàn)斗指令消息的nActorIndex應(yīng)該由服務(wù)器賦值并檢查,但由于本文篇幅所限并且考慮到游戲邏輯的復(fù)雜性和不一性,上述實(shí)現(xiàn)方式并沒有過多考慮防范客戶端作弊,懇請(qǐng)讀者體諒。
而實(shí)際上,服務(wù)器在很多地方都應(yīng)該對(duì)客戶端發(fā)來(lái)的消息進(jìn)行合法性檢查,防范客戶端外掛或作弊,還請(qǐng)讀者根據(jù)自身游戲邏輯另外考慮。
5.3客戶端戰(zhàn)斗系統(tǒng)的實(shí)現(xiàn)
5.3.1客戶端戰(zhàn)斗系統(tǒng)功能模塊
圖5-4客戶端戰(zhàn)斗系統(tǒng)功能模塊
說明:
戰(zhàn)斗系統(tǒng)主要包含戰(zhàn)斗角色、戰(zhàn)斗動(dòng)畫和戰(zhàn)斗指令菜單三個(gè)模塊;
戰(zhàn)斗指令菜單類型分為人物和寵物兩種,分別用于下達(dá)人物和寵物戰(zhàn)斗指令,并發(fā)送戰(zhàn)斗指令消息給服務(wù)器;
戰(zhàn)斗角色由角色圖形、動(dòng)畫控制器和血條組成,通過動(dòng)畫控制器切換不同動(dòng)作的動(dòng)畫,主要負(fù)責(zé)戰(zhàn)斗角色的圖形顯示;
戰(zhàn)斗動(dòng)畫模塊主要有動(dòng)畫計(jì)數(shù)、把接收到的所有動(dòng)畫消息轉(zhuǎn)換為戰(zhàn)斗動(dòng)畫類以及播放戰(zhàn)斗動(dòng)畫三個(gè)功能;
動(dòng)畫計(jì)數(shù)主要是為了等待戰(zhàn)斗中所有正在播放的角色動(dòng)作動(dòng)畫、特效動(dòng)畫播放完,角色動(dòng)作動(dòng)畫或特效動(dòng)畫開始時(shí)會(huì)+1動(dòng)畫計(jì)數(shù),結(jié)束時(shí)會(huì)-1動(dòng)畫計(jì)數(shù),動(dòng)畫計(jì)數(shù)為0后,當(dāng)前單位行動(dòng)結(jié)束,下一個(gè)行動(dòng)單位開始行動(dòng);
播放戰(zhàn)斗動(dòng)畫模塊根據(jù)戰(zhàn)斗動(dòng)畫類數(shù)據(jù)播放不同行為的動(dòng)畫(主要使用協(xié)程來(lái)控制戰(zhàn)斗角色的移動(dòng)、切換戰(zhàn)斗角色的動(dòng)作動(dòng)畫、添加特效等)。
5.3.2客戶端戰(zhàn)斗系統(tǒng)相關(guān)數(shù)據(jù)類型
//戰(zhàn)斗動(dòng)畫類
publicclassBATTLE_ANIM
{
publicushortnActorIndex;//行動(dòng)方索引
publicushort[]nTargetIndex=newushort[10];//目標(biāo)索引
publicushortaction_type;//行動(dòng)類型
publicushortnParam;//參數(shù)(根據(jù)行動(dòng)類型而定,如使用技能則是技能ID,使用物品則是物品欄位置)
publicushortnAnimType;//技能動(dòng)畫類型
publicushortnEffectID;//技能特效ID
publicushortnTargetCount;//目標(biāo)總數(shù)(nTargetIndex數(shù)組中值不為0的個(gè)數(shù))
publicushortnReactionIndex;//當(dāng)前受影響的目標(biāo)索引
publicshortnActorMp;//行動(dòng)方Mp影響(多用于計(jì)算技能使用者的Mp消耗)
publicshort[]nTargetHp=newshort[10];//目標(biāo)索引對(duì)應(yīng)的目標(biāo)Hp影響
}
5.3.3進(jìn)入戰(zhàn)斗后初始化戰(zhàn)斗角色
收到來(lái)自服務(wù)器的戰(zhàn)斗初始化消息后,切換到戰(zhàn)斗場(chǎng)景,并根據(jù)戰(zhàn)斗初始化消息中的battleSprites數(shù)組(包含戰(zhàn)斗角色的玩家ID、圖形及動(dòng)畫控制器ID、名字、Hp和Mp等數(shù)據(jù))動(dòng)態(tài)生成對(duì)應(yīng)的戰(zhàn)斗角色。
并且從戰(zhàn)斗初始化消息中的battleSprites數(shù)組中,對(duì)比玩家ID(客戶端在登錄游戲后,理應(yīng)早已得到玩家ID,那么在這里與battleSprites數(shù)組的玩家ID進(jìn)行對(duì)比)查找自身角色和寵物的索引,用變量保存該索引,用于下達(dá)戰(zhàn)斗指令。
5.3.4下達(dá)戰(zhàn)斗指令并發(fā)送戰(zhàn)斗指令消息
下達(dá)戰(zhàn)斗指令實(shí)質(zhì)是地對(duì)戰(zhàn)斗指令消息的賦值。
一般是先下達(dá)人物戰(zhàn)斗指令,如有參戰(zhàn)寵物,再下達(dá)戰(zhàn)斗指令。在本文4.3.2中,進(jìn)入戰(zhàn)斗時(shí),客戶端已能獲得自身人物和寵物在戰(zhàn)斗中的索引,若是輪到人物下達(dá)戰(zhàn)斗指令,則戰(zhàn)斗指令消息的nActorIndex是人物的索引,若是輪到寵物下達(dá)戰(zhàn)斗指令,則指令消息的nActorIndex是寵物的索引,分情況賦值即可,然后根據(jù)不同的行動(dòng)類型對(duì)指令消息的nActionType行動(dòng)類型賦值,最后通過點(diǎn)選目標(biāo)對(duì)指令消息的nTargetIndex進(jìn)行賦值。
當(dāng)下達(dá)完人物和寵物的戰(zhàn)斗指令后,分別發(fā)送對(duì)應(yīng)的戰(zhàn)斗指令消息。
戰(zhàn)斗指令菜單的UI實(shí)現(xiàn)千變?nèi)f化,本文不作闡述,使用最簡(jiǎn)單的按鈕即可實(shí)現(xiàn)。
下圖為實(shí)踐項(xiàng)目的指令菜單,其中玩家選擇了使用技能的行動(dòng),并即將點(diǎn)選技能:
圖5-5指令菜單
5.3.5接收戰(zhàn)斗動(dòng)畫消息
根據(jù)每一條接收到的戰(zhàn)斗動(dòng)畫消息對(duì)new出來(lái)的戰(zhàn)斗動(dòng)畫類賦值,并push_back到一個(gè)順序容器(可考慮List)。
5.3.6收到戰(zhàn)斗動(dòng)畫播放消息并播放戰(zhàn)斗動(dòng)畫
當(dāng)接收到來(lái)自服務(wù)器的戰(zhàn)斗動(dòng)畫播放消息后,客戶端開始從頭到尾遍歷存放了多個(gè)戰(zhàn)斗動(dòng)畫類的順序容器,根據(jù)戰(zhàn)斗動(dòng)畫類的數(shù)據(jù),逐個(gè)播放動(dòng)畫。
戰(zhàn)斗動(dòng)畫的實(shí)現(xiàn)方式主要是依靠協(xié)同程序和動(dòng)畫計(jì)數(shù)器。角色播放動(dòng)作動(dòng)畫(除待機(jī))或每往場(chǎng)景中添加一個(gè)特效動(dòng)畫,動(dòng)畫計(jì)數(shù)加1,角色播放動(dòng)作動(dòng)畫(除待機(jī))結(jié)束時(shí)調(diào)用方法切回待機(jī)動(dòng)畫并且動(dòng)畫計(jì)數(shù)-1,特效動(dòng)畫播放結(jié)束時(shí)銷毀自身并且調(diào)用方法把動(dòng)畫計(jì)數(shù)減1,當(dāng)動(dòng)畫所有協(xié)程結(jié)束返回并且動(dòng)畫計(jì)數(shù)為0時(shí),當(dāng)前動(dòng)畫播放完畢,開始播放下一個(gè)動(dòng)畫。
當(dāng)播放完所有戰(zhàn)斗動(dòng)畫類后,清空容器,若一方全體陣亡,則戰(zhàn)斗結(jié)束退出戰(zhàn)斗場(chǎng)景,否則進(jìn)入下一回合,玩家開始下達(dá)新回合的戰(zhàn)斗指令。
根據(jù)戰(zhàn)斗動(dòng)畫類的行動(dòng)類型和特效播放方式的不同,動(dòng)畫播放實(shí)現(xiàn)方式也不同,由于動(dòng)畫的實(shí)現(xiàn)較為復(fù)雜,本文在5.3.7小節(jié)單獨(dú)以普通攻擊動(dòng)畫進(jìn)行較詳細(xì)的闡述,讀者可結(jié)合本小節(jié)和5.3.7小節(jié)進(jìn)行理解。
下面是各種戰(zhàn)斗動(dòng)畫的實(shí)現(xiàn)方式:
普通攻擊動(dòng)畫協(xié)程
①行動(dòng)者角色執(zhí)行協(xié)程Move(),從自身位置移動(dòng)到目標(biāo)角色位置,到達(dá)后協(xié)程結(jié)束返回;
②行動(dòng)者播放攻擊動(dòng)作動(dòng)畫,目標(biāo)播放受傷動(dòng)作動(dòng)畫并顯示目標(biāo)扣血數(shù)值,更新角色Hp/Mp,若角色Hp為0則播放死亡動(dòng)作動(dòng)畫;
③使用循環(huán)和yieldreturn0語(yǔ)句直到動(dòng)畫計(jì)數(shù)為0(即攻擊動(dòng)畫、受傷動(dòng)畫播放完)跳出循環(huán);
④行動(dòng)者角色執(zhí)行協(xié)程Back(),回到原本的位置,到達(dá)原本的位置后協(xié)程結(jié)束返回;
⑤協(xié)程結(jié)束返回。
攻擊技能動(dòng)畫協(xié)程
①行動(dòng)者角色執(zhí)行協(xié)程Move(),從自身位置移動(dòng)到目標(biāo)角色位置,到達(dá)后協(xié)程結(jié)束返回;
②行動(dòng)者播放攻擊動(dòng)作動(dòng)畫,添加并播放技能特效動(dòng)畫,目標(biāo)播放受傷動(dòng)作動(dòng)畫并顯示目標(biāo)扣血數(shù)值,更新角色Hp/Mp,若角色Hp為0則播放死亡動(dòng)作動(dòng)畫;
③使用while循環(huán)和yieldreturn0語(yǔ)句直到動(dòng)畫計(jì)數(shù)為0(即攻擊動(dòng)畫、受傷動(dòng)畫、特效動(dòng)畫播放完),后跳出循環(huán);
④行動(dòng)者角色執(zhí)行協(xié)程Back(),回到原本的位置,到達(dá)原本的位置后Back()協(xié)程結(jié)束返回;
⑤協(xié)程結(jié)束返回。
魔法技能(同步型特效)動(dòng)畫協(xié)程
①行動(dòng)者播放施法動(dòng)作動(dòng)畫,遍歷所有目標(biāo),并在目標(biāo)位置上添加并播放特效動(dòng)畫,目標(biāo)播放受傷動(dòng)作動(dòng)畫并顯示扣血數(shù)值,更新角色Hp/Mp,若角色Hp為0則播放死亡動(dòng)作動(dòng)畫;
②使用while循環(huán)和yieldreturn0語(yǔ)句直到動(dòng)畫計(jì)數(shù)為0(即攻擊動(dòng)畫、受傷動(dòng)畫、特效動(dòng)畫播放完),后跳出循環(huán);
③協(xié)程結(jié)束返回。
動(dòng)畫效果如下圖:
圖5-6魔法技能(同步型特效)動(dòng)畫效果
魔法技能(異步型特效)動(dòng)畫協(xié)程
①行動(dòng)者播放施法動(dòng)作動(dòng)畫,遍歷所有目標(biāo),并在目標(biāo)位置上添加并播放特效動(dòng)畫,目標(biāo)播放受傷動(dòng)作動(dòng)畫并顯示扣血數(shù)值,更新角色Hp/Mp,若角色Hp為0則播放死亡動(dòng)作動(dòng)畫,遍歷中使用yieldreturn幀數(shù)/秒數(shù)實(shí)現(xiàn)延遲,從而達(dá)到每個(gè)目標(biāo)身上出現(xiàn)的特效動(dòng)畫時(shí)間不一樣;
②使用while循環(huán)和yieldreturn0語(yǔ)句直到動(dòng)畫計(jì)數(shù)為0(即攻擊動(dòng)畫、受傷動(dòng)畫、特效動(dòng)畫播放完),后跳出循環(huán);
③協(xié)程結(jié)束返回。
動(dòng)畫效果如下圖:
圖5-7魔法技能(異步型特效)動(dòng)畫效果
魔法技能(彈射型特效)動(dòng)畫協(xié)程
①行動(dòng)者播放施法動(dòng)作動(dòng)畫,行動(dòng)者身上添加一個(gè)循環(huán)播放的特效動(dòng)畫;
②遍歷目標(biāo),使用協(xié)程控制特效動(dòng)畫的移動(dòng),使特效移動(dòng)到目標(biāo)的位置,到達(dá)位置后,目標(biāo)播放受傷動(dòng)作動(dòng)畫并顯示扣血數(shù)值,更新角色Hp/Mp,若角色Hp為0則播放死亡動(dòng)作動(dòng)畫,再讓特效移動(dòng)到下一個(gè)目標(biāo)的位置,如此類推,直到最后一個(gè)目標(biāo),手動(dòng)摧毀特效;
③協(xié)程結(jié)束返回。
動(dòng)畫效果如下圖:
圖5-8魔法技能(彈射型特效)動(dòng)畫效果
魔法技能(發(fā)射型特效)動(dòng)畫協(xié)程
①行動(dòng)者播放施法動(dòng)作動(dòng)畫,遍歷目標(biāo),行動(dòng)者身上添加數(shù)量與目標(biāo)數(shù)相同循環(huán)播放的特效動(dòng)畫,并增加同等的動(dòng)畫計(jì)數(shù);
②每個(gè)特效自身執(zhí)行協(xié)程讓自身移向?qū)?yīng)目標(biāo)的位置,到達(dá)后動(dòng)畫計(jì)數(shù)減1,特效銷毀自身,目標(biāo)播放受傷動(dòng)作動(dòng)畫并顯示扣血數(shù)值,更新角色Hp/Mp,若角色Hp為0則播放死亡動(dòng)作動(dòng)畫;
③使用while循環(huán)和yieldreturn0語(yǔ)句直到動(dòng)畫計(jì)數(shù)為0(即攻擊動(dòng)畫、受傷動(dòng)畫、特效動(dòng)畫播放完),后跳出循環(huán);
④協(xié)程結(jié)束返回。
動(dòng)畫效果如下圖:
圖5-9魔法技能(發(fā)射型特效)動(dòng)畫效果
使用物品動(dòng)畫協(xié)程
①行動(dòng)者播放施法動(dòng)作動(dòng)畫,并在所有目標(biāo)身上添加并播放用于恢復(fù)的特效動(dòng)畫并顯示數(shù)值,更新角色Hp/Mp;
②使用while循環(huán)和yieldreturn0語(yǔ)句直到動(dòng)畫計(jì)數(shù)為0(即攻擊動(dòng)畫、受傷動(dòng)畫、特效動(dòng)畫播放完),后跳出循環(huán);
③協(xié)程結(jié)束返回。
5.3.7戰(zhàn)斗動(dòng)畫實(shí)現(xiàn)細(xì)述
本小節(jié)以魔法技能動(dòng)畫(彈射型特效)的實(shí)現(xiàn)為例進(jìn)行細(xì)述,以便讀者理解實(shí)現(xiàn)原理。
相關(guān)偽代碼如下:
//動(dòng)畫播放協(xié)程
IEnumeratorPlayAnim()
{
while(l_AnimList不為空)//存放戰(zhàn)斗動(dòng)畫類的list容器不為空
{
//從容器中獲得頭一個(gè)戰(zhàn)斗動(dòng)畫類
BATTLE_ANIMbattleAnim=l_AnimList[0];
switch(battleAnim.action_type)//判斷行動(dòng)類型
{
case普通攻擊:
yieldreturnStartCoroutine(Attack(battleAnim));
break;
case使用技能:
根據(jù)battleAnim.nParam查詢數(shù)據(jù)庫(kù)技能表獲得技能動(dòng)畫類型
battleAnim.nAnimType=技能動(dòng)畫類型
switch(battleAnim.nAnimType)//判斷技能動(dòng)畫類型
{
case攻擊技能動(dòng)畫:
yieldreturnStartCoroutine(MagicAttack(battleAnim));
break;
case魔法技能動(dòng)畫(彈射型特效):
yieldreturnStartCoroutine(MagicBounce(battleAnim));
break;
……
}
break;
case使用物品:
yieldreturnStartCoroutine(Item(battleAnim));
break;
}
while(動(dòng)畫計(jì)數(shù)器!=0)//(一幀一次循環(huán))
{
//下一幀再回到這里執(zhí)行下一次循環(huán)
yieldreturn1;
}
l_AnimList.Remove(0);//清除本次播放完的戰(zhàn)斗動(dòng)畫類
}
//所有動(dòng)畫播放完
if(一方全體死亡)
戰(zhàn)斗結(jié)束
else
進(jìn)入下一回合,玩家下達(dá)指令
}
說明:
PlayAnim協(xié)同程序主要是遍歷存放戰(zhàn)斗動(dòng)畫類的list容器,通過遍歷逐個(gè)播放戰(zhàn)斗動(dòng)畫類數(shù)據(jù)所包含的動(dòng)畫,通過類型的判斷,執(zhí)行不同的協(xié)同程序?qū)崿F(xiàn)不同類型動(dòng)畫的播放。
//魔法技能動(dòng)畫(彈射型特效)協(xié)程
IEnumeratorMagicBounce(BATTLE_ANIMbattleAnim)
{
行動(dòng)者(施法者)播放施法動(dòng)作動(dòng)畫;
根據(jù)特效ID動(dòng)態(tài)加載特效預(yù)設(shè);
由特效預(yù)設(shè)動(dòng)態(tài)生成特效effect;
把effect移到到施法者的位置上;
動(dòng)畫計(jì)數(shù)+1;
for(inti=0;i<battleAnim.nTargetCount;i++)//遍歷目標(biāo)
{
獲得battleAnim.nTargetIndex[i]目標(biāo)索引并根據(jù)索引獲取目標(biāo)角色
destPos=目標(biāo)角色的位置
while(effect的位置!=destPos)
{
特效的位置=Vector3.MoveTowards(特效的位置,destPos,400*Time.deltaTime);//即根據(jù)速度移向目標(biāo)位置
yieldreturn0;
}
目標(biāo)播放受傷動(dòng)作動(dòng)畫并顯示扣血
}
遍歷完所有目標(biāo)后,銷毀特效
動(dòng)畫計(jì)數(shù)-1
}
說明:
MagicBounce協(xié)同程序主要是控制特效的移動(dòng),實(shí)現(xiàn)彈射型特效動(dòng)畫的播放。
最終的動(dòng)畫效果為:行動(dòng)者施法,技能特效從行動(dòng)者身上移向第一個(gè)目標(biāo),特效到達(dá)第一個(gè)目標(biāo)的位置后目標(biāo)受傷并扣血,特效繼續(xù)移向下一個(gè)目標(biāo),如此類推,直到最后一個(gè)目標(biāo)后,技能動(dòng)畫結(jié)束,銷毀特效,協(xié)程結(jié)束返回到PlayAnim(),PlayAnim()繼續(xù)遍歷播放下一個(gè)動(dòng)畫。
其他動(dòng)畫類型與上述例子類似,只要讀者理解好協(xié)同程序的執(zhí)行原理和動(dòng)畫計(jì)數(shù)器的作用,相信其他動(dòng)畫類型的實(shí)現(xiàn)也能掌握個(gè)大概,甚至能實(shí)現(xiàn)出更多的技能動(dòng)畫類型。
第6章項(xiàng)目測(cè)試
6.1進(jìn)入戰(zhàn)斗測(cè)試
圖6-1進(jìn)入戰(zhàn)斗
從測(cè)試截圖可以看到,游戲進(jìn)入了戰(zhàn)斗畫面,并且成功初始化了所有角色。
6.2普通攻擊功能測(cè)試
圖6-2攻擊測(cè)試截圖1
圖6-3攻擊測(cè)試截圖2
圖6-4攻擊測(cè)試截圖3
從圖6-2可以看出,行動(dòng)角色正移向目標(biāo),圖6-3為攻擊目標(biāo),圖6-4為攻擊完退回原來(lái)的位置,成功實(shí)現(xiàn)了普通攻擊動(dòng)畫。
6.3使用技能功能測(cè)試
圖6-5使用技能測(cè)試截圖1
從上圖中我們發(fā)現(xiàn)了一個(gè)問題,在技能特效沒有播放完成時(shí),下一個(gè)行動(dòng)單位就已經(jīng)開始行動(dòng)了,因?yàn)槲覀兂绦蜻€沒有判斷特效什么時(shí)候結(jié)束,而且不同的特效動(dòng)畫時(shí)長(zhǎng)不一樣。
解決方案:加入動(dòng)畫計(jì)數(shù),在每一個(gè)單獨(dú)的特效生成時(shí),使動(dòng)畫計(jì)數(shù)+1,在特效自身播放結(jié)束的最后一幀,調(diào)用方法使動(dòng)畫計(jì)數(shù)-1,并且銷毀自身,當(dāng)動(dòng)畫計(jì)數(shù)等于0時(shí),表明這個(gè)單位行動(dòng)涉及到的動(dòng)畫都已經(jīng)播放完,則可以開始下一個(gè)單位的行動(dòng)。
圖6-6使用技能測(cè)試截圖2
加入動(dòng)畫計(jì)數(shù)之后,從上圖可以看出角色正在施法,并且特效正在播放,其他單位沒有進(jìn)行行動(dòng),直到特效動(dòng)畫播放完成,下一個(gè)單位才開始行動(dòng),解決了問題。
6.4使用物品功能測(cè)試
圖6-7使用物品測(cè)試截圖1
圖6-8使用物品測(cè)試截圖2
從圖6-7看出角色使用物品的特效正在播放,并且我方角色Hp數(shù)值較低。在圖6-8中,顯示了恢復(fù)數(shù)值,并且我方全體角色Hp得到了恢復(fù),實(shí)現(xiàn)了使用物品動(dòng)畫。
結(jié)論
經(jīng)過對(duì)Unity以及回合制網(wǎng)游的長(zhǎng)時(shí)間深入的研究分析,并且伴隨著畢業(yè)設(shè)計(jì)以及論文的工作,最終成功達(dá)到目的實(shí)現(xiàn)了包含主要功能的回合制網(wǎng)游戰(zhàn)斗系統(tǒng)。
在客戶端戰(zhàn)斗動(dòng)畫播放的實(shí)現(xiàn)里,采用Unity協(xié)同程序的方案實(shí)現(xiàn)也使本戰(zhàn)斗系統(tǒng)得到創(chuàng)新和突破,能夠?qū)崿F(xiàn)多種不同的特效播放方式,并且相信還能實(shí)現(xiàn)更多華麗特別的特效播放方式,有待繼續(xù)研究。
經(jīng)過多次的測(cè)試驗(yàn)證,沒有出現(xiàn)嚴(yán)重的漏洞或問題,證明本論文的研究和實(shí)現(xiàn)方式是可行的。
來(lái)之不易的成功讓筆者十分激動(dòng),也為筆者對(duì)回合制網(wǎng)游戰(zhàn)斗系統(tǒng)的后續(xù)開發(fā)增加了極大的信心。
由于時(shí)間和個(gè)人能力有限,本論文的研究、分析和實(shí)現(xiàn)難免存在錯(cuò)誤和不足之處,還懇請(qǐng)各位讀者提出意見,讓筆者為其改善或更正。
由于回合制戰(zhàn)斗系統(tǒng)邏輯復(fù)雜,加上本人闡述能力有限,讀者可能需要反復(fù)進(jìn)行閱讀和理解,如有疑問和意見懇請(qǐng)讀者聯(lián)系本人。
希望本文能夠?yàn)樽x者提供幫助或啟發(fā),也希望有更多志同道合熱衷于回合制戰(zhàn)斗系統(tǒng)開發(fā)的朋友加入研究、開發(fā)以及進(jìn)行學(xué)術(shù)交流,為回合制戰(zhàn)斗系統(tǒng)尋求更多創(chuàng)新和突破。
參考文獻(xiàn)
[1]鄭莉,董淵,何江舟編著.C++語(yǔ)言程序設(shè)計(jì)(第四版)[M].北京:清華大學(xué)出版社,2010.398-478.
[2]劉琰,王清賢,劉龍,陳熹編著.Windows網(wǎng)絡(luò)編程[M].北京:機(jī)械工業(yè)出版社,2014.56-221.
[3]A神.網(wǎng)絡(luò)連接:unityNetWork與socket的對(duì)比(一)[Z].
/thread-6966-1-1.html
,2014-11-14
[4]黃祖翔.unity2d-第三課:sprite動(dòng)畫編輯及動(dòng)畫事件[Z]./thread-420420-1-1.html,2014-11-12
[5]ChevyRay.snaker7譯.Unity3D協(xié)程介紹以及使用[Z].[Online]Available:
/huang9012/article/details/38492937
(2014-08-11)
[6]FengYu.UGUI教程-12.事件接口[Z].[Online]Available:/thread-1487-1-1.html(2015-1-25)
[7]thirdpig.關(guān)于對(duì)stl容器的遍歷和刪除[Z].[Online]Available:
/s/blog_6a5b0bad0100nz59.html
(2011-01-12)
[8]秦元培.
Unity3D游戲開發(fā)之SQLite讓數(shù)據(jù)庫(kù)開發(fā)更簡(jiǎn)單
[Z].[Online]Available:
/qinyuanpei/article/details/46812655
(2015-07-09)
[9]宣雨松.Unity3D研究院之在Unity中打開第三方數(shù)據(jù)庫(kù)配合Android開發(fā)(三十二)[Z].[Online]Available:
/archives/1454
(2012-08-09)
[10]
tiaotiaoyly
.
使用MySQLconnector/C++鏈接MySQL數(shù)據(jù)庫(kù)
[Z].[Online]Available:/tiaotiaoyly/article/details/5174772(2010-01-11)
致謝
本論文是在廣州大學(xué)華軟軟件學(xué)院游戲系孟輝老師指導(dǎo)下完成的。
本論文能夠順利完成,首先要感謝我的論文指導(dǎo)老師孟輝老師,感謝孟輝老師愿意當(dāng)我的論文指導(dǎo)老師,從論文選題命題、開題、撰寫與修改直到戰(zhàn)斗系統(tǒng)的實(shí)現(xiàn)和測(cè)試,孟輝老師給予了我全程悉心的指導(dǎo)和幫助。同時(shí),本論文內(nèi)容以及項(xiàng)目《PKer》所涉及的知識(shí)極大部分都來(lái)自孟輝老師所教授的《網(wǎng)絡(luò)游戲基礎(chǔ)》課程,感謝孟輝老師在課程中的細(xì)心講解,以及對(duì)我提出的眾多刨根問底疑問的回答,讓我在socket網(wǎng)絡(luò)編程相關(guān)方面打下了扎實(shí)的基礎(chǔ)。
感謝四年以來(lái)廣州大學(xué)華軟軟件學(xué)院所有給我講授過課程的老師,特別是我系游戲系的老師,是你們的悉心教導(dǎo)讓我日積月累、逐步深入地掌握游戲編程的相關(guān)知識(shí)。
感謝我的母校廣州大學(xué)華軟軟件學(xué)院以及我所在的系游戲系,感謝華軟學(xué)院開設(shè)游戲系,在全國(guó)高校中開辟先河,感謝華軟學(xué)院讓我轉(zhuǎn)入游戲系,感謝游戲系的對(duì)我的悉心培育。感謝華軟感謝游戲系,是你們讓我邁入游戲行業(yè),是你們讓我這個(gè)高考落敗的文科生找到了人生的新方向。
感謝IOCP服務(wù)器框架“HP-Socket”的作者小怪獸,高效穩(wěn)定的服務(wù)器給與我更大的信息,框架使我的研究減少了很多憂患,也是我項(xiàng)目能夠成功的必不可少的前提。
另外,感謝參考文獻(xiàn)里分享博客或文章的各位前輩,你們的實(shí)踐和無(wú)私分享讓我獲益匪淺。
最后,還要感謝我的項(xiàng)目《PKer》的GameFriends團(tuán)隊(duì)所有成員,是你們的無(wú)私付出為我的項(xiàng)目《PKer》提供美術(shù)支持,使《PKer》得到不斷完善和升華。在接下來(lái)為數(shù)不多的在校日子里,我和我的團(tuán)隊(duì),會(huì)繼續(xù)開發(fā)和完善《PKer》項(xiàng)目以及其回合制戰(zhàn)斗系統(tǒng)。
由于
溫馨提示
- 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年臥式離子風(fēng)機(jī)項(xiàng)目資金申請(qǐng)報(bào)告代可行性研究報(bào)告
- 2025年廣西安全工程職業(yè)技術(shù)學(xué)院?jiǎn)握新殬I(yè)技能測(cè)試題庫(kù)帶答案
- 2025屆河北省廊坊市高三上學(xué)期期末英語(yǔ)試題及答案
- 2025年度建筑改造項(xiàng)目承重墻拆除安全責(zé)任免責(zé)協(xié)議
- 2025年度拆除施工與建筑垃圾回收合同
- 2025年度電力設(shè)施安全保證金合同
- 2025年度廢輪胎回收與橡膠顆粒生產(chǎn)合同
- 2025年度房地產(chǎn)銷售代理與服務(wù)合同
- 2025年度醫(yī)療機(jī)構(gòu)與保險(xiǎn)公司合作康復(fù)服務(wù)協(xié)議
- 2025年度工業(yè)廠房轉(zhuǎn)租租賃合同
- 《市場(chǎng)營(yíng)銷學(xué)》講稿
- 2025年中國(guó)陪診服務(wù)行業(yè)現(xiàn)狀、發(fā)展環(huán)境及投資前景分析報(bào)告
- 靜脈治療相關(guān)血管解剖結(jié)構(gòu)與生理
- 統(tǒng)編版九年級(jí)道德與法治上冊(cè)期中考試卷及答案
- 學(xué)校安全隱患報(bào)告和舉報(bào)獎(jiǎng)懲制度
- 福建師范大學(xué)《廣告作品賞析》2022-2023學(xué)年第一學(xué)期期末試卷
- 對(duì)外經(jīng)貿(mào)-會(huì)計(jì)學(xué)原理考試答案
- 消渴病中醫(yī)護(hù)理
- 大學(xué)生職業(yè)素養(yǎng)訓(xùn)練(第六版)課件 第五單元學(xué)會(huì)有效溝通
- 醫(yī)院醫(yī)療項(xiàng)目收費(fèi)管理制度
- 建筑師負(fù)責(zé)制工程建設(shè)項(xiàng)目建筑師標(biāo)準(zhǔn)服務(wù)內(nèi)容與流程
評(píng)論
0/150
提交評(píng)論