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

相关推荐
hixiong12311 小时前
C# OpenvinoSharp使用RAD进行缺陷检测
开发语言·人工智能·c#·openvino
InCerry12 小时前
C# .NET 周刊|2026年2月4期
c#·.net周报·.net周刊
刘欣的博客13 小时前
C# 发送飞书webhook消息
c#·飞书·发消息
wearegogog12316 小时前
C# Modbus 协议实现
开发语言·c#
bugcome_com17 小时前
C# 泛型(Generic)完全指南:从基础到高级应用
c#
我是唐青枫17 小时前
C#.NET Memory 深入解析:跨异步边界的内存视图与高性能实战
开发语言·c#·.net
ellis197017 小时前
Unity资源管理框架Addressables[六] 内存管理
unity
人工智能AI技术17 小时前
AI Gateway 实战:基于 C# 与 YARP 构建多模型统一接入与路由网关
人工智能·c#
派葛穆18 小时前
Unity-鼠标悬停改变物体层级
unity·游戏引擎