jackboy 发表于 2024-7-15 18:41

游戏开发技术--Timeline封装(Unity)

一、设计意图

使用Unity中的Timeline功能进行技能效果或者过场剧情开发的都知道,对于Timeline功能的扩展,凡是要扩展以下几个类:

[*]TrackAsset:轨道资源,用来创建片段和Playable混合器,提供序列化数据与Binding。
[*]PlayableAsset:片段资源,用来创建Playable以及提供序列化数据。
[*]PlayableBehaviour:逻辑行为,用来实现Playable具体的业务逻辑。
然而,在扩展过程中,会发现一些不便当的情况,最明显的就是PlayableBehaviour的回调函数,对于不熟悉Playable的开发者来说,需要必然的理解成本。为此,我对Timeline的布局进行了进一步的封装。
二、设计思路

对于TrackAsset和PlayableAsset来说,设计上但愿它们只是提供具体序列化数据,而屏蔽掉对于Playable的创建过程。而对于PlayableBehaviour来说,但愿它仅用于实现业务逻辑,同时提炼回调函数使其友好于普通的Unity游戏开发者,而不需要过多了解Playable的机制。
三、实现方式

1、PlayableBehaviour封装

先来对PlayableBehaviour进行封装,定义BaseBehaviour类,对原有的回调函数使用修饰符sealed,定义通用的回调函数(OnCreate,OnDestroy,OnStart,OnUpdate,OnStop),同时提供一些获取数据的接口(GetData,Time,Duration,Percent)。
全部代码如下:
namespace TimelineKit
{
    using UnityEngine.Playables;
   
    public abstract class BaseBehaviour : PlayableBehaviour
    {
      private bool _started;
      private bool _played;
      private object _playerData;
      private Playable _playable = Playable.Null;
      private PlayableAsset _asset;
      
      public static Playable CreatePlayable<T>(PlayableGraph graph, PlayableAsset data) where T : BaseBehaviour, new()
      {
            var playable = ScriptPlayable<T>.Create(graph);
            T behaviour = playable.GetBehaviour();
            behaviour._asset = data;
            behaviour._playable = playable;
            return playable;
      }

      #region protected

      protected T GetData<T>() where T : PlayableAsset => _asset as T;
      protected float Time => (float)_playable.GetTime();
      protected float Duration => (float) _playable.GetDuration();
      protected float Percent => (float) (_playable.GetDuration().Equals(0) ? 0 : _playable.GetTime() / _playable.GetDuration());

      #endregion protected

      #region base
      protected virtual void OnCreate() { }
      protected virtual void OnDestroy() { }
      protected virtual void OnStart(object binding) { }
      protected virtual void OnUpdate(object binding, float deltaTime) { }
      protected virtual void OnStop(object binding) { }
      #endregion base

      #region sealed
      public sealed override void OnBehaviourPlay(Playable playable, FrameData info)
      {
            if (_played)
                return;
            _started = false;
            _played = true;
      }

      public sealed override void OnBehaviourPause(Playable playable, FrameData info)
      {
            if (!_played)
                return;
            OnStop(_playerData);
            _played = false;
      }

      public sealed override void OnPlayableCreate(Playable playable)
      {
            OnCreate();
      }

      public sealed override void OnPlayableDestroy(Playable playable)
      {
            OnDestroy();
      }

      public sealed override void OnGraphStart(Playable playable) { }

      public sealed override void OnGraphStop(Playable playable) { }

      public sealed override void PrepareData(Playable playable, FrameData info) { }

      public sealed override void PrepareFrame(Playable playable, FrameData info) { }

      public sealed override void ProcessFrame(Playable playable, FrameData info, object playerData)
      {
            if (!_played)
                return;
            if (!_started)
            {
                _started = true;
                _playerData = playerData;
                OnStart(_playerData);
            }
            OnUpdate(_playerData, info.deltaTime);
      }
      #endregion sealed
    }
}
2、PlayableAsset封装

为了和对应的PlayableBehaviour进行绑定,这里使用泛型对PlayableAsset进行封装,定义类型BaseClipAsset<T>,并实现了CreatePlayable的方式。因此担任此类型的子类只需要提供序列化数据即可。
全部代码如下:
namespace TimelineKit
{
    using UnityEngine;
    using UnityEngine.Playables;
   
    public class BaseClipAsset<T> : PlayableAsset where T : BaseBehaviour, new()
    {
      public sealed override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
      {
            return BaseBehaviour.CreatePlayable<T>(graph, this);
      }
    }
}3、TrackAsset封装

TrackAsset的封装和PlayableAsset的封装思路类似,也是对Create方式进行重载权限设置,定义了BaceTrackAsset和BaceTrackAsset<T>两种类型,此中使用泛型的类型可以用来创建Mixer混合器。
全部代码如下:
namespace TimelineKit
{
    using UnityEngine;
    using UnityEngine.Playables;
    using UnityEngine.Timeline;

    public class BaseTrackAsset : TrackAsset
    {
      protected sealed override Playable CreatePlayable(PlayableGraph graph, GameObject gameObject, TimelineClip clip)
      {
            return base.CreatePlayable(graph, gameObject, clip);
      }

      public sealed override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
      {
            return base.CreateTrackMixer(graph, go, inputCount);
      }
    }

    public class BaseTrackAsset<T> : TrackAsset where T : BaseBehaviour, new()
    {
      protected sealed override Playable CreatePlayable(PlayableGraph graph, GameObject gameObject, TimelineClip clip)
      {
            return base.CreatePlayable(graph, gameObject, clip);
      }

      public sealed override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
      {
            var mixer = BaseBehaviour.CreatePlayable<T>(graph, this);
            mixer.SetInputCount(inputCount);
            return mixer;
      }
    }
}四、使用案例

使用上面包装好的Timeline组件,可以很容易的对Timeline功能进行扩展,例如我们要实现一个Timeline控制光强的功能。
TrackAsset:
namespace TimelineKit
{
    using UnityEngine;
    using UnityEngine.Timeline;
   
   
   
   
    public class LightTrackAsset : BaseTrackAsset { }
}BaseClipAsset实现:
namespace TimelineKit
{
    public class LightClipAsset : BaseClipAsset<LightBehaviour>
    {
      public float StartIntensity;
      public float EndIntensity;
    }
}BaseBehaviour实现:
namespace TimelineKit
{
    using UnityEngine;

    public class LightBehaviour : BaseBehaviour
    {
      protected override void OnUpdate(object binding, float deltaTime)
      {
            if (binding is Light light)
            {
                var data = GetData<LightClipAsset>();
                light.intensity = Mathf.Lerp(data.StartIntensity,data.EndIntensity, Percent);
            }
      }
    }
}做了个简易的场景,最终效果如下:


https://www.zhihu.com/video/1630530910461411328
页: [1]
查看完整版本: 游戏开发技术--Timeline封装(Unity)