1. 程式人生 > >Unity角色換裝問題

Unity角色換裝問題

遊戲中經常涉及到角色換裝問題。

1.裝備模型中的每一套裝備都必須使用同一套骨骼,把單個骨骼資料儲存成Prefab,骨骼資料在Unity中以Transform形式存在

2.把模型中的每個部分都單獨儲存成prefab。

實現思路:

1.建立骨骼GameObject,把所有的裝備蒙皮資料合併到一個prefab中。

2.建立裝備GameObject,用來手機其中蒙皮資料,從而建立新的SkinedMeshRenderer。

收集資訊程式碼

// Collect information from meshes
        for (int i = 0; i < meshes.Length; i ++)
        {
            SkinnedMeshRenderer smr 
= meshes[i]; materials.AddRange(smr.materials); // Collect materials // Collect meshes for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++) { CombineInstance ci = new CombineInstance(); ci.mesh = smr.sharedMesh; ci.subMeshIndex
= sub; combineInstances.Add(ci); } // Collect bones for (int j = 0 ; j < smr.bones.Length; j ++) { int tBase = 0; for (tBase = 0; tBase < transforms.Count; tBase ++) {
if (smr.bones[j].name.Equals(transforms[tBase].name)) { bones.Add(transforms[tBase]); break; } } } }

為新的骨骼生成SkinnedMeshRenderer。

// Create a new SkinnedMeshRenderer
SkinnedMeshRenderer oldSKinned = skeleton.GetComponent<SkinnedMeshRenderer> ();
    if (oldSKinned != null) {
    GameObject.DestroyImmediate (oldSKinned);
    }
SkinnedMeshRenderer r = skeleton.AddComponent<SkinnedMeshRenderer>();
r.sharedMesh = new Mesh();
r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);// Combine meshes
r.bones = bones.ToArray();// Use new bones

掛載武器

Transform[] transforms = Instance.GetComponentsInChildren<Transform>();
foreach (Transform joint in transforms) {
if (joint.name == "weapon_hand_r") 
{// find the joint (need the support of art designer)
        WeaponInstance.transform.parent = joint.gameObject.transform;
        break;
    }   
    }

這裡涉及到一個優化,合併出來的新的skinned Mesh Renderer中有所有部位的materials,

這裡我們可以吧materials合併成一個,減少DrawCall,但是同樣會增加記憶體,在具體專案中,可以結合實際情況進行優化,

// merge materials
if (combine)
{
    newMaterial = new Material (Shader.Find ("Mobile/Diffuse"));
    oldUV = new List<Vector2[]>();
    // merge the texture
    List<Texture2D> Textures = new List<Texture2D>();
    for (int i = 0; i < materials.Count; i++)
    {
        Textures.Add(materials[i].GetTexture(COMBINE_DIFFUSE_TEXTURE) as Texture2D);
    }

    newDiffuseTex = new Texture2D(COMBINE_TEXTURE_MAX, COMBINE_TEXTURE_MAX, TextureFormat.RGBA32, true);
    Rect[] uvs = newDiffuseTex.PackTextures(Textures.ToArray(), 0);
    newMaterial.mainTexture = newDiffuseTex;

// reset uv
    Vector2[] uva, uvb;
    for (int j = 0; j < combineInstances.Count; j++)
    {
        uva = (Vector2[])(combineInstances[j].mesh.uv);
        uvb = new Vector2[uva.Length];
        for (int k = 0; k < uva.Length; k++)
        {
            uvb[k] = new Vector2((uva[k].x * uvs[j].width) + uvs[j].x, (uva[k].y * uvs[j].height) + uvs[j].y);
        }
        oldUV.Add(combineInstances[j].mesh.uv);
        combineInstances[j].mesh.uv = uvb;
    }
    }

此方式是參考網上分析,原作者GitHub:https://github.com/zouchunyi/UnityAvater