换装所涉及的其实就是SkinnedMeshRenderer,它包含了网格,骨骼,材质;

首先,需要一个源Model,它包含了换装所需要的所有东西。

这里写图片描述
带Bip字眼的是骨骼;

然后需要就是我们的目标,只有骨骼;
这里写图片描述

换装的过程就是将Source中对应的SkinnedMeshRenderer信息复制到Target上,这里的信息就是网格,骨骼,材质;

下面,一步步来:

1、将Source实例化到场景当中,保存他的Transform,这样可以在隐藏Source对象之后,仍然可以访问到子对象当中的SkinnedMeshRenderer组件

    /// <summary>
    /// 源 包含所有组件
    /// </summary>
    private Transform source;

    /// <summary>
    /// 源的临时GameObject
    /// </summary>
    private GameObject tempsource;

    /// <summary>
    /// 初始化源source 加载Prefab->Source到场景中,设置SetActive为false,并保存它的Transform
    /// 源是不需要显示的
    /// </summary>
    void InstantiateAvatar()
    {
        //Prefab FemaleSource包含所有需要的组件 皮肤、骨骼……
        tempsource = Instantiate(Resources.Load("Source")) as GameObject;
        source = tempsource.transform;
        tempsource.SetActive(false);
    }

这里写图片描述

2、将目标加载到场景当中

    /// <summary>
    /// 目标
    /// </summary>
    private Transform target;

    /// <summary>
    /// 目标的临时GameObject
    /// </summary>
    private GameObject temptarget;

    /// <summary>
    /// 加载目标Prefab->Target到场景中,Target上除了骨骼什么也没有,但这个使我们“换装”的目标
    /// </summary>
    void LoadSkeleton()
    {
        temptarget = Instantiate(Resources.Load("Target")) as GameObject;
        target = temptarget.transform;
    }

这里写图片描述

3、将Source上所有的模型资源存储到data里面,同时在这里会给第2步生成的Target对象添加需要换装的部位(并对他们的Transform等信息),但SkinnedMeshRenderer都是空的

    /// <summary>
    /// 存储Source上所有的模型资源的transform
    /// </summary>
    private Dictionary<string, Dictionary<string, Transform>> data = new Dictionary<string, Dictionary<string, Transform>>();

    /// <summary>
    /// 目标的SkinnedMeshRenderer集合
    /// </summary>
    private Dictionary<string, SkinnedMeshRenderer> targetSmr = new Dictionary<string, SkinnedMeshRenderer>();

    /// <summary>
    /// 通过源获取它的子类中所有skinnedmeshrenderer组件
    /// 来个data赋值
    /// 但是data数据都是空的,这里只是确定了GameObject的transform父子关系,给GameObject添加的SkinnedMeshRenderer
    /// </summary>
    /// <param name="source"></param>
    //load source skinnedmeshrenderer
    void LoadAvatarData(Transform source)
    {
        if (null == source)
            return;

        // 获取所有的SkinnedMeshRenderer,如果对象隐藏了也得到
        SkinnedMeshRenderer[] parts = source.GetComponentsInChildren<SkinnedMeshRenderer>(true);

         foreach (SkinnedMeshRenderer part in parts)
         {
             string[] partName = part.name.Split('-');
             if(!data.ContainsKey(partName[0]))
             {
                data.Add(partName[0], new Dictionary<string, Transform>());
                GameObject partObj = new GameObject();
                partObj.name = partName[0];
                partObj.transform.parent = target;
                partObj.transform.localPosition = part.transform.localPosition;
                partObj.transform.localRotation = part.transform.localRotation;

                targetSmr.Add(partName[0], partObj.AddComponent<SkinnedMeshRenderer>());
             }

             data[partName[0]].Add(partName[1], part.transform);
         }
    }

执行如下代码:

    void Start () 
    {
        InstantiateAvatar();    // 加载源到场景中
        LoadSkeleton();         // 加载目标到场景中
        LoadAvatarData(source); // 将源中所有需要换装的transform保存起来

        //hips = target.GetComponentsInChildren<Transform>(); // 获取目标中所有的骨骼Transform
        //mAnim = target.GetComponentInChildren<Animation>(); // 骨骼上有动画组件

        //InitAvatar();
    }

这里写图片描述

4、换装,对组件SkinnedMeshRenderer进行赋值,也就是网格mesh,骨骼bone,材质material,其中骨骼是需要重新排序的

    /// <summary>
    /// 更换皮肤 可以对外公开的函数
    /// </summary>
    /// <param name="part"></param>
    /// <param name="item"></param>
    public void ChangeMesh(string part, string item)
    {
        SkinnedMeshRenderer smr = data[part][item].GetComponent<SkinnedMeshRenderer>();

        List<Transform> bones = new List<Transform>();
        foreach (Transform bone in smr.bones)
        {
            foreach (Transform hip in hips)
            {
                if (hip.name != bone.name)
                    continue;
                bones.Add(hip);
                break;
            }
        }

        targetSmr[part].sharedMesh = smr.sharedMesh;
        targetSmr[part].bones = bones.ToArray();
        targetSmr[part].materials = smr.materials;
    }

5、核心就是上面的了,具体请查阅码云示例
https://gitee.com/xianglinlove/Avatar
这里写图片描述

这里写图片描述

Logo

致力于链接即构和开发者,提供实时互动和元宇宙领域的前沿洞察、技术分享和丰富的开发者活动,共建实时互动世界。

更多推荐