【Unity动态换装骨骼合并】

文章目录

换装骨骼合并

前言

前司有个换装需求,把身上的衣服当做部件来制作,每个单独的裙子、手头、翅膀、帽子上都完整的骨骼部分(即主骨骼),然后有特殊需求的部分会加上额外骨骼。

换装的实现原理

换装是游戏里面常见的模块,现在已经有非常多的游戏实装了换装的模块,一般来说,换装的核心实现就是以下几种方案:

  • 材质替换(材质或贴图)
  • 网格替换(Mesh)
  • 骨骼绑定部件替换
  • 模型替换

还有其余的一些方法(通过框架比如Universal Multiplayer Avatar等),我下面使用的是主要是第三种方法即使用部件替换。材质替换适用于那种简单的皮肤换色,换纹理,比较朴素(糊弄)的方法,有点是性能消耗少;网格替换使用的也不多,不适合灵活部件的替换,一般会使用合并的方法减少Draw Call(CombineMesh或者自己写)

人物模型

  • 使用网格替换和部件的,都需要基于同一套骨骼或者共享同一骨骼树(如果第三方的部件替换也需要遵循此要求),下面以简单的例子举例:

这是一个人物,如果我们不需要动态添加部件,那么人物的节点信息可以简单的设置成如下(不考虑性能)

可以看到,SkinnedMeshRenderer蒙皮骨骼节点的Root Bone指向的是root节点,

所以这个人物换装的思路就很明确了,因为是公用的一组骨骼,而且本身部件上不带自己的骨骼,所以需要的时候直接SetActive就能达到换装的效果,通过捣鼓配置就能达成换装的目的(ScriptObject啥的),当场景中人物过多的时候,可以执行网格合并减少DC,但是如果组件是从网上下载的(比如通过WebRequest下载部件,然后动态的一个个加上去,而且部件自身带了完整的骨骼,就会稍微麻烦一些)

动态加载

  • 动态加载要做的比上面静态放置的多几个步骤,去掉或者合并自带的骨骼(没有更好),将骨骼减少到一条主骨骼(比如body身体上的部分)。这样做的好处有很多,一是减少了动画控制器的数量,如果你要这几个组件同时做动画,不处理的情况下,有几个部件就需要创建几个动画控制器(虽然可以复用),会增加内存、CPU的调用,Animation.Update会消耗不少性能,还有一个问题就是可能会不协调穿帮(因为是部件组成,会存在部件接缝衔接不上的情况,你可以实际中试试)
  1. 如果是下载的,需要初始化到物体身上,放到父节点下面
  2. 编写脚本进行动态合并:
csharp 复制代码
public List<GameObject> Equips = new List<GameObject>(); // 在外面的面板上可以看到对应的组件
Dictionary<string, Transform> m_MainBonesDic = new Dictionary<string, Transform>(); // 记录骨骼

// 初始化主骨骼
private void InitMainBones()
{
    m_MainBonesDic.Clear();
    Transform outtran = null;

    var childtrans = GetComponentsInChildren<Transform>();
    for (int i = 0; i < childtrans.Length; ++i)
    {
        var tran = childtrans[i];
        if (!m_MainBonesDic.TryGetValue(tran.name, out outtran))
            m_MainBonesDic[tran.name] = tran;
    }
}

// 讲物体全部放到父节点下面后(组件的原点信息最好都是0点)
private void WearParts(List<GameObject> Equips)
{
    for (int i = 0; i < Equips.Count; i++)
    {
        var equip = Equips[i];
        CombineSourceBones(equip);
    }
}

// 合并组件的骨骼到一条骨骼身上
public void CombineSourceBones(GameObject sourceClothing)
{
    sourceClothing.transform.SetParent(transform);
    var skinnedMeshRenderers = sourceClothing.GetComponentsInChildren<SkinnedMeshRenderer>(true);

    foreach (var sourceRenderer in skinnedMeshRenderers)
    {
        sourceRenderer.bones = TranslateTransforms(sourceRenderer.bones);
        sourceRenderer.rootBone = GetBoneByName(sourceRenderer.rootBone.name);

        UpdateSkinMeshMagicaClothInfo(sourceClothing); // (如果有Magic Cloth,可以在这里处理)
    }

	// 如果原来的部件身上都带有完整骨骼,需要销毁
    DestroyRootBones(sourceClothing);

    sourceClothing.transform.localPosition = Vector3.zero;
    sourceClothing.transform.localRotation = Quaternion.identity;
}

// 举个例子
private void DestroyRootBones(GameObject sourceClothing)
{
    var rootbone = sourceClothing.transform.Find("Bip"); // 或者其他名字

    if (rootbone == null)
        rootbone = sourceClothing.transform.Find("Root");

    if (rootbone != null)
        Destroy(rootbone.gameObject);
}

private Transform GetSourceBoneParentInMainBones(Transform sourceBone, out Transform curCheckBoneParent)
{
    var sourceboneparent = sourceBone.parent;
    var mainbone = GetBoneByName(sourceboneparent.name);
    if (mainbone == null)
    {
        return GetSourceBoneParentInMainBones(sourceboneparent, out curCheckBoneParent);
    }
    curCheckBoneParent = sourceBone;
    return mainbone;
}

private Transform GetBoneByName(string boneName)
{
    if (m_MainBonesDic.TryGetValue(boneName, out Transform tran))
    {
        return tran;
    }
    return null;
}
  1. 你可以在Start里面执行初始化和穿装备,半途更新需要的话就封装一个接口调用,这样的话很方便别人调用。

实际效果如何就不展示了,如果你的项目需要,可以到具体的场景中比较效果。

相关推荐
kimloner4 小时前
开源一个 376KB 的 Windows 桌面小工具:便签 + 截图标注 + OCR + 待办提醒(C# / WinForms)
windows·开源·c#·ocr·winforms
阿松爱学习5 小时前
【Unity开发】Aspose.PDF的使用
unity·pdf·c#
半亩码田6 小时前
C#转Python第4.8篇:正则表达式:Python re 模块 vs C# System.Text.RegularExpressions
python·正则表达式·c#
FPGA小徐6 小时前
【一生一芯 / PA】异常响应机制:RISC-V 中 ecall → mtvec → mret 的完整代
开发语言·数据库·c#
淡海水7 小时前
02-04-原理篇-GC Roots与对象图遍历
算法·unity·c#·图·gc·遍历·roots
胖纸不争11 小时前
嫌桌面 Agent 太重,我“自己”写了一个
ai·c#·net core
当青春邂逅吉米多维奇15 小时前
C#图解教程(第5版) 同步方法
开发语言·c#
波力海苔夹心脆67515 小时前
C# 机器视觉实战:单相机引导机械手精准贴合,从拍照、偏差计算(平移+旋转)到坐标补偿全解
开发语言·经验分享·数码相机·c#·视觉检测·.net
Behavior17 小时前
国内下载使用 Unity 6000 踩坑点总结
c#·unity3d·游戏开发
czhc114007566318 小时前
一张图从哪来,一个判断往哪走
c#