Unity记一次时装骨骼换装的合并

Posted avi9111

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity记一次时装骨骼换装的合并相关的知识,希望对你有一定的参考价值。

换装入门

之前做过一次,好久没做了

国内团队都会做,做法也是各自精彩,只是分享的少;而国外的技术相对比较单一,但是模型细节度,丰富度都远不如国内,也很难和国内技术栈整合;所以网上一堆看上去还行的资料

(当然,我们拿到的国外素材很多都是几年前的,和国内团队当前素材进行比较并不公平,这里只是说下易用程度,国内素材+简单代码==容易使用)

重新做的时候被网上的资料坑了很多

搞了2个星期,终于搞完

记录一哈

(以下为残缺代码,非整个项目)

只记录重点,关键就是2个预制体或FBX融合的时候,bones要整合用第一个(很多网上的做法都是直接bones addList(),这种做法的基础必须是所有时装的bones是在同一个预制体,同一个fbx内部的,其实无需要自我限制,骨骼绑定,mesh合并,添加材质等等都是可以很“松耦合”的)

第二个重点,就是选择采用单一mesh合并(skinnedmeshrenderer相当黑盒啊),否则多个mesh,多个材质也是一种办法,相互转化,但Unity官方的东西就是。。好像能 这么用,又好像能那么用。。就是不告诉你怎么用。。。。(不公开透明的API)还真实很多坑的,

烧菜坑思路就是“怎么简单怎么来”

其他重点,就是boneposes和matrix4x4的转换了

    for (int i = 0; i < length; i++)
        {
            if(i==0) continue;//个人模型问题,这个判断可以不要,(个人第二个模型才是人体主模型)
            SkinnedMeshRenderer oneRender = combineRenders[i];
            // 记录骨骼
            //bones.AddRange(oneRender.bones);
            //(两个预制体合并,所以需要合并骨骼,不能用自己的骨骼)
            var bonesInRoot = combinedRoot.GetComponentsInChildren<Transform>();
            List<Transform> findBones = new List<Transform>();
            foreach (var b in oneRender.bones)
            {
                bool did = false;
                foreach (var t in bonesInRoot)
                {
                    if (t.name == b.name)
                    {
                        findBones.Add(t);
                        did = true;
                    }
                    
                }   
                if(did==false)
                    findBones.Add(b);
            }
            bones.AddRange(findBones);
            
            // 记录权重
            BoneWeight[] meshBoneweight = oneRender.sharedMesh.boneWeights;
            for (int j = 0; j < meshBoneweight.Length; ++j)
            {
                BoneWeight bw = meshBoneweight[j];
                BoneWeight bWeight = bw;
                bWeight.boneIndex0 += boneOffset;
                bWeight.boneIndex1 += boneOffset;
                bWeight.boneIndex2 += boneOffset;
                bWeight.boneIndex3 += boneOffset;
                boneWeights.Add(bWeight);
            }
            // offset是为了合并之后BoneWeight.boneIndex还能正确定向骨骼
            boneOffset += oneRender.bones.Length;
            // 记录网格相关信息
            CombineInstance combineInstance = new CombineInstance();
            Mesh mesh = new Mesh();
            oneRender.BakeMesh(mesh);
            mesh.uv = oneRender.sharedMesh.uv;
            combineInstance.mesh = mesh;
            //combineInstance.transform = oneRender.localToWorldMatrix;//有个天坑是 oneRender.localToWorldMatrix 和 oneRender.transform.localToWorldMatrix 是两回事
            //Matrix4x4 matrix = oneRender.transform.worldToLocalMatrix;
//            combineInstance.transform = oneRender.transform.localToWorldMatrix ;
            combineInstance.transform =
                oneRender.transform.localToWorldMatrix * Matrix4x4_Rotation( EnemyManager_Axle.X, -90);
            //combineInstance.subMeshIndex = i-1;//个人模型问题,这个判断可以不要,(个人第二个模型才是人体主模型),也不可以这么分,骨骼会错乱(个人采用了合并单一Mesh方法)
            combineInstances.Add(combineInstance);
           // oneRender.gameObject.SetActive(false);//网上抄的方法,完全不知道为什么要这么用
        }

    // 将所有的骨骼变换矩阵从自身转换到当前预制件下
        List<Matrix4x4> bindposes = new List<Matrix4x4>();
        int boneLength = bones.Count;
        for (int i = 0; i < boneLength; i++)
        {
            bindposes.Add(bones[i].worldToLocalMatrix * combineMatrix);
        }

        Mesh combinedMesh = new Mesh();
        combinedMesh.CombineMeshes(combineInstances.ToArray(), true, true);
        combinedSkinnedRenderer.sharedMesh = combinedMesh;
        combinedSkinnedRenderer.bones = bones.ToArray();
        combinedSkinnedRenderer.sharedMesh.boneWeights = boneWeights.ToArray();
        combinedSkinnedRenderer.sharedMesh.bindposes = bindposes.ToArray();
        combinedSkinnedRenderer.sharedMesh.RecalculateBounds();
        combinedSkinnedRenderer.material = combineMaterial;
  /// <summary>
    /// 一个简单的旋转,其他Matrix4x4变换可看:https://blog.csdn.net/aaa583004321/article/details/81948780
    /// </summary>
    /// <param name="transform"></param>
    /// <param name="axle"></param>
    /// <param name="angle"></param>
    public static Matrix4x4 Matrix4x4_Rotation(EnemyManager_Axle axle, float angle)
    {
        Matrix4x4 matrix = Matrix4x4.identity;

        if (axle == EnemyManager_Axle.X)
        {
            matrix.m11 = Mathf.Cos(angle * Mathf.Deg2Rad);
            matrix.m12 = -Mathf.Sin(angle * Mathf.Deg2Rad);
            matrix.m21 = Mathf.Sin(angle * Mathf.Deg2Rad);
            matrix.m22 = Mathf.Cos(angle * Mathf.Deg2Rad);

        }
        else if (axle == EnemyManager_Axle.Y)
        {
            matrix.m00 = Mathf.Cos(angle * Mathf.Deg2Rad);
            matrix.m02 = Mathf.Sin(angle * Mathf.Deg2Rad);
            matrix.m20 = -Mathf.Sin(angle * Mathf.Deg2Rad);
            matrix.m22 = Mathf.Cos(angle * Mathf.Deg2Rad);
        }
        else
        {
            matrix.m00 = Mathf.Cos(angle * Mathf.Deg2Rad);
            matrix.m01 = -Mathf.Sin(angle * Mathf.Deg2Rad);
            matrix.m10 = Mathf.Sin(angle * Mathf.Deg2Rad);
            matrix.m11 = Mathf.Cos(angle * Mathf.Deg2Rad);
        }

        return matrix;
    }

    public  enum EnemyManager_Axle
    {
        X,
        Y,
        Z
    }

参考:

技巧| Unity中Avatar换装实现 - UWA Blog (uwa4d.com)

以上是关于Unity记一次时装骨骼换装的合并的主要内容,如果未能解决你的问题,请参考以下文章

Unity 网格合并

Unity3D换装系统

Unity 实现 角色的换装

记一次Unity动画入门之二专家技能评定

Unity3d 换装 之 模型动画分离

原Unity 骨骼节点对象优化,AnimatorUtility.OptimizeTransformHierarchy