Unity——#06 下落


??角色在高處往下跳亦或是走出高處的邊緣時(shí)都應(yīng)該表現(xiàn)出下落的動(dòng)畫,就目前而言,角色下落時(shí)只會(huì)播放閑置狀態(tài)(idle)的動(dòng)畫,所以應(yīng)該給它添加一個(gè)下落動(dòng)畫,如圖:


fall

??現(xiàn)在要想的就是,fall動(dòng)畫與其他動(dòng)畫的轉(zhuǎn)換(Transition),應(yīng)該要有:
1.jump到fall的轉(zhuǎn)換,從高處按跳躍鍵往低處跳時(shí)觸發(fā)這個(gè)轉(zhuǎn)換
2.ground到fall的轉(zhuǎn)換,在高處沒跳躍但直接走/跑到高處的邊緣時(shí)(高低差很大)觸發(fā)
3.fall到ground的轉(zhuǎn)換,下落到地面時(shí)觸發(fā)這個(gè)轉(zhuǎn)換。
這里要注意區(qū)分好jump到ground和fall到ground兩個(gè)轉(zhuǎn)換的觸發(fā)條件,如果跳的不高就不用播放fall了,直接轉(zhuǎn)回到ground,不過這是后話。
??一步一步來,逐漸實(shí)現(xiàn)各個(gè)轉(zhuǎn)換,看上去只有3大步,其實(shí)里面有很多問題需要考慮。

1.jump到fall的轉(zhuǎn)換

??二話不說,先給它兩者拉個(gè)Transition,方向自然就是從jump到fall:
jump→fall

??那么遇到的第一個(gè)問題就是,該怎么觸發(fā)這個(gè)轉(zhuǎn)換呢?我們應(yīng)該在一個(gè)時(shí)間點(diǎn)設(shè)一個(gè)判斷,在jump的動(dòng)畫播放完后,要準(zhǔn)備進(jìn)行轉(zhuǎn)換,此時(shí)jump動(dòng)畫由兩條路可以選,一個(gè)是fall,一個(gè)是ground,那決定它的去向自然就是角色此刻是否在地上,這個(gè)時(shí)間點(diǎn)就是在jump動(dòng)畫播放完后。
??要實(shí)現(xiàn)這個(gè)判斷,就要引入一個(gè)虛擬碰撞體,虛擬碰撞體能在角色發(fā)生碰撞時(shí)返回與其碰撞的物體的名稱。實(shí)現(xiàn)這個(gè)虛擬碰撞體的是一個(gè)叫Physics.OverlapCapsule的函數(shù),其實(shí)Overlap的函數(shù)有很多,因?yàn)榻巧旧碛玫木褪悄z囊碰撞體,所以在投射一個(gè)Capsule能比較好的檢測(cè)碰撞。這個(gè)函數(shù)的官方描述是:

public static Collider[] OverlapCapsule(Vector3 point0, Vector3 point1, float radius, int layerMask = AllLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);

Returns
Collider[] Colliders touching or inside the capsule.

Description
Check the given capsule against the physics world and return all overlapping colliders.

??由此得知,功能就是給定一個(gè)膠囊,然后檢查這個(gè)膠囊與物理世界的碰撞(against),然后返回所有碰撞體,據(jù)Returns得知返回的碰撞體放在了一個(gè)Collider類型的數(shù)組里。
??由聲明式知,它吃兩個(gè)Vector3變量,這兩個(gè)Vector3變量確定這個(gè)膠囊的位置;吃一個(gè)float變量radius,這個(gè)radius決定膠囊體上下兩個(gè)圓的半徑,畫個(gè)圖就是這個(gè)亞子:


虛擬膠囊體

??還有一個(gè)int參數(shù)layerMask,這個(gè)參數(shù)是跟Inspector里的Layer有關(guān):
Layer

??以后如果有機(jī)會(huì)就詳細(xì)介紹一下Layer和layerMask。這個(gè)函數(shù)的layerMask默認(rèn)是所有的Layers,即它碰到的所有物體都會(huì)放在返回的Collider數(shù)組里面,不過可以通過LayerMask.GetMask()方法來指定傳遞某一個(gè)Layer。
??首先,我們現(xiàn)在是要檢測(cè)是否于地面相撞,所以我們要新增一個(gè)Layer命名為Ground,如圖:
新增Layer

??然后我們把地面Plane和這個(gè)作為斜坡的Cube的Layer設(shè)為Ground:


Cube

Plane

??3個(gè)參數(shù)中的layerMask準(zhǔn)備就緒,現(xiàn)在來看看怎么設(shè)置這個(gè)虛擬碰撞體的坐標(biāo)和半徑。我們暫時(shí)把這個(gè)虛擬碰撞體跟角色的碰撞體重合,這樣利用角色自身坐標(biāo)和角色碰撞體的半徑和高度得到虛擬碰撞體的坐標(biāo)。角色的自身坐標(biāo)是transform.position,而角色碰撞體的半徑可以通過外傳一個(gè)CapsuleCollier來獲得:


角色碰撞體的CapsuleCollier

??說完這些參數(shù)如何獲得之后,我們來真正寫如何調(diào)用OverlapCapsule:
1.先在角色碰撞體下創(chuàng)建一個(gè)空的子物體Create Empty,并命名為senser(傳感器),負(fù)責(zé)各種碰撞的判斷。
senser

2.在這個(gè)senser里Add Component,命名為OnGroundSenser:


OnGroundSense

3.雙擊打開,先新建兩個(gè)Vector3變量、一個(gè)CapsuleCollider變量、一個(gè)Collider數(shù)組、一個(gè)float變量
    public CapsuleCollider capcol;  //接收膠囊碰撞器
    Vector3 point1;                 //虛擬膠囊體的底圓坐標(biāo)
    Vector3 point2;                 //虛擬膠囊體的頂圓坐標(biāo)
    private Collider[] outputCols;  //用來接收與虛擬膠囊體發(fā)生碰撞的物體
    private float radius;           //模型膠囊體的半徑

4.把角色膠囊體傳給capcol


Capcal

5.在Awake()函數(shù)里獲得角色碰撞體的半徑

    void Awake () {
        radius = capcol.radius;
    }

6.在FixedUpdate()函數(shù)(角色的移動(dòng)是在FixedUpdate()里完成)里建立坐標(biāo)并傳給Physics.OverlapCapsule()函數(shù):

    void FixedUpdate () {
        point1 = transform.position + transform.up * radius;
        point2 = transform.position + transform.up * capcol.height - transform.up * radius;
        outputCols = Physics.OverlapCapsule (point1, point2, radius,LayerMask.GetMask("Ground"));
    }

??虛擬碰撞體的底圓坐標(biāo)(point1)為角色當(dāng)前坐標(biāo)在加一個(gè)角色碰撞體半徑的高度,頂圓坐標(biāo)(point2)為角色當(dāng)前坐標(biāo)加一角色碰撞體的高度再減一個(gè)角色碰撞體半徑的高度。然后使用LayerMask.GetMask("Ground")函數(shù)獲得Layer里的Ground,這樣它就只會(huì)返回與虛擬碰撞體碰撞到的Layer為Ground的GameObject。
7.用先前宣告的Collider數(shù)組來接收Physics.OverlapCapsule()的返回值,并判斷如果這個(gè)數(shù)組的長(zhǎng)度不為0,就往上層(這里是角色的碰撞體PlayerHandle)發(fā)消息:

    void FixedUpdate () {
        point1 = transform.position + transform.up * radius;
        point2 = transform.position + transform.up * (capcol.height-offset) - transform.up * radius;
        outputCols = Physics.OverlapCapsule (point1, point2, radius,LayerMask.GetMask("Ground"));
        if (outputCols.Length!=0) {
            SendMessageUpwards ("IsOnGround");
        } else {
            SendMessageUpwards("IsNotOnGround");
        }
    }

??現(xiàn)在,這個(gè)傳感器就基本ok了,現(xiàn)在應(yīng)該處理一下傳感器的反饋了。
我們應(yīng)該設(shè)置一個(gè)bool變量,在senser給出反饋(發(fā)消息)后,修改這個(gè)bool變量的布爾值,這個(gè)bool變量就是角色是否在地上的依據(jù)。在Animator視窗里的Paramaters創(chuàng)建一個(gè)bool參數(shù)命名為isGround:


isGround

??在ActorController.cs里,接收senser發(fā)送的信息,并改變isGround的布爾值:

    void IsOnGround(){          //當(dāng)與地面發(fā)生碰撞時(shí)
        anim.SetBool ("isGround", true);
    }
    void IsNotOnGround(){       //當(dāng)不與地面發(fā)生碰撞時(shí),跳躍/下墜狀態(tài)
        anim.SetBool("isGround", false);
    }   

??至此,在離開jump動(dòng)畫之后,就能判斷Transition往哪邊走了,可以看看效果:
Fall

??因?yàn)槲覀儸F(xiàn)在并沒有拉fall到ground的Transition所以進(jìn)入到fall動(dòng)畫之后就出不去了,這問題不大,之后肯定會(huì)解決。現(xiàn)在我們能看到,當(dāng)?shù)孛嫣S的時(shí)候,在jump動(dòng)畫還沒播放完,isGround就已經(jīng)經(jīng)歷上勾→消勾→上勾的過程,所以到fall動(dòng)畫的Transition不會(huì)觸發(fā),回到ground;而在斜波上一躍而下時(shí),由于在jump動(dòng)畫播放完后,isGround仍處在消勾狀態(tài),即不在地面,所以觸發(fā)到fall動(dòng)畫Transition,播放fall動(dòng)畫。

??前幾天開始每天的時(shí)間都用來練車了,所以每天能寫的內(nèi)容不算多,但日更不能斷,共勉!

?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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