您的位置:首页 > 移动开发 > Unity3D

Unity SkinnedMesh 换装

2016-10-20 17:49 567 查看
Unity SkinnedMesh : 

游戏中常见的换装做法 : 

一、直接更换贴图

二、更换静态Mesh

三、更换SkinnedMesh

前两者简单,今天需要mark下SkinnedMesh。

1.首先可以了解下SkinnedMesh的基础概念 : http://blog.csdn.net/gamemonkey/article/details/44058291 这篇文章对于入门的同学很不错。

2.Untiy中合并Mesh主要使用到了CombineMeshes接口,接下来就是可以参考Unity文档 : 
https://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html
3.再来就是实例,可以在AssetsStore中搜索Skinned Mesh官方示例 :

Character Customization

注意:

a.Unity5版本以后,代码和AseetBundle有不兼容的的地方,这个我有做修改。

b.AssetBundle需要重新生成。

运行:

打开Scene:CharacterCustomization / DressingroomExample.scene

AssetBundle生成方式:

①.选中Project视图->Assets->CharacterCustomization文件夹。

②.工具栏->Character Generator->CreateAssetbundles。

4..这个工程是采用assetbundle为基础写的。所以对于部分基友们可能会造成困扰,我们需要的其实紧紧是了解SkinnedMesh实现方式,所以我写了一个直接只用Prefab的小用例在里面,这样就只使用到了下面两个C#文件和它的资源,其余无关的逻辑和框架统统可以摒弃。

运行:

打开Scene:CharacterCustomization / SkinMesh.scene

Prefab生成方式:

①.选中Project视图->Assets->CharacterCustomization文件夹。

②.工具栏->Character Generator->CreatePrefabs。

修改后的Unity工程可以去这里下载:http://download.csdn.net/detail/zhousanxi123/9659635

以下是Prefab使用SkinnedMesh的两个主要文件 :

using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Xml;

class CreatePrefabs
{
//创建Prefab
[MenuItem("Character Generator/Create Prefabs")]
static void Execute()
{
bool createdPrefabs = false;
foreach (Object o in Selection.GetFiltered(typeof (Object), SelectionMode.DeepAssets))
{
//1.搜索CharacterCustomization文件下的非动画模型文件
if (!(o is GameObject)) continue;
if (o.name.Contains("@")) continue;
if (!AssetDatabase.GetAssetPath(o).Contains("/characters/")) continue;

//2.从资源创建对应的非动画模型实例
GameObject characterFBX = (GameObject)o;
string name = characterFBX.name.ToLower();

Debug.Log("******* Creating prefab for: " + name + " *******");

// Create a directory to store the generated prefabs.
if (!Directory.Exists(PrefabsPath))
Directory.CreateDirectory(PrefabsPath);

// Delete existing prefabs for current character.
string[] existingPrefabs = Directory.GetFiles(PrefabsPath);
foreach (string prefab in existingPrefabs)
{
if (prefab.EndsWith(".prefab") && prefab.Contains("/Prefabs/" + name))
File.Delete(prefab);
}

GameObject characterClone = Object.Instantiate(characterFBX);

// postprocess animations: we need them animating even offscreen
foreach (Animation anim in characterClone.GetComponentsInChildren())
anim.cullingType = AnimationCullingType.AlwaysAnimate;

//删除克隆对象中的skinnedmesh对象
foreach (SkinnedMeshRenderer smr in characterClone.GetComponentsInChildren())
Object.DestroyImmediate(smr.gameObject);

//给克隆对象添加SkinnedMesh组件
characterClone.AddComponent();
//创建克隆对象的Prefab
Object characterBasePrefab = GetPrefab(characterClone, name);

//3.为每个SkinnedMesh创建Prefab
foreach (SkinnedMeshRenderer smr in characterFBX.GetComponentsInChildren(true))
{
//SkinnedMesh创建Prefab
GameObject rendererClone = (GameObject)PrefabUtility.InstantiatePrefab(smr.gameObject);
GameObject rendererParent = rendererClone.transform.parent.gameObject;
rendererClone.transform.parent = null;
Object.DestroyImmediate(rendererParent);
Object rendererPrefab = GetPrefab(rendererClone, name + "_" + smr.gameObject.name);

//获取骨骼信息存到xml
XmlDocument xml = new XmlDocument();
var root = xml.CreateElement("root");
xml.AppendChild(root);
List bonesList = new List();
foreach (Transform t in smr.bones)
root.AppendChild(xml.CreateElement(t.name));

xml.Save("Assets/Resources/" + name + "_" + smr.gameObject.name + ".xml");

createdPrefabs = true;
}
}

if (createdPrefabs)
Debug.Log("Success");
else
EditorUtility.DisplayDialog("Character Generator", "No Prefabs created. Select the characters folder in the Project pane to process all characters. Select subfolders to process specific characters.", "Ok");
}

static Object GetPrefab(GameObject go, string name)
{
Object tempPrefab = PrefabUtility.CreateEmptyPrefab("Assets/Resources/Prefabs/" + name + ".prefab");
tempPrefab = PrefabUtility.ReplacePrefab(go, tempPrefab);
Object.DestroyImmediate(go);
return tempPrefab;
}

public static string PrefabsPath
{
get { return "Assets" + Path.DirectorySeparatorChar + "Resources" + Path.DirectorySeparatorChar + "Prefabs" + Path.DirectorySeparatorChar; }
}
}using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.IO;
using UnityEngine.UI;

//合并skinnedMesh步骤
//1.从Prefab创一个无子mesh模型
//2.创建 CombineInstances集合(融合实例) | Materials集合(材质) | 创建一个Bone_Transforms结合(骨骼矩阵)用于融合准备
//3.从Prefab创建各个SkinnedMesh模型
//4.给对应的SkinnedMesh赋予Material,并将Material加入Materials集合
//5.根据SkinnedMesh创建CombineInstance,并将CombineInstance加入CombineInstances集合
//6.从无子mesh的模型获取整体Transform,再根据各个SkinnedMesh和骨骼信息,将对应的Transform加入Bone_Transforms集合
//7.在无子mesh下调用CombineMeshes接口,将三个集合信息设置好开始合并。

public class Main_SkinMesh : MonoBehaviour {

public Button Btn_Change;
GameObject Root;
string []change_string = new string[6];

// Use this for initialization
void Start () {
Generate();

Btn_Change.onClick.AddListener(OnClick_Change);
}

// Update is called once per frame
void Update () {

}

//切换模型组件并创建.
public void OnClick_Change()
{
change_
4000
string[0] = change_string[0] == "female_eyes" ? "female_eyes" : "female_eyes";
change_string[1] = change_string[1] == "female_face-1" ? "female_face-2" : "female_face-1";
change_string[2] = change_string[2] == "female_hair-1" ? "female_hair-2" : "female_hair-1";
change_string[3] = change_string[3] == "female_pants-1" ? "female_pants-2" : "female_pants-1";
change_string[4] = change_string[4] == "female_shoes-1" ? "female_shoes-2" : "female_shoes-1";
change_string[5] = change_string[5] == "female_top-1" ? "female_top-2" : "female_top-1";

List constant = new List();
constant.AddRange(change_string);
Generate(Root, constant);
}

public void Generate()
{
//合并skinnedMesh步骤1
Root = Resources.Load("Prefabs/female");
Root = Instantiate(Root) as GameObject;

OnClick_Change();
}

public GameObject Generate(GameObject root, List constant)
{
float startTime = Time.realtimeSinceStartup;

// The SkinnedMeshRenderers that will make up a character will be
// combined into one SkinnedMeshRenderers using multiple materials.
// This will speed up rendering the resulting character.
//合并skinnedMesh步骤2
List combineInstances = new List();
List materials = new List();
List bones = new List();
Transform[] transforms = root.GetComponentsInChildren();

foreach (var res in constant)
{
//合并skinnedMesh步骤3
GameObject element = Resources.Load("Prefabs/" + res);
element = Instantiate(element) as GameObject;
SkinnedMeshRenderer smr = element.GetComponent() as SkinnedMeshRenderer;

//合并skinnedMesh步骤4
string mat_res = "Per Texture Materials/";
string[] existingMaterials = Directory.GetFiles("Assets/Resources/Per Texture Materials/");
foreach (string matfile in existingMaterials)
{
if (matfile.EndsWith(".mat") && matfile.Contains(res))
{
mat_res += matfile.Substring(matfile.LastIndexOf('/') + 1);
break;
}
}
mat_res = mat_res.Substring(0, mat_res.LastIndexOf('.'));
smr.material = Resources.Load(mat_res);
materials.AddRange(smr.materials);

//合并skinnedMesh步骤5
for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
{
CombineInstance ci = new CombineInstance();
ci.mesh = smr.sharedMesh;
ci.subMeshIndex = sub;
combineInstances.Add(ci);
}

//合并skinnedMesh步骤6
XmlDocument xml = new XmlDocument();
xml.Load("Assets/Resources/" + res + ".xml");
var Root = xml.FirstChild;
var bonelist = Root.ChildNodes;

foreach (XmlNode bone in bonelist)
{
foreach (Transform transform in transforms)
{
if (transform.name == bone.Name)
{
bones.Add(transform);
break;
}
}
}

Object.Destroy(smr.gameObject);
}

// Obtain and configure the SkinnedMeshRenderer attached to
// the character base.
//合并skinnedMesh步骤7
SkinnedMeshRenderer r = root.GetComponent();
r.sharedMesh = new Mesh();
r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);
r.bones = bones.ToArray();
r.materials = materials.ToArray();

Debug.Log("Generating character took: " + (Time.realtimeSinceStartup - startTime) * 1000 + " ms");
return root;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unity SkinnedMesh