Skip to main content

自定义数据创建动画

时间

假如你的FrameRate是30,当前为第20帧,那么当前时间为 20/30

关键帧名称

可以直接创建一个prefab然后以文本的方式打开获得,观察下面yaml格式

position对应的是m_LocalPosition.x,m_LocalPosition.y,m_LocalPosition.z

rotation对应的是m_LocalRotation.x,m_LocalRotation.y,m_LocalRotation.z,m_LocalRotation.w

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4496430131825855203
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 4038476264630772384}
  m_Layer: 0
  m_Name: GameObject
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!4 &4038476264630772384
Transform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 4496430131825855203}
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
  m_LocalPosition: {x: -0.119914055, y: -2.430935, z: 2.2560377}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_ConstrainProportionsScale: 0
  m_Children: []
  m_Father: {fileID: 0}
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

设置动画曲线

动画是按照对象路径查找的,例如说A对象下B对象需要移动,那么路径只需要填写 B 即可。如果路径为空默认为根对象。

第二个参数为需要改变的类型

第三个参数填写修改的关键帧名字

第四个参数是动画曲线。例如创建一个线性插值的曲线 AnimationCurve.Linear(0,0,2,100)

clip.SetCurve("", typeof(Transform), "localPosition.x", curve);

创建一个动画

class Curve
{
    public string path;
    public System.Type type;
    public string name;
    public Keyframe[] keyframes;
}

class AnimationClipUtility
{
    public AnimationClip CreateAsset(Curve [] curves, WrapMode mode, float frameRate, AnimationClipSettings settings)
    {
        AnimationClip clip = new AnimationClip();

        //legacy为true时无法使用状态机
        clip.legacy = false;
        clip.wrapMode = mode;
        clip.frameRate = frameRate;
        AnimationUtility.SetAnimationClipSettings(clip, settings);

        for (int i=0;i< curves.Length;i++)
        {
            var curve = curves[i];
            clip.SetCurve(curve.path, curve.type, curve.name, new AnimationCurve(curve.keyframes));
        }

        return clip;
    }
}

给人形骨骼创建Avatar

public class AvatarTool
{
    public static Dictionary<string, string> map = new Dictionary<string, string> 
    {
        {"Hips","Hips"},
        {"Upper_Leg_L","LeftUpperLeg"},
        {"Upper_Leg_R","RightUpperLeg"},
        {"Lower_Leg_L","LeftLowerLeg"},
        {"Lower_Leg_R","RightLowerLeg"},
        {"Foot_L","LeftFoot"},
        {"Foot_R","RightFoot"},
        {"Spine","Spine"},
        {"Chest","Chest"},
        {"Neck","Neck"},
        {"Head","Head"},
        {"Clavicle_L","LeftShoulder"},
        {"Clavicle_R","RightShoulder"},
        {"Upper_Arm_L","LeftUpperArm"},
        {"Upper_Arm_R","RightUpperArm"},
        {"Lower_Arm_L","LeftLowerArm"},
        {"Lower_Arm_R","RightLowerArm"},
        {"Hand_L","LeftHand"},
        {"Hand_R","RightHand"},
        {"Eye_L","LeftEye"},
        {"Eye_R","RightEye"},
        {"Finger_L_Thumb_Proximal","Left Thumb Proximal"},
        {"Finger_L_Thumb_Intermediate","Left Thumb Intermediate"},
        {"Finger_L_Thumb_Distal","Left Thumb Distal"},
        {"Finger_L_Index_Proximal","Left Index Proximal"},
        {"Finger_L_Index_Intermediate","Left Index Intermediate"},
        {"Finger_L_Middle_Proximal","Left Middle Proximal"},
        {"Finger_L_Middle_Intermediate","Left Middle Intermediate"},
        {"Finger_L_Ring_Proximal","Left Ring Proximal"},
        {"Finger_L_Ring_Intermediate","Left Ring Intermediate"},
        {"Finger_L_Pinky_Proximal","Left Little Proximal"},
        {"Finger_L_Pinky_Intermediate","Left Little Intermediate"},
        {"Finger_R_Thumb_Proximal","Right Thumb Proximal"},
        {"Finger_R_Thumb_Intermediate","Right Thumb Intermediate"},
        {"Finger_R_Thumb_Distal","Right Thumb Distal"},
        {"Finger_R_Index_Proximal","Right Index Proximal"},
        {"Finger_R_Index_Intermediate","Right Index Intermediate"},
        {"Finger_R_Middle_Proximal","Right Middle Proximal"},
        {"Finger_R_Middle_Intermediate","Right Middle Intermediate"},
        {"Finger_R_Ring_Proximal","Right Ring Proximal"},
        {"Finger_R_Ring_Intermediate","Right Ring Intermediate"},
        {"Finger_R_Pinky_Proximal","Right Little Proximal"},
        {"Finger_R_Pinky_Intermediate","Right Little Intermediate"},
        {"Upper_Chest","UpperChest"},
        {"Toes_L","LeftToes"},
        {"Toes_R","RightToes"},
        {"Finger_L_Index_Distal","Left Index Distal"},
        {"Finger_L_Middle_Distal","Left Middle Distal"},
        {"Finger_L_Ring_Distal","Left Ring Distal"},
        {"Finger_L_Pinky_Distal","Left Little Distal"},
        {"Finger_R_Index_Distal","Right Index Distal"},
        {"Finger_R_Middle_Distal","Right Middle Distal"},
        {"Finger_R_Ring_Distal","Right Ring Distal"},
        {"Finger_R_Pinky_Distal","Right Little Distal"},
        {"Jaw","Jaw"}
    };
    static SkeletonBone[] CreateKeleton(GameObject go)
    {
        var type = typeof(SkeletonBone);
        FieldInfo fieldInfo=type.GetField("parentName", BindingFlags.Instance | BindingFlags.NonPublic);
        Transform[] transforms = go.GetComponentsInChildren<Transform>();
        SkeletonBone[] skeletonBones = new SkeletonBone[transforms.Length];
        for (int i = 0; i < transforms.Length; i++)
        {
            var transform = transforms[i];
            var bone = new SkeletonBone
            {
                name = transform.name,
                position = transform.localPosition,
                rotation = transform.localRotation,
                scale = transform.localScale
            };
            object o = bone;
            fieldInfo.SetValue(bone, transform.parent.name);
            bone = (SkeletonBone)o;
            skeletonBones[i] = bone;
        }
        return skeletonBones;
    }

    static HumanBone[] CreateHuman(GameObject go, Dictionary<string, string> map)
    {
        Transform[] transforms = go.GetComponentsInChildren<Transform>();
        List<HumanBone> humanBones = new List<HumanBone>();
        foreach (Transform transform in transforms)
        {
            if (map.TryGetValue(transform.name, out string name))
            {
                var humanBone = new HumanBone
                {
                    boneName = transform.name,
                    humanName = name,
                    limit = new HumanLimit()
                };
                humanBone.limit.useDefaultValues = true;
                humanBones.Add(humanBone);
            }
        }
        return humanBones.ToArray();
    }

    public static Avatar CreateAvatar(GameObject go, Dictionary<string, string> map)
    {
        return AvatarBuilder.BuildHumanAvatar(go, new HumanDescription
        {
            armStretch = 0.05f,
            feetSpacing = 0f,
            hasTranslationDoF = false,
            legStretch = 0.05f,
            lowerArmTwist = 0.5f,
            lowerLegTwist = 0.5f,
            upperArmTwist = 0.5f,
            upperLegTwist = 0.5f,
            skeleton = CreateKeleton(go),
            human = CreateHuman(go, map)
        });
    }
}

根据骨骼名称拆分动画

假如你设置了Avatar动画融合看起来还是很奇怪。那么你可以手动对动画拆分。例如行走动画拆成下半身走动,攻击动画拆成上半身攻击。对拆分后的动画规划到同一个层。运行起来的时候就是一边攻击一边走路

public static class AnimationClipUtitlty
{
    public static AnimationClip CreateClip(AnimationClip clip,string[] bones)
    {
        var resultClip = new AnimationClip();
        resultClip.legacy = clip.legacy;
        resultClip.wrapMode = clip.wrapMode;
        resultClip.frameRate = clip.frameRate;

        var setting = AnimationUtility.GetAnimationClipSettings(clip);
        AnimationUtility.SetAnimationClipSettings(resultClip, setting);

        foreach (var binding in AnimationUtility.GetCurveBindings(clip))
        {
            if (bones.Contains(Path.GetFileName(binding.path)))
            {
                var curve = AnimationUtility.GetEditorCurve(clip, binding);
                resultClip.SetCurve(binding.path, binding.type, binding.propertyName, curve);

            }
        }
        return resultClip;
    }
}