数字人:创建空骨骼并绑定肢体动画

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

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值