现在公司的游戏虽然也实现了换装,但是存在一定的问题。我们项目中的换装系统的实现是这样的:一套没有蒙皮信息的人物空骨骼模型(虚拟体),头、身体、武器是独立于人物骨骼的具有蒙皮信息的模型。换装时将模型置为“T-pos”状态,删除需要身体上对应的meshpart, 然后将新的模块模型拷贝到角色骨骼下面,将蒙皮所依赖的骨骼根据名称拷贝到人物模型对应的骨骼下面。这样虽然也实现了换装,但却在人物骨骼中多添加了许多骨骼,造成资源的浪费,而且每次换装都会使模型动画置到默认状态下。
一直想完善现在项目的化妆系统,这个周末终于有时间了,于是利用周天重写了一个简单的换装系统,以此验证一下我设想的换装是否可行。如果可行,便整合到目前的项目中。
其实原理很简单,提取换装模型的蒙皮信息,在人物骨骼中按照提取出来的蒙皮信息重新实现一遍。
说了一大堆废话,还是直接上代码吧:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ActorChangeMeshPart : MonoBehaviour {
private Transform source;
private Transform target;
private GameObject sourceobj;
private GameObject targetobj;
private Dictionary<string, Transform> _boneDic = new Dictionary<string, Transform>();
private Dictionary<string, SkinnedMeshRenderer[]> targetSmr = new Dictionary<string, SkinnedMeshRenderer[]>();
private Animator animator;
private AnimationClip mClip;
public static ActorChangeMeshPart instance;
// Use this for initialization
void Start () {
instance = this;
InstantiateSkeleton();
InitAvatar();
}
// Update is called once per frame
void Update () {
}
void InstantiateSkeleton()
{
_boneDic.Clear();
BuildBoneMap(transform, _boneDic);
}
public void BuildBoneMap(Transform parent, Dictionary<string, Transform> map)
{
try
{
for(int i = 0; i< parent.childCount; i++)
{
Transform child = parent.GetChild(i);
if (child == null)
continue;
map.Add(child.name, child);
BuildBoneMap(child, map);
}
}
catch (System.Exception ex)
{
Debug.LogWarning("already has bone " + parent.name);
}
}
public Transform GetBone(string name)
{
if (!_boneDic.ContainsKey(name))
return null;
return _boneDic[name];
}
public void ChangePart(string partName, string resPath)
{
if(string.IsNullOrEmpty(resPath))
{
RemoveMeshPart(partName);
return;
}
string path = string.Format("Actor/{0}", resPath);
GameObject objMeshPart = Resources.Load(path) as GameObject;
if (objMeshPart == null)
return;
GameObject pMeshs = GameObject.Instantiate(objMeshPart, Vector3.zero, Quaternion.identity) as GameObject;
SetMeshPart(partName, pMeshs);
GameObject.DestroyImmediate(pMeshs);
}
public void SetMeshPart(string partName, GameObject pMeshs)
{
if (targetSmr == null)
{
targetSmr = new Dictionary<string, SkinnedMeshRenderer[]>();
}
RemoveMeshPart(partName);
SkinnedMeshRenderer[] meshRenders = pMeshs.GetComponentsInChildren<SkinnedMeshRenderer>();
if (meshRenders.Length == 0)
{
Debug.LogWarning("************** not has skinnedMeshrenders ");
return;
}
SkinnedMeshRenderer[] newMeshRenders = new SkinnedMeshRenderer[meshRenders.Length];
for (int i = 0; i < meshRenders.Length; i++)
{
GameObject meshObj = new GameObject();
meshObj.name = meshRenders[i].name;
meshObj.transform.parent = transform;
meshObj.transform.localPosition = meshRenders[i].transform.localPosition;
meshObj.transform.localRotation = meshRenders[i].transform.localRotation;
meshObj.transform.localScale = meshRenders[i].transform.localScale;
SkinnedMeshRenderer pSkine = meshObj.AddComponent<SkinnedMeshRenderer>();
List<Transform> tempBones = new List<Transform>();
foreach (Transform bone in meshRenders[i].bones)
{
if(GetBone(bone.name))
{
tempBones.Add(GetBone(bone.name));
}
}
pSkine.sharedMesh = meshRenders[i].sharedMesh;
pSkine.bones = tempBones.ToArray();
pSkine.material = meshRenders[i].material;
if (GetBone(meshRenders[i].rootBone.name) == null)
{
Debug.Log("rootbone name is "+ meshRenders[i].rootBone.name);
}
pSkine.rootBone = GetBone(meshRenders[i].rootBone.name);
pSkine.localBounds = meshRenders[i].localBounds;
newMeshRenders[i] = pSkine;
}
targetSmr.Add(partName, newMeshRenders);
}
public SkinnedMeshRenderer[] GetMeshPart(string partName)
{
try
{
return targetSmr[partName];
}
catch (System.Exception ex)
{
return null;
}
}
public void RemoveMeshPart(string partName)
{
SkinnedMeshRenderer[] parts = GetMeshPart(partName);
if(parts == null)
return;
for(int i = 0;i <parts.Length; i++)
{
GameObject.DestroyImmediate(parts[i].gameObject);
}
targetSmr.Remove(partName);
}
void InitAvatar()
{
ChangePart("head", "X_JianNan/SkinnedMesh/T_01");
ChangePart("body", "X_JianNan/SkinnedMesh/Y_01");
ChangePart("weapon", "X_JianNan/SkinnedMesh/W_01");
}
void OnGUI()
{
// 文本显示
GUI.Label(new Rect(50, 20, 200, 50), "换装系统");
// 头换模型
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 50, 200, 30), "ChangeHead1"))
{
ChangePart("head", "X_JianNan/SkinnedMesh/T_01");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 80, 200, 30), "ChangeHead2"))
{
ChangePart("head", "X_JianNan/SkinnedMesh/T_02");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 110, 200, 30), "ChangeHead3"))
{
ChangePart("head", "X_JianNan/SkinnedMesh/T_03");
}
// 身体换模型
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 140, 200, 30), "ChangeBody1"))
{
ChangePart("body", "X_JianNan/SkinnedMesh/Y_01");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 170, 200, 30), "ChangeBody2"))
{
ChangePart("body", "X_JianNan/SkinnedMesh/Y_02");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 200, 200, 30), "ChangeBody3"))
{
ChangePart("body", "X_JianNan/SkinnedMesh/Y_03");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 230, 200, 30), "ChangeBody4"))
{
ChangePart("body", "X_JianNan/SkinnedMesh/Y_04");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 260, 200, 30), "ChangeBody5"))
{
ChangePart("body", "X_JianNan/SkinnedMesh/Y_05");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 290, 200, 30), "ChangeBody6"))
{
ChangePart("body", "X_JianNan/SkinnedMesh/Y_06");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 320, 200, 30), "ChangeBody7"))
{
ChangePart("body", "X_JianNan/SkinnedMesh/Y_07");
}
// 武器模型
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 350, 200, 30), "ChangeWeapon1"))
{
ChangePart("weapon", "X_JianNan/SkinnedMesh/W_01");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 380, 200, 30), "ChangeWeapon2"))
{
ChangePart("weapon", "X_JianNan/SkinnedMesh/W_02");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 410, 200, 30), "ChangeWeapon3"))
{
ChangePart("weapon", "X_JianNan/SkinnedMesh/W_03");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 440, 200, 30), "ChangeWeapon4"))
{
ChangePart("weapon", "X_JianNan/SkinnedMesh/W_04");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 470, 200, 30), "ChangeWeapon5"))
{
ChangePart("weapon", "X_JianNan/SkinnedMesh/W_05");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 500, 200, 30), "ChangeWeapon5"))
{
ChangePart("weapon", "X_JianNan/SkinnedMesh/W_06");
}
GUI.color = Color.yellow;
GUI.backgroundColor = Color.red;
if (GUI.Button(new Rect(50, 530, 200, 30), "ChangeWeapon7"))
{
ChangePart("weapon", "X_JianNan/SkinnedMesh/W_07");
}
}
}
我的模型都放在Resources下的Actor文件夹,大家可以根据自己的文件路径重写文件路径即可,呵呵。这只是一个小小的验证demo。
版权声明:本文为jinsenianhua2012原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。