1. 程式人生 > >Unity裡的人物驅動/換裝備/換武器/換衣服/動畫重定位

Unity裡的人物驅動/換裝備/換武器/換衣服/動畫重定位

剛學的過程被這個問題困擾最多。

首先,基本的,大家都知道驅動人物需要骨架、綁骨的Mesh和動畫(這三個要是不知道的話就得考慮看看計算機圖形學先)

然後,基本上有點maya(或其他)功底的都知道,在maya裡,骨架、mesh和動畫都是一種單獨的存在,誰混一起誰鬱悶。

在unity裡面,情況也是如此,不過再複雜一點展開。

1、骨架就是transform。不像maya和理論那樣,是一種實體的單獨的存在。在unity裡,骨架就是transform的層次關係。一個骨頭就是一個transform。這個人體的骨架(Transform)組織結構一般是abdome(spine,LeftUpLeg,RightUpLeg),abdome是根(有時用hip),向上是spine(neck,LeftUpArm,RightUpArm)。這個在哪裡都差不多。關鍵是root,就是abdome。在unity裡,引用骨架都是靠它。

2、Mesh。因為Unity裡的Mesh有綁骨和未綁骨兩種(在哪裡都差不多)。在匯入資源的時候,unity會為綁骨的Mesh自動生成一個SkinnedMeshRenderer,為未綁骨的生成一個MeshFilter和一個MeshRenderer。不精細的人物,頭盔、手、武器(甚至靴子)等都不用綁骨,像騎馬與砍殺,只需要把armor綁骨就好。

不綁骨情況:MeshFilter負責從檔案中取出真實的Mesh(這裡用的是Script裡的類來表示,具體可以查reference)。MeshRender負責顯示。這種想要固定在人體上,其實很簡單,把這個GameObject的tansform的parent設定為需要繫結的骨頭的transform就好。(比如武器,transform.parent=item_l,item_l是左手一個繫結武器骨頭)所以,缺點也很明顯,一個mesh只能繫結到一個骨頭,也就是這個mesh不會變形。(這個可以作為綁骨和不綁骨的依據,看Mesh是否需要變形,所以頭盔,武器都不需要綁骨(僅在unity中成立))

       綁骨情況:SkinnedMeshRenderer有倆個重要成員,一個是Mesh,一個是Bones。當然,Mesh裡有每一個頂點的綁骨資料,但這裡存的是序號。就是第幾個骨頭,並沒有指明是哪個骨頭,但是如果給一個根骨頭就能推測出所有的骨頭了。這裡的Bones把所有的骨頭都列出來了,至於順序,有點扯淡,沒啥規律(除錯結果)。問題出來了,那SkinnedMeshRenderer裡的骨頭這麼沒順序,Mesh裡的骨頭序號怎麼對應過來呢?好吧,仔細想想,其實沒那麼複雜。因為SkinnedMeshRenderer裡包含Mesh和Bones兩個成員,由於都是一起生成的,那麼就一個坑埋一個人,正好就對應起來了。之所以要這麼個Bones列表,是因為要把它畫成transform樹吧。

      這樣就再回答一個問題,如何換裝備?原SkinnedMeshRender對應的骨骼已經存在,不可能銷燬它(還有其他部分要用),所以就修改SkinnedMeshRenderer本身就好。要改的有兩個值(加上材質三個):Mesh和Bones。其實,原Bones根本不用變,只需要把Mesh變了就好(除非你的綁骨不是一幫人做的)。詳細程式碼看看Unity有個官方專案。另外,我覺得吧,連SkinnedMeshRender都不用操縱,直接重新生成就好。看文章最後的程式碼。

      動畫部分最有愛。其實呢,layer這個玩意,用來分組比較合適。優先順序這個特點也是可以用的,不過寄太大信心。一個layer可以設定同樣的疊加方式(累加,混合),控制方式(只控制上半身)。一般不同動畫都放在不同層,相同功能動畫在同一層,比如向左砍人和向右砍人。另外,我們動畫一般都是自己搞到的一個一個片段,如果你不是動畫師,還是不要用啥累加和混合了。用一些獨立的動畫,讓他控制身體的某些部位就好。

      所以,綜合看,人物控制,在unity 裡,換裝備,換武器都是可行的。不知道咋了,網上就是搜不到如何換裝的,搞到我自己研究了好久。。。

     另外,最近的新發現,升級到4後,有個mecanima模組,用來做動畫重定位的,這個叫帥啊。我的以下程式碼沒辦法處理動畫,因為動畫之前無法重定位。但是可愛的unity的新功能來了,給每個armor和每個動畫都建立一個對應的avatar,然後就直接可以用一套動畫驅動所有人了(雖然表面上我們顯示的是一個人,但是在內部,實現這種換裝方法用的是好幾個獨立的人體骨骼,當然,這種方法要求每個獨立的骨骼都是相同的)。

附上換裝程式碼。

#pragma strict
public var O_body_r:GameObject;
public var O_hand_L_r:GameObject;
public var O_hand_R_r:GameObject;
public var O_helmet_r:GameObject;
public var O_calf_L_r:GameObject;
public var O_calf_R_r:GameObject;
public var O_weapon_R_r:GameObject;


public var body1_r:GameObject;


private var mBody:GameObject;


private var mHand_L:GameObject;
private var mHand_L_P:Transform;


private var mHand_R:GameObject;
private var mHand_R_P:Transform;


private var mHelmet:GameObject;
private var mHelmet_P:Transform;


private var mCalf_L:GameObject;
private var mCalf_L_P:Transform;


private var mCalf_R:GameObject;
private var mCalf_R_P:Transform;


private var mWeapon_R:GameObject;
private var mWeapon_R_P:Transform;




function Start () {


}


private var ifIs:boolean=true;
function Update () {
   
if(Input.GetButtonDown("Fire1")){
Generate(ifIs);
ifIs=!ifIs;
}


}


function Generate(num:boolean){
if(mBody){
Destroy(mBody);
}
if(num==true){
mBody=Instantiate(O_body_r,transform.position,Quaternion.identity);
}else{
mBody=Instantiate(body1_r,transform.position,Quaternion.identity);
}


mHand_L_P=mBody.transform.Find("abdomen/spine/thorax/shoulder.L/upperarm.L/forearm.L/hand.L");
mHand_L=Instantiate(O_hand_L_r,mHand_L_P.position,Quaternion.identity);
mHand_L.transform.parent=mHand_L_P;
mHand_L.transform.localRotation=Quaternion.Euler(0,270,90);

mHand_R_P=mBody.transform.Find("abdomen/spine/thorax/shoulder.R/upperarm.R/forearm.R/hand.R");
mHand_R=Instantiate(O_hand_R_r,mHand_R_P.position,Quaternion.identity);
mHand_R.transform.parent=mHand_R_P;
mHand_R.transform.localRotation=Quaternion.Euler(0,270,90);

mWeapon_R_P=mBody.transform.Find("abdomen/spine/thorax/shoulder.R/upperarm.R/forearm.R/hand.R/item.R");
mWeapon_R=Instantiate(O_weapon_R_r,mWeapon_R_P.position,Quaternion.identity);
mWeapon_R.transform.parent=mWeapon_R_P;
mWeapon_R.transform.localRotation=Quaternion.Euler(0,270,90);

mCalf_L_P=mBody.transform.Find("abdomen/thigh.L/calf.L");
mCalf_L=Instantiate(O_calf_L_r,mCalf_L_P.position,Quaternion.identity);
mCalf_L.transform.parent=mCalf_L_P;
mCalf_L.transform.localRotation=Quaternion.Euler(0,270,90);

mCalf_R_P=mBody.transform.Find("abdomen/thigh.R/calf.R");
mCalf_R=Instantiate(O_calf_R_r,mCalf_R_P.position,Quaternion.identity);
mCalf_R.transform.parent=mCalf_R_P;
mCalf_R.transform.localRotation=Quaternion.Euler(0,270,90);

mHelmet_P=mBody.transform.Find("abdomen/spine/thorax/head");
mHelmet=Instantiate(O_helmet_r,mHelmet_P.position,Quaternion.identity);
mHelmet.transform.parent=mHelmet_P;
mHelmet.transform.localRotation=Quaternion.Euler(0,270,90);


}