Unity3d官⽅⾓⾊换装Demo源代码学习
由于兴趣爱好,⼯作之余会尝试做做RPG游戏,遇到⾓⾊换装这个功能没什么思路,于是在⽹上苦苦寻相关资料,⽹上⾓⾊换装⽂章还是有⼀些,不过⼤多讲的⽐较简单,最后没办法不得不拿官⽅的⾓⾊换装代码来研究,开始看到⼀头雾⽔,不过慢慢到看这个Demo代码的思路,终于理清了换装的思路。
其实,可以从⾓⾊换装资源打包代码看起,然后看换装代码。
以下是我看代码的思路:CreateAssetbundles.cs -> Main.cs
换装的核⼼代码如下:
(1)打包代码 CreateAssetbundles.cs
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
class CreateAssetbundles
unity3d animation{
// This method creates an assetbundle of each SkinnedMeshRenderer
// found in any selected character fbx, and adds any materials that
// are intended to be used by the specific SkinnedMeshRenderer.
[MenuItem("Character Generator/Create Assetbundles")]
static void Execute()
{
bool createdBundle = false;
foreach (Object o in Selection.GetFiltered(typeof (Object), SelectionMode.DeepAssets))
{
if (!(o is GameObject)) continue;
if (o.name.Contains("@")) continue;
if (!AssetDatabase.GetAssetPath(o).Contains("/characters/")) continue;
GameObject characterFBX = (GameObject)o;
string name = characterFBX.name.ToLower();
Debug.Log("******* Creating assetbundles for: " + name + " *******");
// Create a directory to store the generated assetbundles.
if (!Directory.Exists(AssetbundlePath))
Directory.CreateDirectory(AssetbundlePath);
// Delete existing assetbundles for current character.
string[] existingAssetbundles = Directory.GetFiles(AssetbundlePath);
foreach (string bundle in existingAssetbundles)
{
if (bundle.EndsWith(".assetbundle") && bundle.Contains("/assetbundles/" + name))
File.Delete(bundle);
}
// Save bones and animations to a seperate assetbundle. Any
// possible combination of CharacterElements will use these
// assets as a base. As we can not edit assets we instantiate
// the fbx and remove what we dont need. As only assets can be
// added to assetbundles we save the result as a prefab and delete
/
/ it as soon as the assetbundle is created.
GameObject characterClone = (GameObject)Object.Instantiate(characterFBX);
// postprocess animations: we need them animating even offscreen
foreach (Animation anim in characterClone.GetComponentsInChildren<Animation>())
anim.animateOnlyIfVisible = false;
foreach (SkinnedMeshRenderer smr in characterClone.GetComponentsInChildren<SkinnedMeshRenderer>())
Object.DestroyImmediate(smr.gameObject);
characterClone.AddComponent<SkinnedMeshRenderer>();
Object characterBasePrefab = GetPrefab(characterClone, "characterbase");
string path = AssetbundlePath + name + "_characterbase.assetbundle";
BuildPipeline.BuildAssetBundle(characterBasePrefab, null, path, BuildAssetBundleOptions.CollectDependencies);
AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(characterBasePrefab));
// Collect materials.
List<Material> materials = EditorHelpers.CollectAll<Material>(GenerateMaterials.MaterialsPath(characterFBX));
// Create assetbundles for each SkinnedMeshRenderer.
foreach (SkinnedMeshRenderer smr in characterFBX.GetComponentsInChildren<SkinnedMeshRenderer>(true))
{
List<Object> toinclude = new List<Object>();
// Save the current SkinnedMeshRenderer as a prefab so it can be included
/
/ in the assetbundle. As instantiating part of an fbx results in the
// entire fbx being instantiated, we have to dispose of the entire instance
// after we detach the SkinnedMeshRenderer in question.
GameObject rendererClone = (GameObject)EditorUtility.InstantiatePrefab(smr.gameObject);
GameObject rendererParent = ansform.parent.gameObject;
Object.DestroyImmediate(rendererParent);
Object rendererPrefab = GetPrefab(rendererClone, "rendererobject");
toinclude.Add(rendererPrefab);
// Collect applicable materials.
foreach (Material m in materials)
if (m.name.Contains(smr.name.ToLower())) toinclude.Add(m);
// When assembling a character, we load SkinnedMeshRenderers from assetbundles,
// and as such they have lost the references to their bones. To be able to
// remap the SkinnedMeshRenderers to use the bones from the characterbase assetbundles,
// we save the names of the bones used.
List<string> boneNames = new List<string>();
foreach (Transform t in smr.bones)
boneNames.Add(t.name);
string stringholderpath = "Assets/bonenames.asset";
StringHolder holder = ScriptableObject.CreateInstance<StringHolder> ();
AssetDatabase.CreateAsset(holder, stringholderpath);
toinclude.Add(AssetDatabase.LoadAssetAtPath(stringholderpath, typeof (StringHolder)));
// Save the assetbundle.
string bundleName = name + "_" + smr.name.ToLower();
path = AssetbundlePath + bundleName + ".assetbundle";
BuildPipeline.BuildAssetBundle(null, toinclude.ToArray(), path, BuildAssetBundleOptions.CollectDependencies);
Debug.Log("Saved " + bundleName + " with " + (toinclude.Count - 2) + " materials");
// Delete temp assets.
AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(rendererPrefab));
AssetDatabase.DeleteAsset(stringholderpath);
createdBundle = true;
}
}
if (createdBundle)
UpdateCharacterElementDatabase.Execute();
else
EditorUtility.DisplayDialog("Character Generator", "No Asset Bundles created. Select the characters folder in the Project pane to process all characters. Se }
static Object GetPrefab(GameObject go, string name)
{
Object tempPrefab = EditorUtility.CreateEmptyPrefab("Assets/" + name + ".prefab"); tempPrefab = EditorUtility.ReplacePrefab(go, tempPrefab);
Object.DestroyImmediate(go);
return tempPrefab;
}
public static string AssetbundlePath
{
get { return "assetbundles" + Path.DirectorySeparatorChar; }
}
}
(2)⾓⾊换装代码
// Creates a character based on the currentConfiguration recycling a
// character base, this way the position and animation of the character
// are not changed.
public GameObject Generate(GameObject root)
{
float startTime = altimeSinceStartup;
// 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.
List<CombineInstance> combineInstances = new List<CombineInstance>();
List<Material> materials = new List<Material>();
List<Transform> bones = new List<Transform>();
Transform[] transforms = root.GetComponentsInChildren<Transform>();
foreach (CharacterElement element in currentConfiguration.Values)
{
SkinnedMeshRenderer smr = element.GetSkinnedMeshRenderer();
materials.AddRange(smr.materials);
for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
{
CombineInstance ci = new CombineInstance();
ci.subMeshIndex = sub;
combineInstances.Add(ci);
}
// As the SkinnedMeshRenders are stored in assetbundles that do not
/
/ contain their bones (those are stored in the characterbase assetbundles)
// we need to collect references to the bones we are using
foreach (string bone in element.GetBoneNames())
{
foreach (Transform transform in transforms)
{
if (transform.name != bone) continue;
bones.Add(transform);
break;
}
}
Object.Destroy(smr.gameObject);
}
// Obtain and configure the SkinnedMeshRenderer attached to
// the character base.
SkinnedMeshRenderer r = root.GetComponent<SkinnedMeshRenderer>();
r.sharedMesh = new Mesh();
r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);
r.bones = bones.ToArray();
r.materials = materials.ToArray();
Debug.Log("Generating character took: " + (altimeSinceStartup - startTime) * 1000 + " ms"); return root;
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论