• 基于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);
        }
    }
  • 相关阅读:
    小三角 + 右箭头【纯css】
    小程序自定义弹窗【解决点透问题】
    分页存储过程
    SQL如何用一条语句批量修改表中不同数据
    Dapper批量更新
    vue中 $event 的用法--获取当前父元素,子元素,兄弟元素
    T-sql语句查询执行顺序
    Lucene入门
    Spring Boot入门
    SSM-Spring一些知识点
  • 原文地址:https://www.cnblogs.com/DazeJiang/p/14369922.html
Copyright © 2020-2023  润新知