Unity换装

张开发
2026/4/10 2:21:27 15 分钟阅读

分享文章

Unity换装
前情提要: 需要实现一个换装功能, 人物分为多个部位, 每个部位可以替换每个部件是fbx文件, fbx包含SkinnedMeshRenderer(蒙皮骨骼渲染器)和骨骼先了解一下SkinnedMeshRenderer组件Bounds是包围盒, 用于判断是否在相机视野内进行裁切Mesh网格, 决定形状Quality是蒙皮顶点受几根骨骼影响, 一般选Auto, 数值越大动作时蒙皮衔接越流畅Root Bone所有驱动该蒙皮的骨骼的父节点Materials材质然后实现换装功能前提是所有部件的骨骼必须都是相同的(名称和层级)实现思路有两个:举例: 用部件B替换原部件A思路一 (未验证): 将B的所有资源引用赋值给A, 包含mesh, bones, rootBone, materials这种方法消耗小, 但是未经验证, 且需要原始人物有各部位的槽位思路二 (已实现): 将部件B实例放在部件A同级位置, 将部件B所依赖的骨骼全部重定向到当前人物骨骼中, 将部件B所携带的骨骼删除, 删除或隐藏部件A详细说一下:首先要拼接出来一个默认人物定义一个部件结构[System.Serializable]publicclassCustomizationEntry{publicCustomizationSlotslot;// 装扮插槽 CustomizationSlot是自定义的部位枚举publicGameObjectcustomizationPrefab;// 装扮预制体 可直接导入fbx文件}获取默认部件publicListCustomizationEntrydefaultCustomizationsnewListCustomizationEntry();// 默认装扮这里我是分为了头部和身体保存人物模板预制体只需要保留固定的部位(我这里面部是不会替换的), 和骨骼(最重要!!)在人物实例化后去拼装默认部件循环导入的默认部件去挨个实例出来// 组装默认装扮publicvoidCombineDefaultCharacter(){foreach(CustomizationEntryentryindefaultCustomizations){ReplaceCustomization(entry.customizationPrefab,entry.slot);}}替换部件的公共方法, 也可以为换装提供使用思路就是之前说的方法二// 替换部件publicvoidReplaceCustomization(GameObjectprefab,CustomizationSlotslot){if(prefabnull)return;if(customizations.ContainsKey(slot)prefab.namecustomizations[slot]?.name)return;// 检查是否已存在同名对象 直接激活Transformcustomizationtransform.Find(prefab.name);if(customization!null){customization.gameObject.SetActive(true);customization.localPositionVector3.zero;customization.localRotationQuaternion.identity;customization.localScaleVector3.one;}else{// 实例化新对象customizationInstantiate(prefab,transform).transform;customization.nameprefab.name;customization.SetParent(transform);customization.localPositionVector3.zero;customization.localRotationQuaternion.identity;customization.localScaleVector3.one;RedirectBones(customization.GetComponentsInChildrenSkinnedMeshRenderer());// 删除原有骨骼Transformrootcustomization.Find(Root);if(root!null)Destroy(root.gameObject);}// 禁用原始部件if(customizations.TryGetValue(slot,outTransformoldCustomization)){oldCustomization.gameObject.SetActive(false);}customizations[slot]customization;}接着需要把新实例化的部件的骨骼去重定向到模板骨骼上思路就是循环新部件的bones, 挨个在模板骨骼上查找同名的骨骼保存, 最后统一替换// 骨骼重定向 将实例化的SkinnedMeshRenderer中的bones逐一重定向到当前对象的根骨骼下publicvoidRedirectBones(SkinnedMeshRenderer[]renderers){if(renderersnull||renderers.Length0)return;foreach(SkinnedMeshRendererrendererinrenderers){Transform[]bonesrenderer.bones;// 部件骨骼Transformrootrenderer.rootBone;// 部件根骨骼Transform[]redirectBonesrootBone.GetComponentsInChildrenTransform();Transform[]newBonesnewTransform[bones.Length];TransformnewRootBone;foreach(Transformboneinbones){if(bonenull)continue;intindexArray.IndexOf(bones,bone);newBones[index]Array.Find(redirectBones,xx.namebone.name);}newRootBoneArray.Find(redirectBones,xx.nameroot.name);renderer.bonesnewBones;renderer.rootBonenewRootBone;}}这些操作下来就可以拼接好一个默认的人物模型了( 其实此时把拼接完的模型保存为预制体也是个方法)手套, 鞋子这种多个部分的, 就是一个fbx但是包含多个SkinnedMeshRenderer对象然后替换部件就很简单了, 调用ReplaceCustomization就可以了会出现的问题是, 人物身体和头部衔接的地方会有缝隙, 需要检查下部件切口的顶点数量和权重是否一致(目前知不道咋解决的, 缝隙莫明就消失了)如果项目需要还可以再确定装扮后进行贴图合并, 可以避免动作中出现接缝, 减少drawcall但是如果更换频率较大 且 drawcall影响忽略不计就不必合并了

更多文章