Unity3D角色换装及换装编辑器

来源:互联网 发布:excel怎么清除重复数据 编辑:程序博客网 时间:2024/04/27 14:33

Unity换装系统 第一篇

Darren原创.欢迎转载,转载请注明出处.

这几天在研究换装系统,现在将研究过程中的思路记录下来。
换装系统本人分两步来实现:
1本地换装以及换装编辑器
2换装资源规范制定,以及通过读取网络资源实现换装

今天先记录本地换装,换装的步骤为:

1.创建主骨架。
主骨架有两个方法获取到:
第一个方法是美术直接输出不带蒙皮的骨骼信息
第二个方法是美术输出带蒙皮的模型,将模型所有带SkinnedMeshRenderer的gameobject删掉,剩下的就是主骨架

 _go = Instantiate(prefab) as GameObject;foreach (SkinnedMeshRenderer item in _go.GetComponentsInChildren<SkinnedMeshRenderer>()){      Object.DestroyImmediate(item.gameObject);}

2.记录需要换装的部件中的所有 SkinnedMeshRenderer中的蒙皮信息,具体需要记录SkinnedMeshRenderer.bones,SkinnedMeshRenderer.sharedMaterials以及SkinnedMeshRenderer.sharedMesh

 public struct AvtarInfo {     public Dictionary<string, SkinMeshInfo> skmr; }private void _GetSkinMeshInfo(GameObject prefab, AvtarInfo avtarinfo){        foreach (SkinnedMeshRenderer item in prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true))        {            List<string> bonesName = new List<string>();            for (int i = 0; i < item.bones.Length; i++)            {                bonesName.Add(item.bones[i].name);            }            SkinMeshInfo info = new SkinMeshInfo();            info.mesh = item.sharedMesh;            info.materials = new List<Material>();            info.bonesName = bonesName;            for (int i = 0; i < item.sharedMaterials.Length; i++)            {                info.materials.Add(Instantiate(item.sharedMaterials[i]) as Material);            }            avtarinfo.skmr.Add(item.name, info);        }}

3。将所有换装的Mesh合并成一个Mesh,将所有骨骼合并成一个Transform数组,将所有部件中的materials
合并层一个合计。并将这些数据赋予主骨架物体上的SkinnedMeshRenderer上对应的成员变量

 public struct SkinMeshInfo {    public List<Material> materials;    public Mesh mesh;    public List<string> bonesName; } public GameObject Generate(GameObject root, Dictionary<string, SkinMeshInfo> _skmr) {        float startTime = Time.realtimeSinceStartup;        List<CombineInstance> combineInstances = new List<CombineInstance>();        List<Material> materials = new List<Material>();        List<Transform> bones = new List<Transform>();        Transform[] transforms = root.GetComponentsInChildren<Transform>();        foreach (var item in _skmr)        {            for (int sub = 0; sub < item.Value.mesh.subMeshCount; sub++)            {                CombineInstance ci = new CombineInstance();                ci.mesh = item.Value.mesh;                ci.subMeshIndex = sub;                combineInstances.Add(ci);            }            foreach (string bone in item.Value.bonesName)            {                foreach (Transform transform in transforms)                {                    if (transform.name != bone) continue;                    bones.Add(transform);                    break;                }            }            materials.AddRange(item.Value.materials);        }        SkinnedMeshRenderer r = root.GetComponent<SkinnedMeshRenderer>();        r.sharedMesh = new Mesh();        r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);        r.bones = bones.ToArray();        r.materials = materials.ToArray();        return root; }

整体的流程为:加载主骨架->读取要替换的资源中的所有蒙皮信息并存储->生成新的Mesh,将Mesh与子物体中所有的bones,materials赋予父物体的SkinnedMeshRenderer中对应的成员。

通过这种方法实现换装需要将资源拆分,在编辑器中没办法直接看到合并之后的效果,所以我做了一个编辑器,可以在非运行状态直接看最终合成之后的效果,下面贴上代码:

using UnityEngine;using System.Collections;using System.Collections.Generic;using UnityEditor;public class AvtarEditorWindow : EditorWindow{    private List<AvtarInstanceInfo> _avtarObjects = new List<AvtarInstanceInfo>();    private GameObject _skeleton;    private GameObject _body;    private GameObject _wing;    private GameObject _equip;    private int _equipBoneCount = 0;    public Dictionary<int, string> _equipBlindBoneNames = new Dictionary<int, string>();    private int _avtarCount = 0;    private const string AVTAR_SKELETON = "AVTAR_SKELETON";    private const string AVTAR_BODY = "AVTAR_BODY";    private const string AVTAR_WING = "AVTAR_WING";    private const string AVTAR_EQUIP = "AVTAR_EQUIP";    public struct AvtarInstanceInfo    {        public GameObject go;        public AvtarInfo info;    }    public struct AvtarInfo    {        public Dictionary<string, SkinMeshInfo> skmr;    }    public struct SkinMeshInfo    {        public List<Material> materials;        public Mesh mesh;        public List<string> bonesName;    }    [MenuItem("Tool/Open Avtar EditWindow")]    public static void OpenAvtarWindow()    {        AvtarEditorWindow window = (AvtarEditorWindow)EditorWindow.GetWindow(typeof(AvtarEditorWindow));        window.Show();    }    void OnEnable()    {        _avtarObjects = new List<AvtarInstanceInfo>();        try        {            Debug.Log(EditorPrefs.GetString(AVTAR_SKELETON).Replace(Application.dataPath, "Assets"));            _skeleton = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString(AVTAR_SKELETON).Replace(Application.dataPath, "Assets"), typeof(GameObject)) as GameObject;            _body = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString(AVTAR_BODY).Replace(Application.dataPath, "Assets"), typeof(GameObject)) as GameObject;            _wing = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString(AVTAR_WING).Replace(Application.dataPath, "Assets"), typeof(GameObject)) as GameObject;            _equip = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString(AVTAR_EQUIP).Replace(Application.dataPath, "Assets"), typeof(GameObject)) as GameObject;        }        catch        {            Debug.Log("获取资源出错");        }    }    void OnDestroy()    {        _avtarCount = 0;        if (_avtarObjects.Count > 0)        {            for (int i = 0; i < _avtarObjects.Count; i++)            {                if (_avtarObjects[i].go)                {                    DestroyImmediate(_avtarObjects[i].go);                }            }            _avtarObjects = null;        }        if (_skeleton)            EditorPrefs.SetString(AVTAR_SKELETON, AssetDatabase.GetAssetPath(_skeleton.GetInstanceID()));        if (_body)            EditorPrefs.SetString(AVTAR_BODY, AssetDatabase.GetAssetPath(_body.GetInstanceID()));        if (_wing)            EditorPrefs.SetString(AVTAR_WING, AssetDatabase.GetAssetPath(_wing.GetInstanceID()));        if (_equip)            EditorPrefs.SetString(AVTAR_EQUIP, AssetDatabase.GetAssetPath(_equip.GetInstanceID()));    }    void OnGUI()    {        _skeleton = EditorGUILayout.ObjectField(new GUIContent("主骨架"), _skeleton, typeof(GameObject)) as GameObject;        _body = EditorGUILayout.ObjectField(new GUIContent("身体蒙皮"), _body, typeof(GameObject)) as GameObject;        _wing = EditorGUILayout.ObjectField(new GUIContent("翅膀蒙皮"), _wing, typeof(GameObject)) as GameObject;        _equip = EditorGUILayout.ObjectField(new GUIContent("武器"), _equip, typeof(GameObject)) as GameObject;        _equipBoneCount = EditorGUILayout.IntField("武器骨骼绑点数量:", _equipBoneCount);        if (_equipBoneCount > 0)        {            for (int i = 0; i < _equipBoneCount; i++)            {                if (!_equipBlindBoneNames.ContainsKey(i))                {                    _equipBlindBoneNames.Add(i, "");                }                _equipBlindBoneNames[i] = EditorGUILayout.TextField(string.Format("骨骼名称{0}:", i.ToString()), _equipBlindBoneNames[i]);            }        }        if (GUILayout.Button("Create"))        {            _CreateAvtar();        }        if (_avtarObjects.Count > 0)        {            for (int i = 0; i < _avtarObjects.Count; i++)            {                if (_avtarObjects[i].go != null && GUILayout.Button(_avtarObjects[i].go.name))                {                    DestroyImmediate(_avtarObjects[i].go);                    _avtarObjects.RemoveAt(i);                    break;                }            }        }    }    private void _CreateAvtar()    {        if (_skeleton == null)        {            Debug.Log("主骨架为空,无法生成模型");            return;        }        _avtarCount++;        AvtarInstanceInfo avtarInfo = new AvtarInstanceInfo();        avtarInfo.info = new AvtarInfo();        avtarInfo.info.skmr = new Dictionary<string, SkinMeshInfo>();        GameObject temp = Instantiate(_skeleton, Vector3.zero, new Quaternion(0, 180, 0, 1)) as GameObject;        temp.AddComponent<SkinnedMeshRenderer>();        if (_body) _GetSkinMeshInfo(_body, avtarInfo.info);        if (_wing) _GetSkinMeshInfo(_wing, avtarInfo.info);        avtarInfo.go = Generate(temp, avtarInfo.info.skmr);        avtarInfo.go.name += "_" + _avtarCount;        MountEquip(avtarInfo.go);        _avtarObjects.Add(avtarInfo);    }    private void _GetSkinMeshInfo(GameObject prefab, AvtarInfo avtarinfo)    {        foreach (SkinnedMeshRenderer item in prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true))        {            List<string> bonesName = new List<string>();            for (int i = 0; i < item.bones.Length; i++)            {                bonesName.Add(item.bones[i].name);            }            SkinMeshInfo info = new SkinMeshInfo();            info.mesh = item.sharedMesh;            info.materials = new List<Material>();            info.bonesName = bonesName;            for (int i = 0; i < item.sharedMaterials.Length; i++)            {                info.materials.Add(Instantiate(item.sharedMaterials[i]) as Material);            }            avtarinfo.skmr.Add(item.name, info);        }    }    public GameObject Generate(GameObject root, Dictionary<string, SkinMeshInfo> _skmr)    {        float startTime = Time.realtimeSinceStartup;        List<CombineInstance> combineInstances = new List<CombineInstance>();        List<Material> materials = new List<Material>();        List<Transform> bones = new List<Transform>();        Transform[] transforms = root.GetComponentsInChildren<Transform>();        foreach (var item in _skmr)        {            for (int sub = 0; sub < item.Value.mesh.subMeshCount; sub++)            {                CombineInstance ci = new CombineInstance();                ci.mesh = item.Value.mesh;                ci.subMeshIndex = sub;                combineInstances.Add(ci);            }            foreach (string bone in item.Value.bonesName)            {                foreach (Transform transform in transforms)                {                    if (transform.name != bone) continue;                    bones.Add(transform);                    break;                }            }            materials.AddRange(item.Value.materials);        }        SkinnedMeshRenderer r = root.GetComponent<SkinnedMeshRenderer>();        r.sharedMesh = new Mesh();        r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);        r.bones = bones.ToArray();        r.materials = materials.ToArray();        return root;    }    public void MountEquip(GameObject root)    {        Transform[] transforms = root.GetComponentsInChildren<Transform>(true);        for (int i = 0; i < _equipBoneCount; i++)        {            for (int j = 0; j < transforms.Length; j++)            {                if (transforms[j].name == _equipBlindBoneNames[i])                {                    GameObject equip = Instantiate(_equip) as GameObject;                    equip.transform.parent = transforms[j];                    equip.transform.localPosition = Vector3.zero;                    equip.transform.localRotation = Quaternion.identity;                    equip.transform.localScale = Vector3.one;                    break;                }                if (j == transforms.Length - 1)                {                    Debug.Log("找不到骨骼" + _equipBlindBoneNames[i]);                }            }        }    }}

代码中使用了两种换装方式,一般有带蒙皮信息的部位换装需要合并网格,骨骼,材质球。
不带蒙皮信息的如代码中的武器,就直接将武器挂到挂点上并且初始化位置即可。

感觉整篇写下来表达的还是不够清晰,如果有意见的欢迎找我交流~~

0 0
原创粉丝点击