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