0、关键词解释:
Slot:a mesh which can be added to an UMA
overlay: a texture that can be added to an UMA
DNA: values that changes the shape of an UMA
Race: s set of meshes which work together to make an UMA
1、如何动态创建UMA:
关键代码:
void GenerateUMA ()
{
GameObject GO = new GameObject ("MyUMA");
umaDynamicAvatar = GO.AddComponent ();
//init avatar and grab a reference to it`s data component
umaDynamicAvatar.Initialize ();
umaData = umaDynamicAvatar.umaData;
//character 创建完成后调用
umaData.OnCharacterCreated += CharacterCreatedCallback;
//umaData.OnCharacterDestroyed
//umaData.OnCharacterUpdated
// atach our generator
umaDynamicAvatar.umaGenerator = generator;
umaData.umaGenerator = generator;
// set up slot array
umaData.umaRecipe.slotDataList = new SlotData[numberOfSlots];
// set up our Morph references
umaDna = new UMADnaHumanoid ();
umaTutorialDNA = new UMADnaTutorial ();
umaData.umaRecipe.AddDna (umaDna);
umaData.umaRecipe.AddDna (umaTutorialDNA);
// >>> this is where the fun will happen <<<
CreateMale ();
umaDynamicAvatar.animationController = animController;
// generate our UMA
umaDynamicAvatar.UpdateNewRace ();
GO.transform.parent = this.gameObject.transform;
GO.transform.localPosition = Vector3.zero;
GO.transform.localRotation = Quaternion.identity;
}
2、如何运行过程中动态更改体型
关键代码段:
if (bodyMass != umaDna.upperMuscle) {
SetBodyMass (bodyMass);
umaData.isShapeDirty = true;
umaData.Dirty ();
}
void SetBodyMass (float mass)
{
umaDna.upperMuscle = mass;
umaDna.upperWeight = mass;
umaDna.lowerMuscle = mass;
umaDna.lowerWeight = mass;
umaDna.armWidth = mass;
umaDna.forearmWidth = mass;
}
3、如何动态添加、删除overlay&slot,及颜色【也就是添加、更改、删除衣服】
关键代码段:
/// hair //
if (hairState && !lastHairState) {
lastHairState = true;
SetSlot (7, "M_Hair_Shaggy");
AddOverlay (7, "M_Hair_Shaggy", hairColor);
umaData.isMeshDirty = true;
umaData.isShapeDirty = true;
umaData.isTextureDirty = true;
umaData.Dirty ();
}
if (!hairState && lastHairState) {
lastHairState = false;
RemoveSlot (7);
//RemoveOverlay (7, "M_Hair_Shaggy");
umaData.isMeshDirty = true;
umaData.isShapeDirty = true;
umaData.isTextureDirty = true;
umaData.Dirty ();
}
slot helper /
void SetSlot (int slotNumber, string slotName)
{
umaData.umaRecipe.slotDataList [slotNumber] = slotLibrary.InstantiateSlot (slotName);
}
void RemoveSlot (int slotNumber)
{
umaData.umaRecipe.slotDataList [slotNumber] = null;
}
overlay helper /
void AddOverlay (int slot, string overlayName)
{
umaData.umaRecipe.slotDataList [slot].AddOverlay (overlayLibrary.InstantiateOverlay (overlayName));
}
void AddOverlay (int slot, string overlayName, Color color)
{
umaData.umaRecipe.slotDataList [slot].AddOverlay (overlayLibrary.InstantiateOverlay (overlayName, color));
}
void LinkOverlay (int slotNumber, int slotToLink)
{
umaData.umaRecipe.slotDataList [slotNumber].SetOverlayList (umaData.umaRecipe.slotDataList [slotToLink].GetOverlayList ());
}
void RemoveOverlay (int slotNumber, string overlayName)
{
umaData.umaRecipe.slotDataList [slotNumber].RemoveOverlay (overlayName);
}
void ColorOverlay (int slotNumber, string overlayName, Color color)
{
umaData.umaRecipe.slotDataList [slotNumber].SetOverlayColor (color, overlayName);
}
4、如何导出导入UMA
// save and load /
void SaveTxt ()
{
//genarate uma string
UMATextRecipe recipe = ScriptableObject.CreateInstance ();
recipe.Save (umaDynamicAvatar.umaData.umaRecipe, umaDynamicAvatar.context);
SaveString = recipe.recipeString;
Destroy (recipe);
//save string to text file
string fileName = "Assets/Resources/MyTestUma.txt";
StreamWriter stream = File.CreateText (fileName);
stream.WriteLine (SaveString);
stream.Close ();
}
void LoadTxt ()
{
//loadstring from text.txt
string fileName = "Assets/Resources/MyTestUma.txt";
StreamReader stream = File.OpenText (fileName);
SaveString = stream.ReadLine ();
stream.Close ();
//regenarate uma
UMATextRecipe recipe = ScriptableObject.CreateInstance ();
recipe.recipeString = SaveString;
umaDynamicAvatar.Load (recipe);
Destroy (recipe);
}
void LoadAsset ()
{
UMARecipeBase recipe = Resources.Load ("MyTestUmaAsset") as UMARecipeBase; //注意这里不能有重名的文件名称
umaDynamicAvatar.Load (recipe);
}
void SaveAsset ()
{
#if UNITY_EDITOR
UMATextRecipe asset = ScriptableObject.CreateInstance ();
asset.Save (umaDynamicAvatar.umaData.umaRecipe, umaDynamicAvatar.context);
AssetDatabase.CreateAsset (asset, "Assets/Resources/MyTestUmaAsset.asset");
AssetDatabase.SaveAssets ();
#endif
}
5、如何给UMA添加武器
思路:在创建人物模型成功后的事件中,给uma添加gameObject即可
//character 创建完成后调用
umaData.OnCharacterCreated += CharacterCreatedCallback;
//umaData.OnCharacterDestroyed
//umaData.OnCharacterUpdated
void CharacterCreatedCallback (UMAData umaData)
{
Debug.Log ("Character Created...");
//attach props
GrabStaff ();
}
6、如何动态改变uma的表情
思路:在创建人物模型后,添加
UMAExpressionPlayer
组件,然后在update中动态设置
UMAExpressionPlayer
各个属性的值即可。
void CharacterCreatedCallback (UMAData umaData)
{
//ExpressionSet
UMAExpressionSet expressionSet = umaData.umaRecipe.raceData.expressionSet;
expressionPlayer = umaData.gameObject.AddComponent ();
expressionPlayer.expressionSet = expressionSet;
expressionPlayer.umaData = umaData;
expressionPlayer.Initialize ();
}
在update()中添加:
if ( expressionPlayer && happy != expressionPlayer.midBrowUp_Down) {
expressionPlayer.midBrowUp_Down = happy;
expressionPlayer.leftMouthSmile_Frown = happy;
expressionPlayer.rightMouthSmile_Frown = happy;
}
uma教程视屏资源如下:
http://secretanorak.co.uk/course-listing/
https://www.youtube.com/user/fernandoribeirogames
附完整UMAmaker代码:
using UnityEngine;
using System.Collections;
using UMA;
using UMA.PoseTools;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class UMAMaker : MonoBehaviour
{
public UMAGeneratorBase generator;
public SlotLibrary slotLibrary;
public OverlayLibrary overlayLibrary;
public RaceLibrary raceLibrary;
public RuntimeAnimatorController animController;
private UMADynamicAvatar umaDynamicAvatar;
private UMAData umaData;
private UMADnaHumanoid umaDna;
private UMADnaTutorial umaTutorialDNA;
private int numberOfSlots = 20;
[Range (0.0f, 1.0f)]
public float bodyMass = 0.5f;
[Range (-1.0f, 1.0f)]
public float happy = 0f;
public UMAExpressionPlayer expressionPlayer;
public static string RIGHT_HAND = "Root/Global/Position/Hips/LowerBack/Spine/Spine1/RightShoulder/RightArm/RightForeArm/RightHand";
public bool vestState = false;
private bool lastVestState = false;
public Color vestColor = Color.white;
private Color lastVestColor = Color.white;
public bool hairState = false;
private bool lastHairState = false;
public Color hairColor = Color.white;
private Color lastHairColor = Color.white;
public string SaveString;
public bool saveTxt;
public bool loadTxt;
public bool saveAsset;
public bool loadAsset;
void Start ()
{
GenerateUMA ();
}
void Update ()
{
if (bodyMass != umaDna.upperMuscle) {
SetBodyMass (bodyMass);
umaData.isShapeDirty = true;
umaData.Dirty ();
}
//expression 这里不知道为什么会有一次 expressionPlayer为null的情形
if ( expressionPlayer && happy != expressionPlayer.midBrowUp_Down) {
expressionPlayer.midBrowUp_Down = happy;
expressionPlayer.leftMouthSmile_Frown = happy;
expressionPlayer.rightMouthSmile_Frown = happy;
}
/// vest //
if (vestState && !lastVestState) {
lastVestState = true;
//注意添加顺序不同,就会有不同的影响
AddOverlay (3, "SA_Tee", vestColor);
AddOverlay (3, "SA_Logo");
umaData.isTextureDirty = true;
umaData.Dirty ();
}
if (!vestState && lastVestState) {
lastVestState = false;
RemoveOverlay (3, "SA_Tee");
umaData.isTextureDirty = true;
umaData.Dirty ();
}
if (vestColor != lastVestColor && vestState) {
lastVestColor = vestColor;
ColorOverlay (3, "SA_Tee", vestColor);
umaData.isTextureDirty = true;
umaData.Dirty ();
}
/// hair //
if (hairState && !lastHairState) {
lastHairState = true;
SetSlot (7, "M_Hair_Shaggy");
AddOverlay (7, "M_Hair_Shaggy", hairColor);
umaData.isMeshDirty = true;
umaData.isShapeDirty = true;
umaData.isTextureDirty = true;
umaData.Dirty ();
}
if (!hairState && lastHairState) {
lastHairState = false;
RemoveSlot (7);
//RemoveOverlay (7, "M_Hair_Shaggy");
umaData.isMeshDirty = true;
umaData.isShapeDirty = true;
umaData.isTextureDirty = true;
umaData.Dirty ();
}
if (hairColor != lastHairColor && hairState) {
lastHairColor = hairColor;
ColorOverlay (7, "M_Hair_Shaggy", hairColor);
umaData.isTextureDirty = true;
umaData.Dirty ();
}
if (saveTxt) {
saveTxt = false;
SaveTxt ();
}
if (loadTxt) {
loadTxt = false;
LoadTxt ();
}
if (saveAsset) {
saveAsset = false;
SaveAsset ();
}
if (loadAsset) {
loadAsset = false;
LoadAsset ();
}
}
void GenerateUMA ()
{
GameObject GO = new GameObject ("MyUMA");
umaDynamicAvatar = GO.AddComponent ();
//init avatar and grab a reference to it`s data component
umaDynamicAvatar.Initialize ();
umaData = umaDynamicAvatar.umaData;
//character 创建完成后调用
umaData.OnCharacterCreated += CharacterCreatedCallback;
//umaData.OnCharacterDestroyed
//umaData.OnCharacterUpdated
// atach our generator
umaDynamicAvatar.umaGenerator = generator;
umaData.umaGenerator = generator;
// set up slot array
umaData.umaRecipe.slotDataList = new SlotData[numberOfSlots];
// set up our Morph references
umaDna = new UMADnaHumanoid ();
umaTutorialDNA = new UMADnaTutorial ();
umaData.umaRecipe.AddDna (umaDna);
umaData.umaRecipe.AddDna (umaTutorialDNA);
// >>> this is where the fun will happen <<<
CreateMale ();
umaDynamicAvatar.animationController = animController;
// generate our UMA
umaDynamicAvatar.UpdateNewRace ();
GO.transform.parent = this.gameObject.transform;
GO.transform.localPosition = Vector3.zero;
GO.transform.localRotation = Quaternion.identity;
}
void CreateMale ()
{
// grab a reference to our recipe
var umaRecipe = umaDynamicAvatar.umaData.umaRecipe;
umaRecipe.SetRace (raceLibrary.GetRace ("HumanMale"));
//umaData.umaRecipe.slotDataList [0] = slotLibrary.InstantiateSlot ("MaleEyes");
SetSlot (0, "MaleEyes");
AddOverlay (0, "EyeOverlay");
//umaData.umaRecipe.slotDataList [0].AddOverlay (overlayLibrary.InstantiateOverlay("EyeOverlay"));
//umaData.umaRecipe.slotDataList [1] = slotLibrary.InstantiateSlot ("MaleInnerMouth");
SetSlot (1, "MaleInnerMouth"); //添加脸部分的卡槽
AddOverlay (1, "InnerMouth"); //添加脸部分的遮盖物
//umaData.umaRecipe.slotDataList [1].AddOverlay (overlayLibrary.InstantiateOverlay("InnerMouth"));
//umaData.umaRecipe.slotDataList [2] = slotLibrary.InstantiateSlot ("MaleFace");
SetSlot (2, "MaleFace");
AddOverlay (2, "MaleHead02");
//umaData.umaRecipe.slotDataList [2].AddOverlay (overlayLibrary.InstantiateOverlay("MaleHead01"));
//umaData.umaRecipe.slotDataList [3] = slotLibrary.InstantiateSlot ("MaleTorso");
SetSlot (3, "MaleTorso");
AddOverlay (3, "MaleBody02");
//umaData.umaRecipe.slotDataList [3].AddOverlay (overlayLibrary.InstantiateOverlay("MaleBody02"));
//umaData.umaRecipe.slotDataList [4] = slotLibrary.InstantiateSlot ("MaleHands");
SetSlot (4, "MaleHands");
//umaData.umaRecipe.slotDataList [4].AddOverlay (overlayLibrary.InstantiateOverlay ("MaleBody02"));
umaData.umaRecipe.slotDataList [4].SetOverlayList (umaData.umaRecipe.slotDataList [3].GetOverlayList ());
//umaData.umaRecipe.slotDataList [5] = slotLibrary.InstantiateSlot ("MaleLegs");
SetSlot (5, "MaleLegs");
//umaData.umaRecipe.slotDataList [5].AddOverlay (overlayLibrary.InstantiateOverlay("MaleBody02"));
umaData.umaRecipe.slotDataList [5].SetOverlayList (umaData.umaRecipe.slotDataList [3].GetOverlayList ());
//umaData.umaRecipe.slotDataList [6] = slotLibrary.InstantiateSlot ("MaleFeet");
SetSlot (6, "MaleFeet");
//umaData.umaRecipe.slotDataList [6].AddOverlay (overlayLibrary.InstantiateOverlay("MaleBody02"));
umaData.umaRecipe.slotDataList [6].SetOverlayList (umaData.umaRecipe.slotDataList [3].GetOverlayList ());
AddOverlay (3, "MaleUnderwear01");
//umaData.umaRecipe.slotDataList [3].AddOverlay (overlayLibrary.InstantiateOverlay("MaleUnderwear01"));
//umaData.umaRecipe.slotDataList [5].AddOverlay (overlayLibrary.InstantiateOverlay("MaleUnderwear01"));
umaData.umaRecipe.slotDataList [2].AddOverlay (overlayLibrary.InstantiateOverlay ("MaleEyebrow01", Color.black));
AddOverlay (2, "MaleEyebrow01", Color.black); //眉毛颜色
AddOverlay (3, "MaleUnderwear01"); //添加裤子
//AddOverlay (3, "SA_Tee"); //添加T恤
//AddOverlay(3,"SA_Logo"); //添加logo
//SetSlot(12,"MSimpleLeatherShoes");
//AddOverlay(12,"MSimpleLeatherShoes");
SetSlot (6, "MSimpleLeatherShoes");
AddOverlay (6, "MSimpleLeatherShoes");
//SetSlot(7,"CowboyHat");
//AddOverlay(7,"CowboyHat");
}
void SetBodyMass (float mass)
{
umaDna.upperMuscle = mass;
umaDna.upperWeight = mass;
umaDna.lowerMuscle = mass;
umaDna.lowerWeight = mass;
umaDna.armWidth = mass;
umaDna.forearmWidth = mass;
}
overlay helper /
void AddOverlay (int slot, string overlayName)
{
umaData.umaRecipe.slotDataList [slot].AddOverlay (overlayLibrary.InstantiateOverlay (overlayName));
}
void AddOverlay (int slot, string overlayName, Color color)
{
umaData.umaRecipe.slotDataList [slot].AddOverlay (overlayLibrary.InstantiateOverlay (overlayName, color));
}
void LinkOverlay (int slotNumber, int slotToLink)
{
umaData.umaRecipe.slotDataList [slotNumber].SetOverlayList (umaData.umaRecipe.slotDataList [slotToLink].GetOverlayList ());
}
void RemoveOverlay (int slotNumber, string overlayName)
{
umaData.umaRecipe.slotDataList [slotNumber].RemoveOverlay (overlayName);
}
void ColorOverlay (int slotNumber, string overlayName, Color color)
{
umaData.umaRecipe.slotDataList [slotNumber].SetOverlayColor (color, overlayName);
}
slot helper /
void SetSlot (int slotNumber, string slotName)
{
umaData.umaRecipe.slotDataList [slotNumber] = slotLibrary.InstantiateSlot (slotName);
}
void RemoveSlot (int slotNumber)
{
umaData.umaRecipe.slotDataList [slotNumber] = null;
}
void CharacterCreatedCallback (UMAData umaData)
{
Debug.Log ("Character Created...");
//attach props
GrabStaff ();
//ExpressionSet
UMAExpressionSet expressionSet = umaData.umaRecipe.raceData.expressionSet;
expressionPlayer = umaData.gameObject.AddComponent ();
expressionPlayer.expressionSet = expressionSet;
expressionPlayer.umaData = umaData;
expressionPlayer.Initialize ();
//blinkding and look around
expressionPlayer.enableBlinking = true;
expressionPlayer.enableSaccades = true;
GameObject GO = transform.Find ("MyUMA").gameObject;
//GO.AddComponent ();
}
void GrabStaff ()
{
GameObject staff = GameObject.Find ("Sword");
Transform hand = umaDynamicAvatar.gameObject.transform.FindChild (UMAMaker.RIGHT_HAND);
staff.transform.SetParent (hand);
staff.transform.localPosition = new Vector3 (-0.103f, -0.127f, -0.066f);
staff.transform.localRotation = Quaternion.Euler (new Vector3 (12.607f, 0, 0));
}
// save and load /
void SaveTxt ()
{
//genarate uma string
UMATextRecipe recipe = ScriptableObject.CreateInstance ();
recipe.Save (umaDynamicAvatar.umaData.umaRecipe, umaDynamicAvatar.context);
SaveString = recipe.recipeString;
Destroy (recipe);
//save string to text file
string fileName = "Assets/Resources/MyTestUma.txt";
StreamWriter stream = File.CreateText (fileName);
stream.WriteLine (SaveString);
stream.Close ();
}
void LoadTxt ()
{
//loadstring from text.txt
string fileName = "Assets/Resources/MyTestUma.txt";
StreamReader stream = File.OpenText (fileName);
SaveString = stream.ReadLine ();
stream.Close ();
//regenarate uma
UMATextRecipe recipe = ScriptableObject.CreateInstance ();
recipe.recipeString = SaveString;
umaDynamicAvatar.Load (recipe);
Destroy (recipe);
}
void LoadAsset ()
{
UMARecipeBase recipe = Resources.Load ("MyTestUmaAsset") as UMARecipeBase; //注意这里不能有重名的文件名称
umaDynamicAvatar.Load (recipe);
}
void SaveAsset ()
{
#if UNITY_EDITOR
UMATextRecipe asset = ScriptableObject.CreateInstance ();
asset.Save (umaDynamicAvatar.umaData.umaRecipe, umaDynamicAvatar.context);
AssetDatabase.CreateAsset (asset, "Assets/Resources/MyTestUmaAsset.asset");
AssetDatabase.SaveAssets ();
#endif
}
}