TimeLine如何自定义轨道

一套清晰、可扩展的 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 控制与具体业务逻辑(对话)解耦,同时提供了精确的暂停机制,非常适合用在剧情演出、过场动画等场景,但是还有有很多没完善的,没考虑,主要作为一个参考。

相关推荐
吴可可12321 分钟前
点在线上判定与多段线分割
算法·c#
吴可可12341 分钟前
圆弧多段线离散化采样密度优化
算法·c#
吾日吾身三摆烂2 小时前
Unity协程(Coroutine)底层原理全解析
unity·游戏引擎
LF男男2 小时前
StarBullect.cs
unity
UWA3 小时前
Unity小游戏优化简谱 | 吃透底层逻辑,告别掉帧与流失
unity·性能优化·游戏引擎·小游戏开发
Unity-Plane4 小时前
QClaw 的再一次的深度体验
unity
雪豹阿伟4 小时前
4.C# —— 循环语句、break、continue
c#·上位机
两千次4 小时前
webpost
c#
思麟呀5 小时前
在C++基础上理解Csharp-2
开发语言·jvm·c++·c#
吴可可1237 小时前
用Bulge保持多段线圆弧连续性
算法·c#