学习3 二维游戏动画合成(侠客行)
說明:
<學(xué)習(xí)>系列所有的源代碼均由《計(jì)算機(jī)游戲程序設(shè)計(jì)》提供。本人會(huì)在這些代碼中融入自己的想法,對(duì)其進(jìn)行迭代優(yōu)化,旨在個(gè)人學(xué)習(xí)。
?
教材源代碼示例:
示例代碼的迷之bug賊多,怪物AI也只有一套固定的行動(dòng)模式
優(yōu)化后的效果:
?
學(xué)習(xí)目標(biāo):
1.了解二維游戲動(dòng)畫合成原理。
2.熟悉Cocos2d-x中的用戶交互、觸摸事件、碰撞檢測(cè)機(jī)制。
3.熟悉CocoStudio動(dòng)畫編輯器的使用,了解骨骼動(dòng)畫。
主要修改內(nèi)容:
1.利用cocostudio修改人物骨骼,并將修改結(jié)果在游戲中讀取,從而改變?nèi)宋锿庑?#xff0c;動(dòng)作等,實(shí)現(xiàn)自定義人物骨骼動(dòng)畫效果。
2.增加英雄defend動(dòng)作,記錄成功/失敗次數(shù),增加計(jì)分板功能。
3.修復(fù)bug,使游戲能正常運(yùn)行。
4.怪物AI設(shè)計(jì)。
?
步驟與過程:
1.利用cocostudio修改人物骨骼,并將修改結(jié)果在游戲中讀取,從而改變?nèi)宋锿庑?#xff0c;動(dòng)作等,實(shí)現(xiàn)自定義人物骨骼動(dòng)畫效果。
?
CocoStudio Animation導(dǎo)入新的部位的圖片,如下圖的帽子和斧子:
圖 1
圖 2
?
創(chuàng)建新骨骼并對(duì)圖片進(jìn)行綁定,再綁定到相應(yīng)父骨骼上,得到新的角色外形:
圖 3
之后更新英雄角色每個(gè)動(dòng)作的角色外貌,如下圖奔跑動(dòng)畫:
圖 4
?
添加自制防御動(dòng)畫,如下圖所示:
圖 5
最后給每個(gè)動(dòng)作的最后一幀中的某個(gè)部位層添加一個(gè)“動(dòng)作名_end”的幀事件。如下圖添加攻擊動(dòng)作的幀事件:
圖 6
?
導(dǎo)出文件后替換掉源代碼中英雄文件,運(yùn)行程序,發(fā)現(xiàn)英雄外貌成功改變了:
圖 7
?
2.增加英雄defend動(dòng)作,記錄成功/失敗次數(shù),增加計(jì)分板功能。
?
在cocostudio中已制作了“防御”defend的動(dòng)畫,接下來只要將防御按鈕和動(dòng)作加到游戲中去就行。如下為AnimationScene.cpp文件中按鈕代碼:
圖 9
圖 10
為區(qū)分攻擊和防御鍵,另其分別使用兩張不同的按鈕照片。其中,攻擊按鈕為紅色,防御按鈕為藍(lán)色:
圖 11
枚舉類型中添加了防御DEFEND后,英雄類文件Hero中,簡單模仿攻擊方式的代碼寫一個(gè)防御方式,使點(diǎn)擊防御按鈕后實(shí)現(xiàn)防御功能,如下圖所示:
圖 12
關(guān)于防御功能的代碼及其相關(guān)完善,我將在后文的第5部分再詳細(xì)說明。
?
增加計(jì)分板:
重新新建一個(gè)文件SaveScore (.h和.cpp):
SavaScore.h
圖 13
SavaScore.cpp
圖 14
?
把SaveScore.h頭文件導(dǎo)入AnimationScene.h文件中被其引用。 SaveScore文件的功能為存儲(chǔ)英雄和怪物的比分,heroScore為英雄得分,enemyScore為敵人得分,剛開始都為0。這種做法好處在于,可在AnimationScene文件中直接對(duì)heroScore和enemyScore進(jìn)行操作,在AnimationScene場(chǎng)景被刷新后,計(jì)分值也不會(huì)改變,直接調(diào)用即可。
計(jì)分板代碼:
圖 15
update函數(shù)中不斷調(diào)用judge函數(shù),根據(jù)角色的血量來判斷輸贏,代碼為:
圖 16
圖 17
當(dāng)每輪游戲結(jié)束時(shí)(還沒到最終輸贏),需要刷新當(dāng)前場(chǎng)景,這里用scheduleOnce的方式調(diào)用了restart函數(shù),該函數(shù)里執(zhí)行的代碼可刷新場(chǎng)景。由于一輪游戲結(jié)束后場(chǎng)景不會(huì)被馬上刷新,而是在等待幾秒中后才刷新,所以這里的scheduleOnce對(duì)選擇器選擇的函數(shù)的執(zhí)行是在3秒后。
restart代碼:
圖 18
其中,場(chǎng)景過渡使用了部落格特效,持續(xù)1.2s 。
?
3.云朵移動(dòng)
尋找或自行扣掉一張沒有背景的云朵的png圖片,替換掉資源原有的cloud.png圖片 。
圖 19
AnimationScene.cpp初始化時(shí)創(chuàng)建3朵云,位置設(shè)置為不同,代碼較簡單,不顯示。
再在update函數(shù)中不斷修正其x軸方向位置,每朵云的位移距離不同。當(dāng)云朵移動(dòng)到鏡頭的一端看不見時(shí),在修正其位置到屏幕另一端,這樣就能得到循環(huán)播放的3朵云。
以1號(hào)云朵的位移代碼為例,其余類似:
圖 20
運(yùn)行效果圖:
圖 21
?
4.BUG修正
教材提供的示例代碼有許許多多的bug,主要原因是代碼邏輯寫得不好,這一部分花了大量的時(shí)間去Debug來修正。終于解決了游戲中存在的目前能找到的所有Bug,令游戲能正確且流暢的運(yùn)行起來。
由于游戲bug太多,下面根據(jù)解決方法的不同,總結(jié)為4類Bug:
- 按攻擊鍵除了能控制英雄的攻擊動(dòng)作,也能控制怪物的攻擊動(dòng)作。
- SMITTEN顫抖/硬直動(dòng)作沒有執(zhí)行、ATTACK動(dòng)作的動(dòng)畫可被打斷、角色被擊中后會(huì)原地卡在STAND或RUN動(dòng)作中執(zhí)行不了任何操作 ?等一系列稀奇古怪的動(dòng)作。
- 莫名其妙連續(xù)多次扣血(不是指暴擊),在解決②的bug后,容易發(fā)現(xiàn)當(dāng)角色砍中另一角色時(shí),會(huì)繼續(xù)不斷使用attack。
- 角色在奇怪的地方被砍中,或砍不到角色。
?
下面修正上述bug:
- 攻擊鍵也能控制怪物攻擊。
????注釋掉或刪掉AnimationScene.cpp文件中的attackCallback函數(shù)中的紅框中代碼即可:
圖 22
?
- SMITTEN動(dòng)作不執(zhí)行,被砍后不能操作,attack動(dòng)作被打斷 等奇怪舉動(dòng)的bug。
這個(gè)bug為該程序中最主要的bug,以英雄Hero為例,問題主要出現(xiàn)在下面三個(gè)地方:
圖 23
圖 24
圖 25
圖 26
?
分析bug原因:
可見,1)根據(jù)搖桿的操作情況,調(diào)用2)的Hero中play函數(shù),而3)能檢測(cè)Hero的狀態(tài),執(zhí)行動(dòng)作。
因?yàn)?)在update函數(shù)中;2)被1)調(diào)用;3)是update函數(shù)。 所以理論上1)2)3)都是一直在運(yùn)作中的,沒有固定的先后順序。因此,由于不能確定運(yùn)作順序(除了 2)會(huì)在1)后運(yùn)行),導(dǎo)致程序容易出錯(cuò)。
?
舉例:
當(dāng)hero被攻擊,Hero的play函數(shù)會(huì)被傳入枚舉類型SMITTEN作為參數(shù),根據(jù)play代碼可知,此時(shí)受傷狀態(tài)變量m_ishurt會(huì)變?yōu)閠rue,當(dāng)前角色狀態(tài)m_state會(huì)被賦值為SMITTEN。 ?
理論上來說,下一步該執(zhí)行Hero中的update函數(shù),判斷并執(zhí)行SMITTEN動(dòng)作了才對(duì)。 然而,這里也有可能在執(zhí)行Hero的update函數(shù)之前,先執(zhí)行了AnimationScene的update函數(shù)。
如果先執(zhí)行了AnimationScene的update函數(shù),那么1)的“控制角色移動(dòng)”的代碼段就先被執(zhí)行了,如果此時(shí)沒有動(dòng)搖桿,那么枚舉類型STAND將作為Hero的play函數(shù)的參數(shù)傳進(jìn)去,之后就會(huì)重新賦值給m_state,SMITTEN狀態(tài)就會(huì)被STAND狀態(tài)給覆蓋掉了。
此時(shí),m_state的值為STAND,而m_ishurt的狀態(tài)依然是true(因?yàn)镾MITTEN動(dòng)作沒有被執(zhí)行)。 再進(jìn)入Hero的update函數(shù)時(shí),由于m_ishurt也是各個(gè)動(dòng)作是否該執(zhí)行的判斷依據(jù),當(dāng)m_ishurt == true時(shí),這些動(dòng)作都不會(huì)被執(zhí)行。
因此,當(dāng)hero被攻擊后,hero的操作都將失效。
?
?
根據(jù)上面分析,想要解決這個(gè)bug必須要保證兩個(gè)前提(暫不討論DEFEND防御):
解決上述1)和2):
????1)在Hero.h中聲明新的布爾類型私有變量actionFlag,其作用為 ?當(dāng)m_state被賦值為ATTACK或SMITTEN時(shí),actionFlag被賦值為true,當(dāng)其為true時(shí),m_state不能被再改變,只有在ATTACK和SMITTEN動(dòng)畫運(yùn)行到最后一幀時(shí),actionFlag變?yōu)閒alse,此時(shí)m_state允許被賦值。
修改相關(guān)代碼:
圖 27
圖 28
?
????2)研究源代碼中attack相關(guān)函數(shù),發(fā)現(xiàn)Hero中的m_isAttack的作用為判斷hero是否“正在攻擊”,這里指的是“動(dòng)作”而不是“狀態(tài)”。通過這一變量,在動(dòng)作執(zhí)行時(shí)(動(dòng)畫播放時(shí))才賦值為true,在最后一幀播放完了再賦值為false。把該變量作為動(dòng)作執(zhí)行的判斷依據(jù),能有效地控制并防止動(dòng)作的被打斷以及持續(xù)進(jìn)行(譬如一直點(diǎn)擊攻擊鍵,攻擊動(dòng)作不斷被打斷并重新執(zhí)行,只播放前幾幀);
模仿m_isAttack變量,把m_ishurt變量的意義從原來的“受傷狀態(tài)”更改為“受傷動(dòng)作”。
同時(shí),在監(jiān)聽幀事件的函數(shù)中,要在動(dòng)作執(zhí)行完后加入play(STAND)的代碼,防止其帶著原來的m_state先執(zhí)行Hero的update函數(shù)又引起什么奇怪的操作。
模仿著更改代碼:
圖 29
圖 30
?
????同理修改Enemy的代碼即可。
?
?
- 莫名其妙連續(xù)多次扣血(不是指暴擊),在解決②的bug后,容易發(fā)現(xiàn)當(dāng)角色砍中另一角色時(shí),會(huì)繼續(xù)不斷使用attack。
查看碰撞檢測(cè)文件MyContactListener.cpp,查看其update函數(shù):
圖 31
以敵人攻擊英雄為例,關(guān)鍵代碼部分放大↓:
圖 32
分析:
由上面代碼可知,當(dāng)其他條件滿足的前提下,Enemy的m_isAttack變量為true時(shí),表示此時(shí)敵人正在執(zhí)行攻擊動(dòng)作,if滿足條件,執(zhí)行Hero的hurt函數(shù),hero受傷掉血。然后Enemy執(zhí)行setAttack(false)把其m_isAttack置為false。
然而,當(dāng)m_isAttack置為false后,在Enemy中,會(huì)把其視為攻擊動(dòng)作已經(jīng)結(jié)束,在m_state還是ATTACK時(shí),會(huì)把m_isAttack==false作為再次執(zhí)行攻擊動(dòng)作的判斷依據(jù)。而檢測(cè)碰撞文件的update函數(shù)又會(huì)很快的被再次執(zhí)行,m_enemy->isAttack()又會(huì)被視為true……如此地連續(xù)執(zhí)行,可能會(huì)造成角色的連續(xù)多次掉血,或者角色一旦攻擊到另一角色時(shí),會(huì)不斷地執(zhí)行攻擊動(dòng)作。
?
解決:
通過上述分析,我們了解到,解決問題的關(guān)鍵點(diǎn)在于不能在檢測(cè)碰撞中執(zhí)行m_enemy->setAttack(false)來改變破壞Enemy的攻擊動(dòng)作。
綜上,我們保留其思想,但是不改變m_isAttack的值,為角色引入一個(gè)新的私有變量attackHurtFlag,表示被攻擊傷到傷害的標(biāo)志,增加set和get方法。以enemy攻擊hero為例,關(guān)鍵代碼為:
圖 33
???
?Hero的update函數(shù)中:
圖 34
?
- 角色在奇怪的地方被砍中,或砍不到角色。
????觀察碰撞檢測(cè)文件MyContactListener.cpp,查看其update函數(shù)中enemy攻擊hero部分:
圖 35
分析:
發(fā)現(xiàn)其碰撞檢測(cè)的基本原理為:為enemy的ax層(即enemy的斧子部件)添加2個(gè)檢測(cè)點(diǎn),再根據(jù)hero的位置創(chuàng)建一個(gè)矩形。當(dāng)enemy為攻擊狀態(tài),并且其斧子的2個(gè)檢測(cè)點(diǎn)在hero的矩形范圍內(nèi)時(shí),即為實(shí)現(xiàn)碰撞。
因此,這里該如何創(chuàng)建矩形成為關(guān)鍵。
分析Rect方法的參數(shù),其第1個(gè)參數(shù)為矩形左下角的x坐標(biāo),第2個(gè)參數(shù)為矩形左下角的y坐標(biāo),第3個(gè)參數(shù)為矩形的寬,第4個(gè)參數(shù)為矩形的高。
結(jié)合游戲運(yùn)行圖來分析:
假設(shè)在使用cocoStudio Animation時(shí),角色的中心點(diǎn)在身體的中心點(diǎn),隨意創(chuàng)建一個(gè)矩形,則有:
圖 36
假設(shè)還是同一程序,當(dāng)hero轉(zhuǎn)身后,其矩形不會(huì)根據(jù)角色的轉(zhuǎn)身而左右顛倒,如下圖所示:
圖 37
由上面兩張圖可知道,創(chuàng)建矩形時(shí),寬(即x軸)的中間位置的x坐標(biāo)最好落在角色中心點(diǎn)的x坐標(biāo)上。只有這樣,hero無論轉(zhuǎn)身與否,其前后的被攻擊的判定范圍都是一樣的,這樣才不會(huì)出現(xiàn)奇怪的“有時(shí)能砍到,有時(shí)又砍不到”的奇怪現(xiàn)象。
最后只要不斷調(diào)整矩形的寬度即可(即調(diào)整第1個(gè)參數(shù)和第3個(gè)參數(shù))。而矩形的高度只要足以涵蓋住角色即可(即調(diào)整第2個(gè)參數(shù)和第4個(gè)參數(shù))。
?
最終矩形參數(shù)修改為:
圖 38
圖 39
?
5.防御機(jī)制
防御機(jī)制設(shè)定為:
- 點(diǎn)擊藍(lán)色按鈕進(jìn)入防御狀態(tài)
- 防御狀態(tài)下,角色最后會(huì)保持防御動(dòng)畫的最后一幀
- 防御狀態(tài)下能減少一段暴擊及暴擊傷害,受到的傷害值以藍(lán)色數(shù)
值顯示
- 防御狀態(tài)下,操作搖桿,點(diǎn)擊攻擊按鈕,能打斷防御狀態(tài),并執(zhí)行其他相應(yīng)動(dòng)作
- 防御狀態(tài)下,再點(diǎn)擊一次防御按鈕可以取消防御狀態(tài)
?
根據(jù)以上設(shè)定,編寫代碼:
Hero中的update:
圖 40
并為每個(gè)動(dòng)作的執(zhí)行加上m_isDefend=false,以STAND站立動(dòng)作為例:
圖 41
Hero的showBloodTips函數(shù) “減少暴擊數(shù)和暴擊傷害”以及“防御狀態(tài)下傷害值為藍(lán)色”:
圖 42
圖 43
運(yùn)行圖:
圖 44
6.AI設(shè)計(jì)
觀察AI文件AIManager的原代碼,發(fā)現(xiàn)怪物AI僅僅是根據(jù)一套固有的動(dòng)作反復(fù)執(zhí)行而已,關(guān)鍵代碼為:
圖 45
分析上面代碼,可知這個(gè)AI并不智能。并且根據(jù)上面的執(zhí)行結(jié)果,可知moveLeft的動(dòng)作持續(xù)最久,因此游戲中的后半段,敵人emeny會(huì)一直往左邊界“推墻”,moveRight的持續(xù)時(shí)間太短,因此無法往右半邊回來。
?
因此,重新編寫一個(gè)AI代碼文件,使其能夠根據(jù)hero的位置,實(shí)現(xiàn)自動(dòng)跟蹤,在適宜的位置進(jìn)行攻擊的功能。
編寫后的關(guān)鍵代碼為(以hero在enemy的左方為例):
圖 46
hero在enemy的右方時(shí)也同理可得,而當(dāng)hero和enemy位置相同時(shí),enemy直接攻擊即可。
?
由上述得到了敵人AI的最佳方案,但是如果直接把bestAI函數(shù)放到update函數(shù)不斷調(diào)用的話,會(huì)發(fā)現(xiàn)游戲會(huì)變得非常困難,幾乎沒有贏的可能性。并且,敵人enemy的行動(dòng)模式不夠隨機(jī)也反而顯得不是那么的“智能”。因此,在此基礎(chǔ)上減少bestAI的執(zhí)行次數(shù),插入隨機(jī)行動(dòng)模式,并適當(dāng)?shù)販p少攻擊頻率,讓游戲變得更簡單,令A(yù)I變得更隨機(jī)些。
修改后的代碼為:
圖 47
bestAI()中以hero在enemy的左方為例:
圖 48
AI的隨機(jī)方案:
圖 49
通過上述操作后,敵人AI能夠保持在最佳行動(dòng)方案的基礎(chǔ)上,也進(jìn)行些許隨機(jī)行動(dòng)了。
7.其余Label顯示細(xì)節(jié)
???① 每輪游戲開始時(shí)都會(huì)出現(xiàn)“Round X”,X表示游戲的第幾輪,1.5s后消失(移除)
圖 50
???② 每一小輪游戲結(jié)束后,都會(huì)在對(duì)應(yīng)角色的血條下方顯示“win”字樣
圖 51
③ 場(chǎng)景有部落格特效過渡
④ 游戲結(jié)束后會(huì)顯示玩家的輸贏,贏了顯示“YOU WIN!”;輸了顯示“YOU LOSE!”
圖 52
總結(jié)
以上是生活随笔為你收集整理的学习3 二维游戏动画合成(侠客行)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux移动文件 rf参数_linux
- 下一篇: Concurrent HTTP conn