Unity3d 简单的Avatar换装系统
换装所涉及的其实就是SkinnedMeshRenderer,它包含了网格,骨骼,材质;首先,需要一个源Model,它包含了换装所需要的所有东西。带Bip字眼的是骨骼;然后需要就是我们的目标,只有骨骼;换装的过程就是将Source中对应的SkinnedMeshRenderer信息复制到Target上,这里的信息就是网格,骨骼,材质;下面,一步步来:1、将Source实例化到场景当中,保存他的T
换装所涉及的其实就是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
更多推荐
所有评论(0)