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

??現(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:
??那么遇到的第一個(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):

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

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


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

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

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

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

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:

??在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往哪邊走了,可以看看效果:
??因?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)容不算多,但日更不能斷,共勉!