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

相关推荐
公子小六3 分钟前
基于.NET的Windows窗体编程之WinForms布局简介
windows·microsoft·c#·.net
zaim16 分钟前
计算机的错误计算(二百二十六)
java·python·c#·c·错数·mpmath
William_cl40 分钟前
[特殊字符]C# ASP.NET 架构封神之路:分层 + 仓储 + EFCore,写出企业级可维护代码!
架构·c#·asp.net
tq6J5Yg141 小时前
.NET 10 & C# 14 New Features 新增功能介绍-带修饰符的简单 lambda 参数
开发语言·c#·.net
fe7tQnVan1 小时前
从玩具到生产:基于 ChromaDB 打造工程级 RAG 系统
开发语言·c#
ySq0REx011 小时前
.NET 10 & C# 14 New Features 新增功能介绍-.NET CLI工具改进
开发语言·c#·.net
张人玉1 小时前
C#程序设计编程二维码识别程序
开发语言·c#·二维码
sR916Mecz1 小时前
JavaParser使用指南
开发语言·c#
WarPigs1 小时前
Unity单例笔记
unity·游戏引擎
数据的世界0116 小时前
C#4.0权威指南第12章:接口
开发语言·c#