unity3d 模型换装系统

现在公司的游戏虽然也实现了换装,但是存在一定的问题。我们项目中的换装系统的实现是这样的:一套没有蒙皮信息的人物空骨骼模型(虚拟体),头、身体、武器是独立于人物骨骼的具有蒙皮信息的模型。换装时将模型置为“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 _boneDic = new Dictionary();
    private Dictionary targetSmr = new Dictionary();

    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 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();
        }

        RemoveMeshPart(partName);

        SkinnedMeshRenderer[] meshRenders = pMeshs.GetComponentsInChildren();
        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();

            List tempBones = new List();
            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 


我的模型都放在Resources下的Actor文件夹,大家可以根据自己的文件路径重写文件路径即可,呵呵。这只是一个小小的验证demo。

你可能感兴趣的:(Unity,C#,Unity,游戏开发)