换装系列文章:
换装原理初探
换装原理中篇
换装原理实践
换装英文中称为Character Customization,在游戏中很常见。换装的基本步骤如下。
- MaleModel预设制作,此模型包括所有样式的SkinnedMeshRenderer和骨骼,并以一定规则命名,比如eye有三种,则命名为eye-1,eye-2,eye-3。
- MaleTarget预设制作,此模型只包含骨骼。
- 加载MaleModel和MaleTarget。
- 保存数据,将MaleModel中的所有样式SkinnedMeshRenderer组件保存在字典中maleData,将MaleTarget中的骨骼保存在数组maleHips中,为MaleTarget添加所有身体部件,例如eye、body,并将此保存到另一个字典中targetData。
- 换装,在给定身体部件名称和序号的情况下,从maleData数组中找到相应的SkinnedMeshRenderer组件part,从中提取骨骼信息,遍历骨骼,在MaleTarget中的骨骼中找到对应骨骼,并保存为数组bones。将MaleTarget相应的SkinnedMeshRenderer组件中的骨骼绑定为bones,网格替换为part的网格,材质替换为part的材质。
最后,给出相应的代码如下。
using System.Collections.Generic;
using UnityEngine;
public class AvataCustomization : MonoBehaviour
{
private Transform maleModelTrans;
private Transform maleTargetTrans;
private Dictionary<string, Dictionary<string, SkinnedMeshRenderer>> maleData = new Dictionary<string, Dictionary<string, SkinnedMeshRenderer>>();
private Dictionary<string, SkinnedMeshRenderer> targetData = new Dictionary<string, SkinnedMeshRenderer>();
private Transform[] maleHips;
private void Start()
{
InitiateModel();
InitiateTarget();
SaveData();
}
void InitiateModel()
{
GameObject maleModel = GameObject.Instantiate(Resources.Load("MaleModel")) as GameObject;
maleModelTrans = maleModel.transform;
maleModel.SetActive(false);
}
void InitiateTarget()
{
GameObject maleTarget = GameObject.Instantiate(Resources.Load("MaleTarget")) as GameObject;
maleTargetTrans = maleTarget.transform;
}
void SaveData()
{
SkinnedMeshRenderer[] parts = maleModelTrans.GetComponentsInChildren<SkinnedMeshRenderer>();
maleHips = maleTargetTrans.GetComponentsInChildren<Transform>();
foreach (SkinnedMeshRenderer part in parts)
{
string[] names = part.name.Split('-');
if(!maleData.ContainsKey(names[0]))
{
GameObject partGo = new GameObject();
partGo.name = names[0];
partGo.transform.SetParent(maleTargetTrans);
maleData.Add(names[0], new Dictionary<string, SkinnedMeshRenderer>());
targetData.Add(names[0], partGo.AddComponent<SkinnedMeshRenderer>());
}
maleData[names[0]].Add(names[1], part);
}
}
void ChangeStyle(string partName, string num)
{
SkinnedMeshRenderer part = maleData[partName][num];
List<Transform> bones = new List<Transform>();
foreach(var trans in part.bones)
{
foreach(var bone in maleHips)
{
if(bone.name == trans.name)
{
bones.Add(bone);
break;
}
}
}
targetData[partName].bones = bones.ToArray();
targetData[partName].sharedMesh = part.sharedMesh;
targetData[partName].material = part.material;
}
}