
一個(gè)開(kāi)發(fā)者的點(diǎn)滴積累
使用工具:UE4 (4.20.3)
使用資源:Dungeon_Areas(付費(fèi))、ParagonShinbi(免費(fèi))、ParagonSunWukong(免費(fèi))
開(kāi)發(fā)耗時(shí):3756分鐘
運(yùn)行流程
打開(kāi)游戲,先進(jìn)入游戲開(kāi)始界面,界面上有兩個(gè)按鈕:游戲開(kāi)始和退出游戲。
點(diǎn)擊游戲開(kāi)始,加載場(chǎng)景,場(chǎng)景中有兩個(gè)英雄,我的界面上有3張牌,以及回合結(jié)束按鈕。
選中牌,然后移動(dòng)到戰(zhàn)場(chǎng)中,召喚一個(gè)隨從。拖拽自己的英雄到對(duì)方的英雄身上,自己的英雄會(huì)走過(guò)去,攻擊,然后退回來(lái)。
點(diǎn)擊回合結(jié)束按鈕,然后敵方英雄進(jìn)行攻擊。攻擊的時(shí)候顯示血量減少。然后回合結(jié)束。
再由我進(jìn)行攻擊,然后游戲結(jié)束,顯示結(jié)束界面。然后退回到開(kāi)始界面。
目前開(kāi)發(fā)完成的功能是:
拖拽自己的英雄到對(duì)方英雄身上,自己的英雄走過(guò)去,攻擊,然后退回來(lái)。
開(kāi)發(fā)過(guò)程與反思
游戲模式
原本沒(méi)有在意游戲模式,因?yàn)閹缀跛械慕坛潭贾粫?huì)告訴你游戲模式設(shè)置成什么樣子,至于游戲模式是用來(lái)做什么的都不會(huì)提。我也是在機(jī)緣巧合之下才會(huì)仔細(xì)地閱讀了一遍游戲模式的文檔,雖然沒(méi)有解決我遇到的問(wèn)題,但是也讓我對(duì)游戲模式和游戲狀態(tài)有了一個(gè)很好的了解,這一節(jié)就是來(lái)介紹游戲模式的。
先來(lái)看看UE4文檔中時(shí)如何介紹游戲模式的:
即便是最開(kāi)放的游戲也有一些基礎(chǔ)規(guī)則,這些規(guī)則在UE4中被稱為游戲模式(Game Mode)。對(duì)大多數(shù)關(guān)卡來(lái)說(shuō),基礎(chǔ)規(guī)則包括:
- 當(dāng)前玩家和旁觀者數(shù)量,以及允許的最大玩家數(shù)和旁觀者數(shù)。
- 玩家如何進(jìn)入游戲,這包括選擇出生點(diǎn)的規(guī)則,以及其他出生/重生行為的規(guī)則。
- 游戲能否被暫停,如果可以被暫停,那么如何處理暫停。
- 關(guān)卡之間如何切換,游戲是否要從電影模式啟動(dòng)。
你可以看到,這些規(guī)則真的很基礎(chǔ),基礎(chǔ)到基本不用理會(huì)的地步,事實(shí)上,我們真的不需要花太多時(shí)間在上面。
大多數(shù)情況下,我們的游戲需要一個(gè)自定義的Game Mode。而這個(gè)Game Mode通常是一個(gè)繼承自Game Mode的藍(lán)圖類。

這是我們可以重新定義類的一部分,這個(gè)項(xiàng)目里,只需要重新定義兩個(gè)類就行了:Player Controller Class和Default Pawn Class。沒(méi)錯(cuò),就是后面有黃色返回箭頭的那兩個(gè)。UE4真是太人性化了!
Player Controller是用來(lái)定義如何操作的類。UE4默認(rèn)不會(huì)顯示鼠標(biāo),要顯示的話就需要定義一個(gè)Player Controller類,然后將鼠標(biāo)控制的相關(guān)操作打開(kāi)。不過(guò)說(shuō)實(shí)話,對(duì)Game Mode,Player Controller和Default Pawn三個(gè)東西還是自己重新定義來(lái)的心安。

最后是character。這里的character并沒(méi)有實(shí)際的意義,游戲本身是一個(gè)策略游戲,一個(gè)上帝視角的游戲,不是只操作一個(gè)character的游戲。所以,這里的character是一個(gè)空類,占個(gè)位罷了。
觀察視角
官方文檔中指出:如果你的游戲中沒(méi)有一個(gè)Player Start(玩家起始)對(duì)象,那么玩家會(huì)從坐標(biāo)(0,0,0)的位置開(kāi)始。所以,不管需不需要,最好還是在地圖中放一個(gè)Player Start對(duì)象。
不過(guò)我在這個(gè)項(xiàng)目里就沒(méi)有放,因?yàn)槲矣X(jué)得不需要的東西就不用存在。
游戲需要一個(gè)45度俯視的觀察角度,所以在場(chǎng)景中加了一個(gè)CameraActor。有兩種方法設(shè)置這個(gè)視角,第一種方法是調(diào)用Set View Target with Blend函數(shù),將Camera Actor啟用。這種方法的特點(diǎn)是非常靈活,如果你需要切換多個(gè)攝像機(jī)的話可以使用。第二種方法是直接在CameraActor的細(xì)節(jié)面板上把Auto Activate for Player設(shè)置成Player 0,就跟下面的圖片一樣:

這種方法就是簡(jiǎn)單,如果不需要切換攝像機(jī),那么這種方法是最合適的。顯然,我采用的就是這種方法。
邏輯控制
如何讓角色響應(yīng)鼠標(biāo)的控制呢?
首先需要明確,有多少的鼠標(biāo)消息要響應(yīng)。滿打滿算只有四個(gè)消息:懸停、不懸停、按下、釋放。鼠標(biāo)移動(dòng)到角色上時(shí),需要明確地提示玩家這個(gè)角色是可以操作的,所以,鼠標(biāo)的樣式需要改變。當(dāng)玩家按下左鍵,鼠標(biāo)樣式也要改變,示意玩家已經(jīng)抓住了這個(gè)角色。當(dāng)玩家抓住這個(gè)角色的時(shí)候,將鼠標(biāo)移動(dòng)到其他地方,角色的朝向就會(huì)改變。此時(shí),松開(kāi)鼠標(biāo),角色就會(huì)移動(dòng)過(guò)去進(jìn)行攻擊。
兩個(gè)關(guān)鍵點(diǎn):1、如何判定已經(jīng)抓住角色?2、如何計(jì)算角色的朝向?
1、如何判定已經(jīng)抓住角色?
鼠標(biāo)能抓住角色只有角色在原點(diǎn)的時(shí)候才有效。所以,角色有一個(gè)初始狀態(tài),Idle(一開(kāi)始的時(shí)候用一個(gè)bool變量IsAtOrigin來(lái)判斷角色是否處于原點(diǎn),隨著玩家的狀態(tài)越來(lái)越多,發(fā)現(xiàn)這種方式非常不適合,于是改成用玩家的狀態(tài)來(lái)區(qū)分,簡(jiǎn)潔大方)。鼠標(biāo)必須在角色上(通過(guò)IsOnCharacter來(lái)判斷),然后按下左鍵才能算是抓?。▽⑦@種狀態(tài)保存到變量IsGrabbed中)。具體的邏輯如下圖所示:

2、如何計(jì)算角色的朝向?
要計(jì)算朝向,需要一個(gè)前置條件,那就是能否觸發(fā)。觸發(fā)的條件有兩個(gè),其一:角色必須被抓??;其二:鼠標(biāo)移動(dòng)的距離必須大于一定數(shù)值(這里是200)。兩個(gè)條件都滿足后,觸發(fā)這種狀態(tài)就會(huì)被保存到IsTroggleOn變量中。

如果能觸發(fā),還需要計(jì)算角色的旋轉(zhuǎn)角度。方法是這樣,先在鼠標(biāo)位置與角色位置之間拉一個(gè)向量(鼠標(biāo)位置-角色位置),將這個(gè)向量標(biāo)準(zhǔn)化后,計(jì)算與角色朝向向量之間的點(diǎn)積,這個(gè)點(diǎn)積就是旋轉(zhuǎn)角度的cos值。然后通過(guò)acosd函數(shù)將其轉(zhuǎn)換成角度,特別要注意的是,輸出的角度需要有正負(fù),用來(lái)區(qū)分角色是從當(dāng)前朝向往左轉(zhuǎn)(加負(fù)值)還是往右轉(zhuǎn)(加正值):

算完角度后,更新角色的朝向就簡(jiǎn)單了,只需在原來(lái)的旋轉(zhuǎn)角度值上加上計(jì)算出的角度,像這樣:

整個(gè)刷新流程就是這樣:

角色動(dòng)畫(huà)
對(duì)于角色動(dòng)畫(huà)的實(shí)現(xiàn),真的是走了很多彎路。我是一個(gè)做棋牌游戲開(kāi)發(fā)的程序員,腦子里的思維模式是觸發(fā)式思維。
觸發(fā)式思維就是如果要產(chǎn)生什么動(dòng)作,必須有觸發(fā)這個(gè)動(dòng)作的一次調(diào)用
但是3D游戲不一樣,3D游戲的場(chǎng)景永遠(yuǎn)處于刷新之中,所以角色的動(dòng)畫(huà)應(yīng)該是輪詢式(每次刷新時(shí)都獲取當(dāng)前的狀態(tài),來(lái)決定需要繪制成什么樣)。所以一開(kāi)始我做的時(shí)候?qū)?dòng)畫(huà)的控制都是放在關(guān)卡藍(lán)圖里,然后直接設(shè)置Animation Blueprint。這種方式如果場(chǎng)景中只有一個(gè)角色還能應(yīng)付,多兩個(gè)就應(yīng)付不來(lái)了。要意識(shí)到這兩種模式之間的區(qū)別真不是一件容易的事情,我的運(yùn)氣真是太好了。
播放角色動(dòng)畫(huà)有兩個(gè)關(guān)鍵點(diǎn),一是如何切換角色的動(dòng)畫(huà),二是如何更新角色的位置。
如何切換角色動(dòng)畫(huà)?
官方推薦做法:使用Animation Blueprint來(lái)控制動(dòng)畫(huà)。方式是從新建目錄中定位到動(dòng)畫(huà)->動(dòng)畫(huà)藍(lán)圖,然后在彈出的窗口中選好目標(biāo)骨架,這樣就創(chuàng)建好了一個(gè)動(dòng)畫(huà)藍(lán)圖(Animation Blueprint)。
在動(dòng)畫(huà)藍(lán)圖中,最適當(dāng)?shù)姆绞綉?yīng)該就是創(chuàng)建一個(gè)狀態(tài)機(jī),讓動(dòng)畫(huà)藍(lán)圖根據(jù)當(dāng)前的狀態(tài)來(lái)播放適當(dāng)?shù)膭?dòng)畫(huà)。角色有六種狀態(tài),分別是:待機(jī)、前進(jìn)、停止前進(jìn)、攻擊、后退、停止后退。狀態(tài)切換的順序也是按照這個(gè)順序,也就是說(shuō),角色只能順序切換這些狀態(tài),不能跳狀態(tài)切換(例如從待機(jī)到攻擊)。在狀態(tài)機(jī)中的表現(xiàn)就是這樣:

切換的條件分別是:
- Idle -> Moving:角色狀態(tài)變成MovingFwd(前進(jìn))
- Moving -> Stop:角色狀態(tài)變成FwdStop(停止前進(jìn))
- Stop -> Attacking:前一個(gè)動(dòng)畫(huà)剩余時(shí)間<=2.5秒
- Attacking -> MovingBack:前一個(gè)動(dòng)畫(huà)剩余時(shí)間<=0.5秒
- MovingBack -> BackStop:角色狀態(tài)變成BwdStop(停止后退)
- BackStop -> Idle:前一個(gè)動(dòng)畫(huà)剩余時(shí)間<=2.5秒
因?yàn)閺耐O碌焦舻椒祷貢r(shí)一套完整的流程(停止后退到待機(jī)狀態(tài)也是這樣),所以不需要根據(jù)狀態(tài)改變來(lái)切換,動(dòng)畫(huà)放到一定程度就可以到下一階段了,而且這時(shí)候需要將動(dòng)畫(huà)的狀態(tài)同步給角色,因此還需要添加一些動(dòng)畫(huà)事件。

動(dòng)畫(huà)事件有3個(gè):進(jìn)入攻擊狀態(tài)、進(jìn)入返回狀態(tài)、進(jìn)入待機(jī)狀態(tài)。這些事件可以在狀態(tài)上添加,也可以在狀態(tài)之間的切換操作上添加。下圖就是在Attacking上添加的事件:

動(dòng)畫(huà)狀態(tài)的切換需要讓角色知道,動(dòng)畫(huà)藍(lán)圖也需要從角色藍(lán)圖中獲取狀態(tài)以便選取適當(dāng)?shù)膭?dòng)畫(huà)。前者可以通過(guò)定義一個(gè)事件調(diào)度器(ShinbiAnimationsStatus)然后調(diào)用來(lái)實(shí)現(xiàn),后者可以在每一幀都會(huì)調(diào)用的Update事件里去獲取角色狀態(tài)。

如何更新角色位置?
角色位置更新首先需要在前進(jìn)或者后退狀態(tài)才會(huì)有效。通過(guò)計(jì)算原點(diǎn)和目標(biāo)點(diǎn)之間的向量,標(biāo)準(zhǔn)化之后乘上一個(gè)速度,再加到當(dāng)前位置上就非常容易實(shí)現(xiàn):

前進(jìn)與后退的區(qū)別也就是向量的指向不同了而已。
設(shè)置角色的位置,調(diào)用SetActorLocation即可,另外,當(dāng)玩家到目標(biāo)位置或者玩家回到原點(diǎn)之后,需要更新一下玩家的狀態(tài),這個(gè)操作由Calculate Character Status函數(shù)來(lái)完成。

項(xiàng)目管理
U盤(pán)。資源實(shí)在太大了,沒(méi)法上傳github,甚至連碼云都沒(méi)法上傳(碼云單個(gè)文件限制大小為100M)。
當(dāng)前進(jìn)展
目前要做的就是角色之間的碰撞(collision)響應(yīng)。只是我對(duì)碰撞的了解甚少,所以需要仔細(xì)的閱讀碰撞相關(guān)文檔,要完成相關(guān)的碰撞功能,時(shí)間估計(jì)不會(huì)低于500分鐘。
總結(jié)
想要盡快地實(shí)現(xiàn)功能,直接參考已有的項(xiàng)目并不是最快的方法(如果沒(méi)人解釋為什么要這么做的話),最快的方式還是老老實(shí)實(shí)地閱讀文檔,對(duì)整個(gè)功能有一個(gè)完整的理解。