基于TimeLine编辑角色动画(三)

编写一个KeyCodeListener  Playables

实现主要细节(使用到插件odin)

1.针对不同按键操作有不同应对方式:比如正常按下,长按,组合按键

2.将动画片段帧分为可输入帧范围和执行成功输入的可执行的帧范围

3.利用编辑器通过枚举控制编辑器属性显示隐藏。

[Serializable]
public class KeyState
{
    public KeyCode keyCode;
    [LabelText("切换的动画状态")]
    public AnimState animState;
    [HideInInspector]
    public bool canHandle;
    [LabelText("按键操作类型")]
    public KeyExecuteType keyExecuteType;
    [LabelText("以当前Clip为Frame范围为标准")]
    public Vector2Int acceptInputFrameRange;
    public Vector2Int executeOperationFrameRange;
    [LabelText("后续按键")]
    public KeyCode nextKeyCode;
    [LabelText("按下帧时长/在多少帧内按下连续按键")]
    public int downframe;
}
public enum KeyExecuteType
{
    Normal,
    LongPress,
    ContinuousPress
}
PlayableBehaviour 部分代码
通过info.frameId可以精确获得帧的序号,帧序号在TimeLine上是逐帧增加的,可以在OnBehaviourPlay中记录开始帧序号,当前clips运行的帧数=info.frameId-在OnBehaviourPlay中记录的
开始帧序号。
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using Sirenix.OdinInspector;

[Serializable]
public class InputKeyCodeListenerBehaviour : PlayableBehaviour
{
    [LabelText("操作列表,优先级从前到后")]
    public List<KeyState> keyStates = new List<KeyState>();
    [NonSerialized]
    public PlayerController playerController;
    bool isIn=false;
    ulong initframe=0;
    bool beginpress;
    int beginPressFrame;
    public override void OnPlayableCreate (Playable playable)
    {
        isIn = false;
        beginpress = false;
    }
    public override void OnBehaviourPlay(Playable playable, FrameData info)
    {
        isIn = true;
        initframe = info.frameId;
        beginPressFrame = 0;
        beginpress = false;
        for (int i = 0; i < keyStates.Count; i++)
        {
            keyStates[i].canHandle = false;
        }
    }
    public override void OnBehaviourPause(Playable playable, FrameData info)
    {
        isIn = false;
    }
    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        if (!isIn)
            return;
        for (int i = 0; i < keyStates.Count; i++)
        {
            int currFrame = (int)(info.frameId - initframe);
            switch (keyStates[i].keyExecuteType)
            {
                case KeyExecuteType.Normal:

                    if (currFrame >= keyStates[i].acceptInputFrameRange.x && currFrame <= keyStates[i].acceptInputFrameRange.y)
                    {
                        keyStates[i].canHandle |= Input.GetKeyDown(keyStates[i].keyCode);
                    }
                    break;
                case KeyExecuteType.LongPress:
                    if (currFrame >= keyStates[i].acceptInputFrameRange.x && currFrame <= keyStates[i].acceptInputFrameRange.y)
                    {
                        if (Input.GetKey(keyStates[i].keyCode)&& !beginpress)
                        {
                            beginpress = true;
                            beginPressFrame = currFrame;
                        }
                        if (beginpress)
                        {
                            if (Input.GetKeyUp(keyStates[i].keyCode)){
                                beginpress = false;
                                beginPressFrame = 0;
                            }
                            if (currFrame- beginPressFrame>=keyStates[i].downframe)
                            {
                                keyStates[i].canHandle = true;
                            }
                        }
                    }

                    break;
                case KeyExecuteType.ContinuousPress:
                    if (currFrame >= keyStates[i].acceptInputFrameRange.x && currFrame <= keyStates[i].acceptInputFrameRange.y)
                    {
                        if (currFrame - beginPressFrame < keyStates[i].downframe - 1 && currFrame > 0&& beginpress)
                        {

                                keyStates[i].canHandle |= Input.GetKeyDown(keyStates[i].nextKeyCode);
                                break;
                        }
                        if (Input.GetKey(keyStates[i].keyCode) && !beginpress)
                        {
                            beginpress = true;
                            beginPressFrame = currFrame;
                        }
                    }
                    break;
            }
            if (currFrame > keyStates[i].executeOperationFrameRange.x && currFrame < keyStates[i].executeOperationFrameRange.y)
            {
                if (keyStates[i].canHandle)
                {
                    playerController.GetSkillController.StopPlay();
                    playerController.GetSkillController.Play(keyStates[i].animState);
                }
            }

        }

    }

}

对KeyState编辑的Editor类,通过按键不同操作显示相应的属性

using UnityEditor;
using UnityEngine;
using UnityEngine.Playables;

[CustomPropertyDrawer(typeof(KeyState))]
public class InputKeyCodeListenerEditor :PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        SerializedProperty keyExecuteType = property.FindPropertyRelative("keyExecuteType");
        SerializedProperty nextKeyCode = property.FindPropertyRelative("nextKeyCode");
        SerializedProperty downframe = property.FindPropertyRelative("downframe");
        EditorGUILayout.PropertyField(property.FindPropertyRelative("keyCode"));
        EditorGUILayout.PropertyField(property.FindPropertyRelative("animState"));
        EditorGUILayout.PropertyField(property.FindPropertyRelative("acceptInputFrameRange"));
        EditorGUILayout.PropertyField(property.FindPropertyRelative("executeOperationFrameRange"));
        EditorGUILayout.PropertyField(keyExecuteType);
        switch (keyExecuteType.enumValueIndex)
        {
            case 1:
                EditorGUILayout.PropertyField(downframe);
                break;
            case 2:
                EditorGUILayout.PropertyField(nextKeyCode);
                break;
        }
        property.serializedObject.ApplyModifiedProperties();
    }
}

PlayableAsset代码

using System;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

[Serializable]
public class InputKeyCodeListenerClip : PlayableAsset, ITimelineClipAsset
{
    public InputKeyCodeListenerBehaviour template = new InputKeyCodeListenerBehaviour ();
    public ExposedReference<PlayerController> playerController;

    public ClipCaps clipCaps
    {
        get { return ClipCaps.None; }
    }

    public override Playable CreatePlayable (PlayableGraph graph, GameObject owner)
    {
        var playable = ScriptPlayable<InputKeyCodeListenerBehaviour>.Create (graph, template);
        InputKeyCodeListenerBehaviour clone = playable.GetBehaviour ();
        clone.playerController = playerController.Resolve (graph.GetResolver ());
        return playable;
    }
}
PlayableBehaviourMixer,暂时不打算处理混合逻辑没用到,可以删掉
using System;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

public class InputKeyCodeListenerMixerBehaviour : PlayableBehaviour
{
    // NOTE: This function is called at runtime and edit time.  Keep that in mind when setting the values of properties.
    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        int inputCount = playable.GetInputCount ();

        for (int i = 0; i < inputCount; i++)
        {
            float inputWeight = playable.GetInputWeight(i);
            ScriptPlayable<InputKeyCodeListenerBehaviour> inputPlayable = (ScriptPlayable<InputKeyCodeListenerBehaviour>)playable.GetInput(i);
            InputKeyCodeListenerBehaviour input = inputPlayable.GetBehaviour ();
            
            // Use the above variables to process each frame of this playable.
            
        }
    }
}
TrackAsset没有修改
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

[TrackColor(0.855f, 0.8623f, 0.87f)]
[TrackClipType(typeof(InputKeyCodeListenerClip))]
public class InputKeyCodeListenerTrack : TrackAsset
{
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    {
        return ScriptPlayable<InputKeyCodeListenerMixerBehaviour>.Create (graph, inputCount);
    }
}
原文地址:https://www.cnblogs.com/DazeJiang/p/14369922.html