找回密码
 立即注册
查看: 112|回复: 0

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

[复制链接]
发表于 2024-7-15 18:41 | 显示全部楼层 |阅读模式
一、设计意图

使用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)。
全部代码如下:
  1. namespace TimelineKit
  2. {
  3.     using UnityEngine.Playables;
  4.    
  5.     public abstract class BaseBehaviour : PlayableBehaviour
  6.     {
  7.         private bool _started;
  8.         private bool _played;
  9.         private object _playerData;
  10.         private Playable _playable = Playable.Null;
  11.         private PlayableAsset _asset;
  12.         
  13.         public static Playable CreatePlayable<T>(PlayableGraph graph, PlayableAsset data) where T : BaseBehaviour, new()
  14.         {
  15.             var playable = ScriptPlayable<T>.Create(graph);
  16.             T behaviour = playable.GetBehaviour();
  17.             behaviour._asset = data;
  18.             behaviour._playable = playable;
  19.             return playable;
  20.         }
  21.         #region protected  
  22.         protected T GetData<T>() where T : PlayableAsset => _asset as T;
  23.         protected float Time => (float)_playable.GetTime();
  24.         protected float Duration => (float) _playable.GetDuration();
  25.         protected float Percent => (float) (_playable.GetDuration().Equals(0) ? 0 : _playable.GetTime() / _playable.GetDuration());
  26.         #endregion protected
  27.         #region base
  28.         protected virtual void OnCreate() { }
  29.         protected virtual void OnDestroy() { }
  30.         protected virtual void OnStart(object binding) { }
  31.         protected virtual void OnUpdate(object binding, float deltaTime) { }
  32.         protected virtual void OnStop(object binding) { }
  33.         #endregion base
  34.         #region sealed
  35.         public sealed override void OnBehaviourPlay(Playable playable, FrameData info)
  36.         {
  37.             if (_played)
  38.                 return;
  39.             _started = false;
  40.             _played = true;
  41.         }
  42.         public sealed override void OnBehaviourPause(Playable playable, FrameData info)
  43.         {
  44.             if (!_played)
  45.                 return;
  46.             OnStop(_playerData);
  47.             _played = false;
  48.         }
  49.         public sealed override void OnPlayableCreate(Playable playable)
  50.         {
  51.             OnCreate();
  52.         }
  53.         public sealed override void OnPlayableDestroy(Playable playable)
  54.         {
  55.             OnDestroy();
  56.         }
  57.         public sealed override void OnGraphStart(Playable playable) { }
  58.         public sealed override void OnGraphStop(Playable playable) { }
  59.         public sealed override void PrepareData(Playable playable, FrameData info) { }
  60.         public sealed override void PrepareFrame(Playable playable, FrameData info) { }
  61.         public sealed override void ProcessFrame(Playable playable, FrameData info, object playerData)
  62.         {
  63.             if (!_played)
  64.                 return;
  65.             if (!_started)
  66.             {
  67.                 _started = true;
  68.                 _playerData = playerData;
  69.                 OnStart(_playerData);
  70.             }
  71.             OnUpdate(_playerData, info.deltaTime);
  72.         }
  73.         #endregion sealed
  74.     }
  75. }
复制代码
2、PlayableAsset封装

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

TrackAsset的封装和PlayableAsset的封装思路类似,也是对Create方式进行重载权限设置,定义了BaceTrackAsset和BaceTrackAsset<T>两种类型,此中使用泛型的类型可以用来创建Mixer混合器。
全部代码如下:
  1. namespace TimelineKit
  2. {
  3.     using UnityEngine;
  4.     using UnityEngine.Playables;
  5.     using UnityEngine.Timeline;
  6.     public class BaseTrackAsset : TrackAsset
  7.     {
  8.         protected sealed override Playable CreatePlayable(PlayableGraph graph, GameObject gameObject, TimelineClip clip)
  9.         {
  10.             return base.CreatePlayable(graph, gameObject, clip);
  11.         }
  12.         public sealed override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
  13.         {
  14.             return base.CreateTrackMixer(graph, go, inputCount);
  15.         }
  16.     }
  17.     public class BaseTrackAsset<T> : TrackAsset where T : BaseBehaviour, new()
  18.     {
  19.         protected sealed override Playable CreatePlayable(PlayableGraph graph, GameObject gameObject, TimelineClip clip)
  20.         {
  21.             return base.CreatePlayable(graph, gameObject, clip);
  22.         }
  23.         public sealed override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
  24.         {
  25.             var mixer = BaseBehaviour.CreatePlayable<T>(graph, this);
  26.             mixer.SetInputCount(inputCount);
  27.             return mixer;
  28.         }
  29.     }
  30. }
复制代码
四、使用案例

使用上面包装好的Timeline组件,可以很容易的对Timeline功能进行扩展,例如我们要实现一个Timeline控制光强的功能。
TrackAsset:
  1. namespace TimelineKit
  2. {
  3.     using UnityEngine;
  4.     using UnityEngine.Timeline;
  5.    
  6.     [TrackColor(1f,1f,0f)]
  7.     [TrackClipType(typeof(LightClipAsset))]
  8.     [TrackBindingType(typeof(Light))]
  9.     public class LightTrackAsset : BaseTrackAsset { }
  10. }
复制代码
BaseClipAsset实现:
  1. namespace TimelineKit
  2. {
  3.     public class LightClipAsset : BaseClipAsset<LightBehaviour>
  4.     {
  5.         public float StartIntensity;
  6.         public float EndIntensity;
  7.     }
  8. }
复制代码
BaseBehaviour实现:
  1. namespace TimelineKit
  2. {
  3.     using UnityEngine;
  4.     public class LightBehaviour : BaseBehaviour
  5.     {
  6.         protected override void OnUpdate(object binding, float deltaTime)
  7.         {
  8.             if (binding is Light light)
  9.             {
  10.                 var data = GetData<LightClipAsset>();
  11.                 light.intensity = Mathf.Lerp(data.StartIntensity,data.EndIntensity, Percent);
  12.             }
  13.         }
  14.     }
  15. }
复制代码
做了个简易的场景,最终效果如下:


https://www.zhihu.com/video/1630530910461411328

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-21 11:26 , Processed in 0.067046 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表