(一)表情Morph
即使你把ModelImporter.optimizeMesh設(shè)為false,Unity還是會嘗試合并相同的頂點(diǎn)并破壞你模型的拓?fù)?,這使得頂點(diǎn)動畫無法正常工作,唯一的辦法就是實(shí)現(xiàn)自定義模型格式的導(dǎo)出和導(dǎo)入。
計算頂點(diǎn)動畫時,用MeshFilter.sharedMesh的頂點(diǎn)作為源頂點(diǎn),加上Morph的偏移最終保存到MeshFilter.mesh的頂點(diǎn)中去即可。
(二)透明排序
這個有點(diǎn)老生常談了。上圖從左到右依次為:
1.AlphaTest
2.AlphaBlend
3.AlphaBlend
4.AlphaBlend排序
可以看出只有經(jīng)過排序的效果才是最理想的。
在建模時需要美工對模型進(jìn)行分層處理,要制定好命名規(guī)范以便程序員識別。畫的時候先從外到內(nèi)畫所有背面,再從內(nèi)到外畫所有正面。正反兩面的模型要分開各自存放,否則用Cull
Front|Back來回切換會很麻煩。Unity控制渲染順序最簡單的方法就是修改Material.renderQueue,好在Material的大量實(shí)例化并不會明顯增加內(nèi)存的負(fù)擔(dān)。
(三)ConfigurableJoint和SkinnedCloth
萌舞使用ConfigurableJoint來模擬頭發(fā)和胸部。首先要把位移(xyzMotion)鎖死,然后選擇兩個軸打開旋轉(zhuǎn)(三個都打開會有錯誤),只有X軸可以雙向設(shè)置參數(shù)(lowAngularXLimit是負(fù)角度時的參數(shù),highAngularXLimit是正角度時的參數(shù)),利用這個特性可以限制披肩的長發(fā)只能向后飄而不會向前穿過身體,通過設(shè)置Joint.axis和Joint.secondaryAxis來確定Joint內(nèi)部的軸向。
所有Joint所在的GameObject都要放在根節(jié)點(diǎn)上,通過Joint.connectedBody連接到它原來的父節(jié)點(diǎn)。
用SkinnedCloth實(shí)現(xiàn)裙子的效果很容易,建模時把頂點(diǎn)綁在腿上就行,進(jìn)Unity以后再拿筆刷畫頂點(diǎn)的系數(shù),犯懶的話直接根據(jù)頂點(diǎn)的Y坐標(biāo)推算也可以。
(四)動作
MMD的舞蹈動作使用了IK,可以轉(zhuǎn)BVH再轉(zhuǎn)FBX,拿MotionBuilder過了一遍發(fā)現(xiàn)兩分多鐘的舞蹈居然有7M,萌舞有20多個舞蹈,加上音樂模型貼圖APK會超過200M,這是無法容忍的。于是開始找原因,發(fā)現(xiàn)Unity的動畫系統(tǒng)不能直接對Quaternion做Slerp,只能一個個分量分別插值,還要記錄每個關(guān)鍵幀的inTangent和outTangent,不僅如此,由于這樣插值有誤差,Unity還會自動加幀。其他類型的游戲由于動作都比較短所以這個問題并不明顯,對于萌舞來說卻是致命的。
于是只好自己寫動畫系統(tǒng),順便把IK實(shí)現(xiàn)了,這樣會進(jìn)一步壓縮動作,對IK感興趣的同學(xué)可以參考這個古老的網(wǎng)站,感覺網(wǎng)上絕大多數(shù)的代碼都是照它寫的。
接下來是骨骼映射和軸向轉(zhuǎn)換,也就是實(shí)現(xiàn)MotionBuilder的核心功能。骨骼映射把名字對上就行了,軸向的話由于MMD采用的是世界0旋轉(zhuǎn)而我們的人物是局部0旋轉(zhuǎn)所以動畫數(shù)據(jù)每個骨骼的旋轉(zhuǎn)要左乘一個ReferencePose時它自身的世界旋轉(zhuǎn)。 (五)防作弊與破解 單機(jī)游戲最怕的就是作弊,主要包括內(nèi)存修改和存檔修改。我寫了一個AES-256加密版的PlayerPrefs,存檔中的所有數(shù)據(jù)都是用的時候解出來改完立刻存回去,全局變量里不留痕跡。key是基于DeviceID生成的,所以存檔也不能共享。 為了防止APK被人重新簽名發(fā)布,需要在程序內(nèi)部驗(yàn)證當(dāng)前APK的簽名,顯然這件事不應(yīng)該在Java里做,反正我是能看懂混淆后的Java代碼,況且Unity項目Java代碼就那么一丁點(diǎn)。利用AndroidJavaClass和AndroidJavaObject很容易實(shí)現(xiàn)在Unity中驗(yàn)證A |
|