一套清晰、可扩展的 Timeline + 对话系统 框架设计,旨在实现精确暂停/恢复和事件驱动解耦,用作使用参考,如何自己定义播放轨道,Playable是Unity实现动画系统的底层逻辑,实际上无论是Animator还是Timeline的实现实际上都使用了它。
TimelineController ------ 每个 Timeline 的专属控制器
csharp
using UnityEngine;
using UnityEngine.Playables;
/// <summary>
/// 挂载在包含 PlayableDirector 的游戏对象上,负责单个 Timeline 的播放控制
/// </summary>
[RequireComponent(typeof(PlayableDirector))]
public class TimelineController : MonoBehaviour
{
private PlayableDirector director;
private PlayableGraph graph;
void Awake()
{
director = GetComponent<PlayableDirector>();
graph = director.playableGraph;
}
/// <summary> 暂停 Timeline(通过设置速度 = 0)</summary>
public void Pause()
{
if (graph.IsValid())
{
graph.GetRootPlayable(0).SetSpeed(0d);
Debug.Log($"[{name}] Timeline paused");
}
}
/// <summary> 恢复 Timeline(速度 = 1)</summary>
public void Resume()
{
if (graph.IsValid())
{
graph.GetRootPlayable(0).SetSpeed(1d);
Debug.Log($"[{name}] Timeline resumed");
}
}
/// <summary> 停止并重置 Timeline(回到起点)</summary>
public void Stop()
{
director.Stop();
}
/// <summary> 直接播放/恢复(如果已暂停则继续)</summary>
public void Play()
{
director.Play();
}
/// <summary> 获取当前播放状态</summary>
public PlayState State => director.state;
/// <summary> 获取或设置播放时间</summary>
public double Time
{
get => director.time;
set => director.time = value;
}
}
TimelineManager ------ 全局管理器(单例)
csharp
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 全局 Timeline 管理器,负责注册所有 TimelineController,
/// 并提供按名称/ID 暂停恢复的功能。
/// </summary>
public class TimelineManager : Singleton<TimelineManager>
{
private Dictionary<string, TimelineController> timelines = new Dictionary<string, TimelineController>();
/// <summary> 注册一个 Timeline(通常由 TimelineController 自己在 Start/Awake 时调用)</summary>
public void RegisterTimeline(string id, TimelineController controller)
{
if (!timelines.ContainsKey(id))
timelines.Add(id, controller);
else
Debug.LogWarning($"Timeline with id {id} already exists!");
}
/// <summary> 注销(对象销毁时调用)</summary>
public void UnregisterTimeline(string id)
{
if (timelines.ContainsKey(id))
timelines.Remove(id);
}
/// <summary> 通过 ID 获取 TimelineController </summary>
public TimelineController GetTimeline(string id)
{
timelines.TryGetValue(id, out var ctrl);
return ctrl;
}
/// <summary> 暂停指定 ID 的 Timeline </summary>
public void PauseTimeline(string id)
{
if (timelines.TryGetValue(id, out var ctrl))
ctrl.Pause();
}
/// <summary> 恢复指定 ID 的 Timeline </summary>
public void ResumeTimeline(string id)
{
if (timelines.TryGetValue(id, out var ctrl))
ctrl.Resume();
}
/// <summary> 暂停当前所有活跃的 Timeline </summary>
public void PauseAll()
{
foreach (var ctrl in timelines.Values)
ctrl.Pause();
}
/// <summary> 恢复所有 Timeline </summary>
public void ResumeAll()
{
foreach (var ctrl in timelines.Values)
ctrl.Resume();
}
}
DialogueBehaviour ------ 对话行为(PlayableBehaviour)
csharp
using UnityEngine;
using UnityEngine.Playables;
[System.Serializable]
public class DialogueBehaviour : PlayableBehaviour
{
public DialoguePiece dialoguePiece; // 对话数据(ScriptableObject 或普通类)
private PlayableDirector director;
private string timelineId; // 用于向管理器暂停时识别 Timeline
public override void OnPlayableCreate(Playable playable)
{
// 获取当前 Timeline 的 Director
director = playable.GetGraph().GetResolver() as PlayableDirector;
if (director != null)
{
// 用 Director 所在对象的名称作为 Timeline ID(也可自定义)
timelineId = director.gameObject.name;
}
}
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
// 触发对话显示事件(由 UI 系统监听)
EventHandler.CallShowDialogueEvent(dialoguePiece);
if (Application.isPlaying)
{
if (dialoguePiece != null && dialoguePiece.hasToPause)
{
// 需要暂停当前 Timeline
// 通过全局管理器暂停对应 ID 的 Timeline
TimelineManager.Instance.PauseTimeline(timelineId);
}
else
{
// 没有暂停需求,确保隐藏之前的对话(可选)
EventHandler.CallShowDialogueEvent(null);
}
}
}
public override void OnBehaviourPause(Playable playable, FrameData info)
{
// 当 Timeline 离开该片段时,可能也需要隐藏对话
// 但要注意:暂停时也会调用 OnBehaviourPause,需要区分
// 简单起见,可以用标志位判断是否因为暂停而离开
// 这里示例:如果是因为结束而离开,则隐藏对话
if (info.evaluationType == FrameData.EvaluationType.Playback) // 正常播放结束
{
EventHandler.CallShowDialogueEvent(null);
}
}
}
DialogueClip 和 DialogueTrack
csharp
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
// 对话片段
[System.Serializable]
public class DialogueClip : PlayableAsset, ITimelineClipAsset
{
public ClipCaps clipCaps => ClipCaps.None;
// 可编辑的对话行为实例
public DialogueBehaviour dialogue = new DialogueBehaviour();
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
// 将 dialogue 复制到 Playable 中
var playable = ScriptPlayable<DialogueBehaviour>.Create(graph, dialogue);
return playable;
}
}
// 对话轨道
[TrackClipType(typeof(DialogueClip))]
public class DialogueTrack : TrackAsset
{
// 轨道不需要额外逻辑,默认混合行为即可
}
这套框架将 Timeline 控制与具体业务逻辑(对话)解耦,同时提供了精确的暂停机制,非常适合用在剧情演出、过场动画等场景,但是还有有很多没完善的,没考虑,主要作为一个参考。