Unity——#14 一段攻擊


??這節(jié)打算新增一段攻擊動(dòng)畫,但一般一段攻擊是不夠的,完整的一套平A應(yīng)該要有三段或四段攻擊,在以后我打算做三段攻擊,這節(jié)先看看一段攻擊如何實(shí)現(xiàn)。



??我認(rèn)為現(xiàn)在這一層的動(dòng)畫狀態(tài)機(jī)已經(jīng)完整了,一些角色的基本動(dòng)作例如走、跑、跳、翻滾都有了,不應(yīng)該再往上添加動(dòng)畫了。所以我想把多段攻擊放在一層新的狀態(tài)機(jī)上,然后實(shí)現(xiàn)兩者同時(shí)存在且互不干擾,說起來簡單但做起來就不一定了。OK, Let's do it.
??首先就要新增一層Layer,在Animator的Layer里點(diǎn)選+號(hào),新增一層Layer并命名為attack:


attack

??新增之后第一件事并不是直接加動(dòng)畫哦,而是應(yīng)該點(diǎn)開attack右邊的小齒輪:

??里面的幾個(gè)屬性我來解釋一下:
  • Weight 這個(gè)代表當(dāng)前Layer的權(quán)重值,就是如果幾個(gè)Layer同時(shí)存在的話,Animator會(huì)判斷當(dāng)前誰的Layer的權(quán)重值最大就去執(zhí)行那個(gè)Layer的狀態(tài)機(jī),如果多個(gè)Layer的權(quán)重值都是最大(最大為1),那么都執(zhí)行。第一層Layer(Base Layer)的權(quán)重值一直最大且不能修改,這是為了保持至少有一個(gè)Layer得到執(zhí)行(我是這么認(rèn)為的)。新增Layer的權(quán)重默認(rèn)為0。
  • Mask 這里它接受一個(gè)Avatar Mask,何為Avatar Mask?Avatar Mask就是一個(gè)動(dòng)畫遮罩圖層,通過選定人形模型身體上某些部位,這些部位會(huì)在某個(gè)Layer的動(dòng)畫執(zhí)行時(shí)覆蓋其他Layer的動(dòng)畫。這么說可能說得不太明白哦,我們可以新建一個(gè)Avatar Mask來看看其中的究竟。在Project視圖點(diǎn)擊右鍵create→Avatar Mask(我命名為fullbody):

    ?在它的Inspector視窗里能看到Humanoid選項(xiàng),點(diǎn)開它:
    Avatar Mask

    ?能看到一個(gè)人形的大致關(guān)節(jié)圖,可以通過點(diǎn)擊這些關(guān)節(jié),來選定哪些關(guān)節(jié)是你想在動(dòng)畫里面發(fā)生運(yùn)動(dòng)的,例如如果我只選中右手:

    ?那么使用這個(gè)Avatar Mask的Layer它里面的動(dòng)畫播放時(shí)將只有右手有動(dòng)作(即使動(dòng)畫本身是整個(gè)身體都動(dòng)起來),其余部分不為所動(dòng)。
  • Blending 這個(gè)是Avatar Mask的混合方式,有additive和override兩個(gè)選擇,分別是附加和重載的意思,重載就是我上面說的那樣(只有右手活動(dòng)),而附加就是跟其他Layer一起播放。
  • sync 同步層,就是讓該層復(fù)刻其他層的動(dòng)畫,其他層可以從Source Layer 選擇。這個(gè)不多做解釋,因?yàn)檫@節(jié)用不上。
  • IK Pass 這個(gè)是開啟IK通道,讓OnAnimatorIK()函數(shù)調(diào)用起來,如果不勾選這個(gè)選項(xiàng)即使你的代碼里有OnAnimatorIK()這個(gè)方法,也不會(huì)被調(diào)用。而這個(gè)方法的作用是在運(yùn)行時(shí)改變動(dòng)畫模型本身的一些參數(shù)。在日后講述防御的時(shí)候會(huì)用到。
    ??那么,由于現(xiàn)在還沒有代碼實(shí)現(xiàn)這個(gè)權(quán)重的變化,所以我們可以先把它拉滿,主要是為了測(cè)試接下來添加的動(dòng)畫。另外,還要給它一個(gè)Avatar Mask,不過剛才在示范時(shí)我已經(jīng)創(chuàng)建了一個(gè)命名為fullbody的Avatar Mask,所以可以直接套用給它,在給予了Avatar Mask后,該Layer的小齒輪左邊會(huì)有一個(gè)M的符號(hào):

    ??然后就可以加動(dòng)畫了,首先先要添加一個(gè)閑置狀態(tài)的動(dòng)畫(idle),這是必須的,因?yàn)椴豢赡苓@Layer一開始就是一段攻擊,那么就一直攻擊出不去了。然后在添加了一個(gè)一段攻擊的動(dòng)畫,命名為attack1hA,意思是單手攻擊(1h)的第一段攻擊(A)。這兩者先拉Transition,如圖:

    ??先說簡單的attack1hA→idle,這個(gè)沒什么好說的,當(dāng)前觸發(fā)這個(gè)轉(zhuǎn)換的條件就是當(dāng)attack1hA播放完畢后自動(dòng)轉(zhuǎn)回idle。
    ??然后到idle→attack1hA,這個(gè)就有點(diǎn)難度了,首先考慮Has Exit Time,這個(gè)消勾,應(yīng)該不用解釋為什么了:

    ??其次來考慮觸發(fā)這個(gè)轉(zhuǎn)換的條件,肯定需要一個(gè)Trigger參數(shù),像實(shí)現(xiàn)跳躍和翻滾那樣,那么就給它整一個(gè),命名為attack:

    ??然后在這個(gè)轉(zhuǎn)換的Conditions里新增這個(gè)attack:

    ??這時(shí)就該考慮用代碼把輸入和信號(hào)連接起來了:
    ??在PlayerInput.cs里,宣告一個(gè)bool變量命名為attack,宣告一個(gè)string變量記錄觸發(fā)attack的keyname。
    public string keyC;
    public bool attack;
    void Upate(){
        ...
        attack = Input.GetKeyDown(keyC);
    }

??對(duì)于keyC我設(shè)置為j,對(duì)于該用什么按鍵攻擊并沒仔細(xì)考慮,現(xiàn)在先測(cè)試這個(gè)Trigger能不能正常使用要緊。



??然后在ActorController.cs里將這個(gè)bool變量的attack與Trigger attack聯(lián)系起來:

    void Update(){
        ...
        if (pi.attack) {
            anim.SetTrigger ("attack");
        }
    }

??現(xiàn)在先來看看效果哦:


??噢上帝這效果簡直不堪入目,一堆bug,我們來看看現(xiàn)在究竟都出些啥問題:
1.完全沒有了走路的動(dòng)畫,整個(gè)模型就在滑行
2.按空格沒有任何反饋,既不能跳又不能翻滾
3.攻擊隨意轉(zhuǎn)向
??遇到這么多問題哦肯定會(huì)慌張,感覺無從下手,都是像我這些小白常有的心態(tài)。先別急,回想一下是不是哪個(gè)環(huán)節(jié)出了問題,我沉思了一下,發(fā)現(xiàn):

??可能就是這兩個(gè)玩意搞得我一地雞毛,重載嘛,全身嘛,然后在attack這個(gè)Layer的idle動(dòng)畫直接覆蓋Base Layer的所有動(dòng)畫了,所以Base Layer的所有動(dòng)畫突然都不奏效了。那這肯定不是我想要的,怎么解決?我現(xiàn)在需要一個(gè)動(dòng)態(tài)調(diào)節(jié)attack這個(gè)Layer的權(quán)重值的方法,只要權(quán)重值為0那么這兩個(gè)玩意就不會(huì)奏效了,在我沒有鍵入j鍵時(shí),attack的權(quán)重值為0,此時(shí)就能正常播放Base Layer里的所有動(dòng)畫;在我鍵入j后,進(jìn)入到attack的Layer里,此時(shí)將不能播放Base Layer里的動(dòng)畫,不過在攻擊時(shí)不能跑跳滾也是合理的。這個(gè)該怎么實(shí)現(xiàn)呢?
??此時(shí)我們應(yīng)該了解關(guān)于狀態(tài)機(jī)的一個(gè)特點(diǎn),這有助于我們?nèi)?shí)現(xiàn)這個(gè)動(dòng)態(tài)調(diào)節(jié)的方法。就是即使某層的的權(quán)重值為0,但它在狀態(tài)機(jī)里依舊會(huì)運(yùn)行,只是不會(huì)表現(xiàn)在角色的動(dòng)畫里,這個(gè)是我鍵入一次j的效果:

??那么我們就想,能不能這樣做:當(dāng)我們鍵入j,在進(jìn)入到attack1hA的動(dòng)畫時(shí),把a(bǔ)ttack這個(gè)Layer的權(quán)重值拉滿,然后在attack1hA播放完畢,回到idle時(shí),把權(quán)重值拉回0,感覺可行是不是?還能順便把輸入給鎖死了。事不宜遲,馬上動(dòng)身!
??先在attack1hA和idle身上add Behaviour,就是FSMCallOnEnter,老招了,現(xiàn)在應(yīng)該駕輕就熟了:


??至于怎么調(diào)節(jié)權(quán)重值,Animator給了一個(gè)方法就是Animator.SetLayerWeight(),它就只有一個(gè)Signature,那就是:
public void SetLayerWeight(int layerIndex, float weight);
??參數(shù)不難理解:

Parameters Mean
layerIndex The layer index.
weight The new layer weight.

??要注意的是,它接的是Layer的索引值,并不是Layer的名字,對(duì)于Animator的Layers,它是按索引值區(qū)分的,而這個(gè)索引值類似于數(shù)組那般,從0開始。



??像這樣Base Layer的索引值就是0,attack的索引值就是1,以此類推。
??而這個(gè)函數(shù)的官方描述呢也是通俗易懂:

Description
Sets the weight of the layer at the given index.

??前面提到,Animator.SetLayerWeight()的第一個(gè)參數(shù)要求的是Layer的索引值,那么直接喂給它數(shù)字1是不好的,萬一以后attack的位置改變了,這種寫死的行為就會(huì)帶來災(zāi)難。所以還是把這個(gè)寫活好,用Layer的名字就最好,畢竟名字會(huì)改的情況不多,那么如何通過Layer的名字獲得它當(dāng)前的索引值呢,那就要用到Animator的另外一個(gè)函數(shù)Animator.GetLayerIndex(),這個(gè)函數(shù)也不難理解:
public int GetLayerIndex(string layerName);給它layerName,就返還指定layerName的索引值。
??至此兩個(gè)要用到的函數(shù)就緒,可以動(dòng)手:

    void OnAttackIdleEnter()
    {
        pi.InputEnabled = true;
        anim.SetLayerWeight (anim.GetLayerIndex ("attack"), 0);
    }

    void OnAttack1hAEnter()
    {
        pi.InputEnabled = false;
        anim.SetLayerWeight (anim.GetLayerIndex ("attack"),1.0f);   
    }

??現(xiàn)在我想要做就實(shí)現(xiàn)了,可以看看效果:



??現(xiàn)在恢復(fù)正常了,可以走路了,而且平A時(shí)也不能轉(zhuǎn)向了,但動(dòng)作有點(diǎn)生硬,原因就是沒給到它一個(gè)向前的沖量,而且現(xiàn)在attack這個(gè)Layer的權(quán)重值缺少了平滑處理,不是0就是1,導(dǎo)致兩個(gè)Layer之間的切換太過突兀:



??可以看到這個(gè)藍(lán)條,不是滿就是空的,要對(duì)其進(jìn)行平滑處理,做到慢慢拉滿的效果。
??除了這些問題,其實(shí)還有一些很嚴(yán)重的問題,那就是現(xiàn)在我能進(jìn)行跳劈加翻滾這種逆天操作,而且還有在奔跑時(shí)鍵入攻擊鍵之后鍵入跳躍鍵,會(huì)在平A完接一個(gè)翻滾。這其實(shí)都是jump和attack兩個(gè)Trigger的沖突問題,這么多問題,這節(jié)應(yīng)該是講不完了,只能留到下節(jié)在逐一擊破。共勉!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容