数字人:肢体动画控制

unity animator 动画数据 保存为json,然后传输到网络,接收端接收josn数据恢复为ainimator动画数据驱动gameobject 播放动画

确保以下步骤在 Unity 项目中完成:

  1. 配置 Animator 和 Avatar

    • 创建并配置 Avatar,并将其绑定到 Animator 组件中。
    • 确保 Animator Controller 正确设置。
  2. 添加和配置脚本

    • AnimatorAvatarCheckerDynamicAnimationCreatorDynamicAnimationLoader 脚本添加到同一个 GameObject 上,并配置相应的 public 属性

// 检测 Animator 和 Avatar 绑定关系
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

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

public class AnimatorAvatarChecker : MonoBehaviour
{
    public Animator animator;

    void Start()
    {
        if (animator == null)
        {
            Debug.LogError("Animator component is not assigned.");
            return;
        }

        if (animator.avatar == null)
        {
            Debug.LogError("Animator does not have an Avatar assigned.");
            return;
        }

        if (!animator.avatar.isValid)
        {
            Debug.LogError("Assigned Avatar is not valid.");
            return;
        }

        if (!animator.avatar.isHuman)
        {
            Debug.LogError("Assigned Avatar is not a humanoid.");
            return;
        }

        Debug.Log("Animator and Avatar are correctly configured.");
    }
}

public class DynamicAnimationCreator : MonoBehaviour
{
    public Animator animator;
    public SkinnedMeshRenderer[] skinnedMeshRenderers;

    void Start()
    {
        if (animator == null)
        {
            Debug.LogError("Animator component is not assigned.");
            return;
        }

        if (skinnedMeshRenderers.Length == 0)
        {
            Debug.LogError("No SkinnedMeshRenderers assigned.");
            return;
        }

        List<Transform> bones = new List<Transform>();
        foreach (HumanBodyBones bone in System.Enum.GetValues(typeof(HumanBodyBones)))
        {
            Transform boneTransform = animator.GetBoneTransform(bone);
            if (boneTransform != null)
            {
                bones.Add(boneTransform);
            }
        }

        AnimationClip clip = new AnimationClip();
        clip.legacy = false;

        List<float> keyframeTimes = new List<float> { 0f, 1f, 2f };

        foreach (Transform bone in bones)
        {
            Keyframe[] posXKeys = { new Keyframe(0f, bone.localPosition.x), new Keyframe(1f, bone.localPosition.x + 0.1f), new Keyframe(2f, bone.localPosition.x) };
            Keyframe[] posYKeys = { new Keyframe(0f, bone.localPosition.y), new Keyframe(1f, bone.localPosition.y + 0.1f), new Keyframe(2f, bone.localPosition.y) };
            Keyframe[] posZKeys = { new Keyframe(0f, bone.localPosition.z), new Keyframe(1f, bone.localPosition.z + 0.1f), new Keyframe(2f, bone.localPosition.z) };

            Keyframe[] rotXKeys = { new Keyframe(0f, bone.localEulerAngles.x), new Keyframe(1f, bone.localEulerAngles.x + 45f), new Keyframe(2f, bone.localEulerAngles.x) };
            Keyframe[] rotYKeys = { new Keyframe(0f, bone.localEulerAngles.y), new Keyframe(1f, bone.localEulerAngles.y + 45f), new Keyframe(2f, bone.localEulerAngles.y) };
            Keyframe[] rotZKeys = { new Keyframe(0f, bone.localEulerAngles.z), new Keyframe(1f, bone.localEulerAngles.z + 45f), new Keyframe(2f, bone.localEulerAngles.z) };

            Keyframe[] scaleXKeys = { new Keyframe(0f, bone.localScale.x), new Keyframe(1f, bone.localScale.x * 1.1f), new Keyframe(2f, bone.localScale.x) };
            Keyframe[] scaleYKeys = { new Keyframe(0f, bone.localScale.y), new Keyframe(1f, bone.localScale.y * 1.1f), new Keyframe(2f, bone.localScale.y) };
            Keyframe[] scaleZKeys = { new Keyframe(0f, bone.localScale.z), new Keyframe(1f, bone.localScale.z * 1.1f), new Keyframe(2f, bone.localScale.z) };

            string bonePath = GetRelativePath(bone);
            clip.SetCurve(bonePath, typeof(Transform), "localPosition.x", new AnimationCurve(posXKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localPosition.y", new AnimationCurve(posYKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localPosition.z", new AnimationCurve(posZKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localEulerAngles.x", new AnimationCurve(rotXKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localEulerAngles.y", new AnimationCurve(rotYKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localEulerAngles.z", new AnimationCurve(rotZKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localScale.x", new AnimationCurve(scaleXKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localScale.y", new AnimationCurve(scaleYKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localScale.z", new AnimationCurve(scaleZKeys));
        }

        foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
        {
            string rendererPath = GetRelativePath(skinnedMeshRenderer.transform);
            for (int i = 0; i < skinnedMeshRenderer.sharedMesh.blendShapeCount; i++)
            {
                string blendShapeName = skinnedMeshRenderer.sharedMesh.GetBlendShapeName(i);
                Keyframe[] blendShapeKeys = { new Keyframe(0f, 0f), new Keyframe(1f, 100f), new Keyframe(2f, 0f) };
                clip.SetCurve(rendererPath, typeof(SkinnedMeshRenderer), "blendShape." + blendShapeName, new AnimationCurve(blendShapeKeys));
            }
        }

        AnimatorOverrideController overrideController = new AnimatorOverrideController(animator.runtimeAnimatorController);
        overrideController["DefaultAnimation"] = clip;
        animator.runtimeAnimatorController = overrideController;

        animator.Play("DefaultAnimation");

        string json = SaveAnimationData(clip, bones, keyframeTimes);
        File.WriteAllText(Application.dataPath + "/animationData.json", json);
    }

    private string GetRelativePath(Transform transform)
    {
        List<string> pathElements = new List<string>();
        Transform current = transform;
        while (current != transform.root)
        {
            pathElements.Insert(0, current.name);
            current = current.parent;
        }
        return string.Join("/", pathElements);
    }

    private string SaveAnimationData(AnimationClip clip, List<Transform> bones, List<float> keyframeTimes)
    {
        AnimationDataContainer dataContainer = new AnimationDataContainer { animations = new List<AnimationData>() };

        foreach (Transform bone in bones)
        {
            AnimationData data = new AnimationData { boneName = bone.name, keyframeTimes = keyframeTimes.ToArray(), positions = new List<Vector3>(), rotations = new List<Vector3>(), scales = new List<Vector3>() };

            var bindings = AnimationUtility.GetCurveBindings(clip);
            foreach (var binding in bindings)
            {
                if (binding.path == GetRelativePath(bone))
                {
                    AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, binding);

                    if (binding.propertyName == "localPosition.x")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.positions.Exists(pos => pos.x == key.value))
                            {
                                Vector3 position = data.positions.Count > 0 ? data.positions[data.positions.Count - 1] : Vector3.zero;
                                data.positions.Add(new Vector3(key.value, position.y, position.z));
                            }
                        }
                    }
                    else if (binding.propertyName == "localPosition.y")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.positions.Exists(pos => pos.y == key.value))
                            {
                                Vector3 position = data.positions.Count > 0 ? data.positions[data.positions.Count - 1] : Vector3.zero;
                                data.positions[data.positions.Count - 1] = new Vector3(position.x, key.value, position.z);
                            }
                        }
                    }
                    else if (binding.propertyName == "localPosition.z")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.positions.Exists(pos => pos.z == key.value))
                            {
                                Vector3 position = data.positions.Count > 0 ? data.positions[data.positions.Count - 1] : Vector3.zero;
                                data.positions[data.positions.Count - 1] = new Vector3(position.x, position.y, key.value);
                            }
                        }
                    }
                    else if (binding.propertyName == "localEulerAngles.x")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.rotations.Exists(rot => rot.x == key.value))
                            {
                                Vector3 rotation = data.rotations.Count > 0 ? data.rotations[data.rotations.Count - 1] : Vector3.zero;
                                data.rotations.Add(new Vector3(key.value, rotation.y, rotation.z));
                            }
                        }
                    }
                    else if (binding.propertyName == "localEulerAngles.y")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.rotations.Exists(rot => rot.y == key.value))
                            {
                                Vector3 rotation = data.rotations.Count > 0 ? data.rotations[data.rotations.Count - 1] : Vector3.zero;
                                data.rotations[data.rotations.Count - 1] = new Vector3(rotation.x, key.value, rotation.z);
                            }
                        }
                    }
                    else if (binding.propertyName == "localEulerAngles.z")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.rotations.Exists(rot => rot.z == key.value))
                            {
                                Vector3 rotation = data.rotations.Count > 0 ? data.rotations[data.rotations.Count - 1] : Vector3.zero;
                                data.rotations[data.rotations.Count - 1] = new Vector3(rotation.x, rotation.y, key.value);
                            }
                        }
                    }
                    else if (binding.propertyName == "localScale.x")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.scales.Exists(scale => scale.x == key.value))
                            {
                                Vector3 scale = data.scales.Count > 0 ? data.scales[data.scales.Count - 1] : Vector3.one;
                                data.scales.Add(new Vector3(key.value, scale.y, scale.z));
                            }
                        }
                    }
                    else if (binding.propertyName == "localScale.y")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.scales.Exists(scale => scale.y == key.value))
                            {
                                Vector3 scale = data.scales.Count > 0 ? data.scales[data.scales.Count - 1] : Vector3.one;
                                data.scales[data.scales.Count - 1] = new Vector3(scale.x, key.value, scale.z);
                            }
                        }
                    }
                    else if (binding.propertyName == "localScale.z")
                    {
                        foreach (var key in curve.keys)
                        {
                            if (!data.scales.Exists(scale => scale.z == key.value))
                            {
                                Vector3 scale = data.scales.Count > 0 ? data.scales[data.scales.Count - 1] : Vector3.one;
                                data.scales[data.scales.Count - 1] = new Vector3(scale.x, scale.y, key.value);
                            }
                        }
                    }
                }
            }

            dataContainer.animations.Add(data);
        }

        foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
        {
            string rendererPath = GetRelativePath(skinnedMeshRenderer.transform);
            for (int i = 0; i < skinnedMeshRenderer.sharedMesh.blendShapeCount; i++)
            {
                AnimationData data = new AnimationData { boneName = rendererPath, keyframeTimes = keyframeTimes.ToArray(), positions = new List<Vector3>(), rotations = new List<Vector3>(), scales = new List<Vector3>() };

                string blendShapeName = skinnedMeshRenderer.sharedMesh.GetBlendShapeName(i);
                AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, new EditorCurveBinding { path = rendererPath, type = typeof(SkinnedMeshRenderer), propertyName = "blendShape." + blendShapeName });

                if (curve != null)
                {
                    foreach (var key in curve.keys)
                    {
                        data.positions.Add(new Vector3(key.time, key.value, 0));
                    }
                    dataContainer.animations.Add(data);
                }
            }
        }

        return JsonUtility.ToJson(dataContainer);
    }

    [System.Serializable]
    public class AnimationData
    {
        public string boneName;
        public float[] keyframeTimes;
        public List<Vector3> positions;
        public List<Vector3> rotations;
        public List<Vector3> scales;
    }

    [System.Serializable]
    public class AnimationDataContainer
    {
        public List<AnimationData> animations;
    }
}

public class DynamicAnimationLoader : MonoBehaviour
{
    public Animator animator;
    public SkinnedMeshRenderer[] skinnedMeshRenderers;

    void Start()
    {
        if (animator == null)
        {
            Debug.LogError("Animator component is not assigned.");
            return;
        }

        string json = File.ReadAllText(Application.dataPath + "/animationData.json");
        AnimationDataContainer dataContainer = JsonUtility.FromJson<AnimationDataContainer>(json);

        AnimationClip clip = new AnimationClip();
        clip.legacy = false;

        foreach (var data in dataContainer.animations)
        {
            Keyframe[] posXKeys = new Keyframe[data.keyframeTimes.Length];
            Keyframe[] posYKeys = new Keyframe[data.keyframeTimes.Length];
            Keyframe[] posZKeys = new Keyframe[data.keyframeTimes.Length];
            Keyframe[] rotXKeys = new Keyframe[data.keyframeTimes.Length];
            Keyframe[] rotYKeys = new Keyframe[data.keyframeTimes.Length];
            Keyframe[] rotZKeys = new Keyframe[data.keyframeTimes.Length];
            Keyframe[] scaleXKeys = new Keyframe[data.keyframeTimes.Length];
            Keyframe[] scaleYKeys = new Keyframe[data.keyframeTimes.Length];
            Keyframe[] scaleZKeys = new Keyframe[data.keyframeTimes.Length];

            for (int i = 0; i < data.keyframeTimes.Length; i++)
            {
                if (i < data.positions.Count)
                {
                    posXKeys[i] = new Keyframe(data.keyframeTimes[i], data.positions[i].x);
                    posYKeys[i] = new Keyframe(data.keyframeTimes[i], data.positions[i].y);
                    posZKeys[i] = new Keyframe(data.keyframeTimes[i], data.positions[i].z);
                }

                if (i < data.rotations.Count)
                {
                    rotXKeys[i] = new Keyframe(data.keyframeTimes[i], data.rotations[i].x);
                    rotYKeys[i] = new Keyframe(data.keyframeTimes[i], data.rotations[i].y);
                    rotZKeys[i] = new Keyframe(data.keyframeTimes[i], data.rotations[i].z);
                }

                if (i < data.scales.Count)
                {
                    scaleXKeys[i] = new Keyframe(data.keyframeTimes[i], data.scales[i].x);
                    scaleYKeys[i] = new Keyframe(data.keyframeTimes[i], data.scales[i].y);
                    scaleZKeys[i] = new Keyframe(data.keyframeTimes[i], data.scales[i].z);
                }
            }

            string bonePath = data.boneName;
            clip.SetCurve(bonePath, typeof(Transform), "localPosition.x", new AnimationCurve(posXKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localPosition.y", new AnimationCurve(posYKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localPosition.z", new AnimationCurve(posZKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localEulerAngles.x", new AnimationCurve(rotXKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localEulerAngles.y", new AnimationCurve(rotYKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localEulerAngles.z", new AnimationCurve(rotZKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localScale.x", new AnimationCurve(scaleXKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localScale.y", new AnimationCurve(scaleYKeys));
            clip.SetCurve(bonePath, typeof(Transform), "localScale.z", new AnimationCurve(scaleZKeys));
        }

        foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
        {
            foreach (var data in dataContainer.animations)
            {
                if (data.boneName == GetRelativePath(skinnedMeshRenderer.transform))
                {
                    for (int i = 0; i < skinnedMeshRenderer.sharedMesh.blendShapeCount; i++)
                    {
                        string blendShapeName = skinnedMeshRenderer.sharedMesh.GetBlendShapeName(i);
                        Keyframe[] blendShapeKeys = new Keyframe[data.keyframeTimes.Length];

                        for (int j = 0; j < data.keyframeTimes.Length; j++)
                        {
                            blendShapeKeys[j] = new Keyframe(data.keyframeTimes[j], data.positions[j].y);
                        }

                        clip.SetCurve(data.boneName, typeof(SkinnedMeshRenderer), "blendShape." + blendShapeName, new AnimationCurve(blendShapeKeys));
                    }
                }
            }
        }

        AnimatorOverrideController overrideController = new AnimatorOverrideController(animator.runtimeAnimatorController);
        overrideController["DefaultAnimation"] = clip;
        animator.runtimeAnimatorController = overrideController;

        animator.Play("DefaultAnimation");
    }

    private string GetRelativePath(Transform transform)
    {
        List<string> pathElements = new List<string>();
        Transform current = transform;
        while (current != transform.root)
        {
            pathElements.Insert(0, current.name);
            current = current.parent;
        }
        return string.Join("/", pathElements);
    }

    [System.Serializable]
    public class AnimationData
    {
        public string boneName;
        public float[] keyframeTimes;
        public List<Vector3> positions;
        public List<Vector3> rotations;
        public List<Vector3> scales;
    }

    [System.Serializable]
    public class AnimationDataContainer
    {
        public List<AnimationData> animations;
    }
}

  1. 相对路径计算

    • GetRelativePath 方法用于计算 Transform 的相对路径。确保路径在设置和读取动画曲线时是一致的。
  2. 动画数据保存和恢复

    • 更新 SaveAnimationData 方法以保存 AnimationClip 中的曲线数据。
    • 更新 DynamicAnimationLoader 以从 JSON 文件中恢复动画曲线。
  3. SkinnedMeshRenderer 支持

    • 在创建和读取动画时,处理每个 SkinnedMeshRenderer 的变形键。

使用步骤

  1. 绑定 Animator 和 SkinnedMeshRenderer

    • 在 Unity Editor 中,将 Animator 组件和所有需要处理的 SkinnedMeshRenderer 组件拖放到相应的脚本组件中。
  2. 运行场景

    • 运行 Unity 场景,确保控制台中没有错误信息并且动画正常播放。
  3. 验证 JSON 数据

    • 确认生成的 JSON 文件包含正确的动画数据,并且可以通过 DynamicAnimationLoader 脚本正确加载和播放动画。

通过这些更新和检查,可以确保代码逻辑正确且能够动态控制人体骨骼动画,并支持多 SkinnedMeshRenderer 的变形键。

 

  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值