uinity 创建一个空的gameobject 挂在一个自定义脚本AvatarCreator,AvatarCreator实现代码方式动态创建符合HumanBodyBones的骨骼人体模型,模型包含完整的骨骼节点,创建完骨骼后创建显示部分,用长方体代替骨骼,关节处用球体,头部用球体,眼睛用球体,并用AvatarBuilder.BuildHumanAvatar创建Avatar绑定到动画,AvatarCreator同时包含保存骨骼肢体动作信息为json文件,并能从json文件加载avatar姿势的完整信息
using UnityEngine;
using UnityEngine.Animations;
using System.Collections.Generic;
using System.IO;
public class AvatarCreator : MonoBehaviour
{
// 定义骨骼节点列表
private Dictionary<HumanBodyBones, Transform> boneDictionary = new Dictionary<HumanBodyBones, Transform>();
void Start()
{
// 创建骨骼模型
CreateAvatar();
}
void CreateAvatar()
{
// 创建一个空的 GameObject 作为根节点
GameObject avatar = new GameObject("Avatar");
// 遍历 HumanBodyBones 中的所有枚举值
foreach (HumanBodyBones boneType in System.Enum.GetValues(typeof(HumanBodyBones)))
{
// 跳过不使用的骨骼类型
if (boneType == HumanBodyBones.LastBone) continue;
// 创建骨骼节点的可视化表示
GameObject boneObject = new GameObject(boneType.ToString());
boneObject.transform.parent = avatar.transform;
boneObject.transform.localPosition = Vector3.zero;
// 创建关节的球体
GameObject jointSphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
jointSphere.name = boneType.ToString() + "_Joint";
jointSphere.transform.parent = boneObject.transform;
jointSphere.transform.localPosition = Vector3.zero;
jointSphere.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f); // 球体的大小
Destroy(jointSphere.GetComponent<Collider>()); // 移除碰撞体,使其不影响其他物体的碰撞检测
// 如果是头部,添加一个球体表示头部
if (boneType == HumanBodyBones.Head)
{
GameObject headSphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
headSphere.transform.parent = boneObject.transform;
headSphere.transform.localPosition = Vector3.zero;
headSphere.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f); // 头部球体的大小
Destroy(headSphere.GetComponent<Collider>()); // 移除碰撞体,使其不影响其他物体的碰撞检测
// 添加眼睛
GameObject leftEye = GameObject.CreatePrimitive(PrimitiveType.Sphere);
leftEye.transform.parent = headSphere.transform;
leftEye.transform.localPosition = new Vector3(-0.05f, 0.05f, 0.1f);
leftEye.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
Destroy(leftEye.GetComponent<Collider>());
GameObject rightEye = GameObject.CreatePrimitive(PrimitiveType.Sphere);
rightEye.transform.parent = headSphere.transform;
rightEye.transform.localPosition = new Vector3(0.05f, 0.05f, 0.1f);
rightEye.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
Destroy(rightEye.GetComponent<Collider>());
}
// 将骨骼节点添加到字典中
boneDictionary.Add(boneType, boneObject.transform);
}
// 创建并绑定 Avatar
HumanDescription humanDescription = CreateHumanDescription(avatar);
Avatar humanAvatar = AvatarBuilder.BuildHumanAvatar(avatar, humanDescription);
Animator animator = avatar.AddComponent<Animator>();
animator.avatar = humanAvatar;
}
HumanDescription CreateHumanDescription(GameObject avatar)
{
List<HumanBone> humanBones = new List<HumanBone>();
List<SkeletonBone> skeletonBones = new List<SkeletonBone>();
foreach (KeyValuePair<HumanBodyBones, Transform> pair in boneDictionary)
{
HumanBone humanBone = new HumanBone
{
boneName = pair.Key.ToString(),
humanName = pair.Key.ToString(),
limit = new HumanLimit
{
useDefaultValues = true
}
};
humanBones.Add(humanBone);
SkeletonBone skeletonBone = new SkeletonBone
{
name = pair.Key.ToString(),
position = pair.Value.localPosition,
rotation = pair.Value.localRotation,
scale = pair.Value.localScale
};
skeletonBones.Add(skeletonBone);
}
return new HumanDescription
{
human = humanBones.ToArray(),
skeleton = skeletonBones.ToArray(),
upperArmTwist = 0.5f,
lowerArmTwist = 0.5f,
upperLegTwist = 0.5f,
lowerLegTwist = 0.5f,
armStretch = 0.05f,
legStretch = 0.05f,
feetSpacing = 0.0f,
hasTranslationDoF = false
};
}
// 保存骨骼肢体动作信息为 JSON 文件
public void SaveAvatarPose(string filePath)
{
List<SerializableTransform> boneTransforms = new List<SerializableTransform>();
foreach (KeyValuePair<HumanBodyBones, Transform> pair in boneDictionary)
{
SerializableTransform serializableTransform = new SerializableTransform(pair.Key.ToString(), pair.Value);
boneTransforms.Add(serializableTransform);
}
string json = JsonUtility.ToJson(new SerializableTransformList { transforms = boneTransforms });
File.WriteAllText(filePath, json);
}
// 从 JSON 文件加载 avatar 姿势的完整信息
public void LoadAvatarPose(string filePath)
{
if (File.Exists(filePath))
{
string json = File.ReadAllText(filePath);
SerializableTransformList boneTransforms = JsonUtility.FromJson<SerializableTransformList>(json);
foreach (SerializableTransform serializableTransform in boneTransforms.transforms)
{
HumanBodyBones boneType;
if (System.Enum.TryParse(serializableTransform.boneName, out boneType) && boneDictionary.ContainsKey(boneType))
{
boneDictionary[boneType].localPosition = serializableTransform.position;
boneDictionary[boneType].localRotation = serializableTransform.rotation;
boneDictionary[boneType].localScale = serializableTransform.scale;
}
}
}
else
{
Debug.LogError("File does not exist: " + filePath);
}
}
[System.Serializable]
public class SerializableTransform
{
public string boneName;
public Vector3 position;
public Quaternion rotation;
public Vector3 scale;
public SerializableTransform(string name, Transform transform)
{
boneName = name;
position = transform.localPosition;
rotation = transform.localRotation;
scale = transform.localScale;
}
}
[System.Serializable]
public class SerializableTransformList
{
public List<SerializableTransform> transforms;
}
}