news 2026/9/19 4:18:26

TiXL 设置系统重构指南:三层设置模型与编辑器状态持久化架构解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TiXL 设置系统重构指南:三层设置模型与编辑器状态持久化架构解析

TiXL 设置系统重构指南:三层设置模型与编辑器状态持久化架构解析

【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3

本篇技术指南以 TiXL(t3)项目中的设置重构规划文档(Plan_ProjectSettings.md)为骨架,系统讲解 TiXL 当前采用的三层设置模型——App 级(CoreSettings)、Composition 级(CompositionSettings)、Project-global 级(根 Operator 的.t3ui状态),以及渲染设置、输出窗口状态、时间线状态、窗口布局等编辑器状态的持久化机制。读完本文,你将掌握 TiXL 各类设置分别存于哪个文件、由哪个访问器读取、如何通过 breadcrumb 继承、如何进行旧格式迁移与克隆,并能据此理解或排查 TiXL 项目中"设置不生效 / 状态丢失 / 布局未恢复"一类问题。

一、背景与动机:一次"同名异义"驱动的设置重构

在 TiXL 早期代码中,ProjectSettings这个名字同时被用于全局设置每个 Symbol 的播放设置,导致以下混乱:

  • 全局的ProjectSettings(保存于projectSettings.json)与 per-symbol 的PlaybackSettings命名歧义;
  • 部分本应属于单个 Composition 的字段(IO、性能开关)却散落在应用级配置里;
  • 设置界面长期以弹窗(Popup)形式存在,缺乏格式版本管理与窗口化交互;
  • 切换 Composition 时渲染设置存在"引用共享"的克隆缺陷。

重构的核心动作(对应规划文档 "Completed Work" 部分)可以归纳为三次重命名 + 一次分层

重构前重构后序列化位置用途
全局ProjectSettingsCoreSettingsprojectSettings.json应用级设置(保持原文件名不变)
per-symbolPlaybackSettingsCompositionSettings(历经ProjectSettingsSymbolSettings过渡).t3文件每个 Composition 的播放 / 音频 / 导出配置
Symbol.ProjectSettingsSymbol.CompositionSettingsSymbol 数据模型上的挂载点

同时,设置类整体迁移到T3.Core.Settings命名空间(目录 Core/Settings),FileLocationsUserData也随之迁入;GlobalMute/GlobalPlaybackVolume更名为AppMute/AppVolume;IO 与性能配置被移回CoreSettings(它们是应用级而非 per-composition 的)。出于向后兼容,.t3文件中的 JSON key 仍保留"ProjectSettings"

二、三层设置模型总览

重构后的架构将设置严格划分为三个层级,每一层都有独立的存储文件、生命周期与访问入口:

层级存储位置典型内容访问方式
Project-global(项目全局)根 Operator 的.t3ui输出窗口状态、窗口布局与可见性;IO(OSC 端口)与性能开关则在CoreSettingsOutputWindow.State(经_lastSyncedSymbolUi读取根 op 的 SymbolUi)
Per-symbol Composition(随 Composition 继承)所在 Symbol 的.t3(渲染设置与时间线状态在.t3ui播放配置(BPM、soundtrack、音频源、同步方式)、音频混音、导出配置、渲染设置、时间线视图状态CompositionSettings.Current(breadcrumb 向上遍历继承)
App-level(应用级)projectSettings.jsonAppMute/AppVolume、MIDI 捕获限制、默认 OSC 端口、性能与日志开关CoreSettings.ConfigUserSettings.Config

三个层级中最关键的设计意图是:随项目走的数据进入.t3/.t3ui,随机器/用户走的数据进入projectSettings.json与用户配置文件。例如音频输入设备在CompositionSettings.Playback.AudioInputDeviceName中按项目记录,而机器特定的默认输入设备则通过CoreSettings.ConfigData.LocalAudioInputDeviceName保留在projectSettings.json中,从而保证共享项目在另一台机器上仍能解析到真实输入设备。

三、App 级设置:CoreSettings 与 projectSettings.json

CoreSettings(源码:Core/IO/CoreSettings.cs)是跨 Core、Editor、Player 三端共享的全局应用设置,序列化到设置目录下的projectSettings.json(文件名在重构中刻意保留,避免迁移成本)。其ConfigData内嵌类包含:

字段默认值说明
AppMute/AppVolumefalse/1全局静音与音量(由工具栏音频 toggle 直接切换,而非 per-project 的 SoundtrackMute)
LimitMidiDeviceCapturenull限制 MIDI 设备捕获
DefaultOscPort8000默认 OSC 端口
LocalAudioInputDeviceName机器特定的 WASAPI 输入设备,保持共享项目可移植
TimeClipSuspendingtrue时间剪辑挂起优化开关
SkipOptimizationfalse跳过优化
EnableDirectXDebugfalseDirectX 调试层开关
EnableBeatSyncProfilingfalseBeat 同步性能剖析
LogAssemblyVersionMismatches/LogCompilationDetails/LogAssemblyLoadingDetails/LogFileEventsfalse各类日志开关
UseProcessScopedShadowCopiesfalse4.3 之前的进程级 shadow copy 逃生舱

底层机制:Settings<T> 基类

CoreSettings继承自通用的 Core/IO/Settings.cs 中的Settings<T>基类,该基类承担了所有 JSON 配置文件的读写职责:

  • 通过Settings<T>.Defaults提供默认值,Settings<T>.Config为当前生效实例;
  • 构造时从FileLocations.SettingsDirectory下加载 JSON;文件缺失时以默认值新建;
  • 若文件存在但无法读取(被其他实例锁定或损坏),会保留磁盘文件不动_preserveFileOnDisk),避免退出时用默认值覆盖用户真实配置,并记录警告;
  • 注册ProcessExit事件在退出时自动Save()
  • 子类可重写OnBeforeSave()在序列化前归一化数据。

四、Composition 级设置:CompositionSettings 与 breadcrumb 继承

CompositionSettings(源码:Core/Settings/CompositionSettings.cs)是本次重构的核心成果——每个 Symbol 可以拥有自己的项目设置,并且通过图层次(breadcrumb)向上遍历继承:当某 Composition 未定义设置时,会沿父级查找,直到找到最近的"定义设置"的 Symbol。CompositionSettings.Current静态访问器等价于Animation.Playback.Current?.Settings ?? Defaults,即 null 安全地回退到默认值。

4.1 顶层结构与主要枚举

public sealed class CompositionSettings { public static CompositionSettings Defaults { get; } = new(); public static CompositionSettings Current => Animation.Playback.Current?.Settings ?? Defaults; public bool Enabled { get; set; } public PlaybackConfig Playback { get; init; } = new(); public AudioMixConfig Audio { get; init; } = new(); public ExportConfig Export { get; init; } = new(); public ProxyConfig Proxy { get; init; } = new(); }

Enabled是"本 Symbol 是否主动定义设置"的总开关——关闭时 UI 会提示当前继承自哪个父 Composition。其中几个关键枚举(在 CompositionSettings.cs 的#region Enums中定义):

  • AudioSourcesProjectSoundTrack(时间线驱动的项目配乐)/ExternalDevice(外部设备);
  • SyncModesTimeline/Tapping(手动打拍子驱动时钟);
  • BeatLockSourcesOnsetDetection(经典瞬态检测)/PhaseModel(DanceAi 神经网络的 bar-phase 模型)/PhaseModelRaw(网络原始输出,供对比);
  • BpmChangeModesStretchWithBeat(保持 bar 数值不变,内容随 BPM 变速)/KeepSeconds(保持秒数不变,仅移动网格)。

4.2 四个配置子类与默认值

PlaybackConfig(播放配置)

字段默认值说明
Bpm120项目速度;TiXL 动画单位是 bar,BPM 直接控制动画速度
OnBpmChangeBPM 修改对 bar 定时内容(剪辑位置、关键帧、循环范围)的处理方式
AudioClips空列表时间线音频剪辑(TimelineAudioClip列表)
AudioSource音频源:项目配乐或外部设备
Syncing同步方式:Timeline 或 Tapping
UsesBeatTapping计算属性AudioSource == ExternalDevice && Syncing == Tapping时成立
AudioInputDeviceName输入设备名,空 = 用默认输入
AudioGainFactor/AudioDecayFactor1/0.9输入信号增益与 [AudioReaction] 的衰减因子
EnableAudioBeatLockingtrue是否启用音频节拍锁定(编辑器会寻找低音瞬态、hi-hat、军鼓并锁定播放速度)
BeatLockSource/BeatLockSmoothing— /0.5节拍锁定来源与平滑度(0 紧跟模型可能抖动,1 信任运行节奏缓慢修正)
BeatLockAudioOffsetSec0相位偏移(±1s 范围),用于补偿视频处理设备造成的输出延迟

AudioMixConfig(音频混音)

字段默认值说明
SoundtrackMute/OperatorMutefalse/false配乐 / Operator 静音
SoundtrackVolume/OperatorVolume0.5/1配乐 / Operator 音量
AudioResyncThreshold0.04音频偏离动画超过该阈值(推荐 0.02s~0.05s)时触发重新同步

ExportConfig(导出可执行文件配置)Title/Author(空则分别回退到 Operator 名/包名)、DefaultWindowMode = FullscreenEnablePlaybackControlWithKeyboard = true(方向键跳转 + 空格暂停)、PreferredWidth/Height = 1920×1080ShowLogs = falseSkipStartupDialog = false(仍可用--dialog强制呼出)、StripUnusedOperators = true(只打包与导出输出相连的 Operator 及自动播放的音频 Operator)。

ProxyConfig(预览代理配置)Format = ProRes(仅限 all-intra/LGPL 编码,绝不使用 H.264/HEVC 即 libx264/libx265,因其为 GPL)、Resolution = 0.5(源分辨率的比例)、UseForPreview = true(预览/拖动时播放代理,渲染始终用全分辨率源)。

4.3 序列化与旧格式迁移

CompositionSettings.WriteToJson仅在Enabled、存在音频剪辑或 Proxy 非默认时写出;写出的 JSON key 为"ProjectSettings"(顶层对象下),包含PlaybackAudioExportProxy四个子对象。读取端(ReadFromJson)则做了三重兼容:

  1. 新格式优先:优先取"ProjectSettings"key,若其下存在"Playback"子对象则按新嵌套格式读取;
  2. 旧 key 兼容:找不到"ProjectSettings"时回退到旧的"PlaybackSettings"key;
  3. 旧扁平格式迁移:若"ProjectSettings"内没有"Playback"子对象,按旧版扁平格式读取——包括将音频剪辑从 Symbol 根或 settings 根读取,以及BPM 迁移:旧项目把 BPM 存在剪辑本身,新模型只保留在Playback.Bpm;加载时若发现Playback.Bpm == 0而主配乐剪辑带LegacyBpmForMigration,会一次性复制进Playback.Bpm并自动Enabled = true

此外,加载音频剪辑时对"绝对路径已不存在的非主配乐剪辑"会直接丢弃并告警(IsUnmanageableMissingClip),因为这类死引用没有 UI 可以移除,避免每帧重复注册和报错;相对包路径的剪辑则保留,交由资源消费者延迟解析。

4.4 克隆:用序列化做深拷贝

规划文档特别提到"修复了切换 Composition 时克隆 bug(原来是共享引用,现在克隆)"。CompositionSettings.Clone()的实现策略是通过自己的序列化往返深拷贝:先把设置写入 JSON,再ReadFromJson读回,保证克隆结果与源设置的 save/load 往返完全一致——用于将 Symbol 复制为新类型时隔离设置实例。

五、Project-global 设置:根 Operator 的 .t3ui 状态

这一层存储于根 Operator 的.t3ui(整个项目只有一份),包含输出窗口状态与窗口布局。规划文档强调这些数据"属于项目"而非"属于某个 Composition",因此读取时使用ProjectView.Focused.RootInstance而非当前 Composition。

5.1 OutputWindowState(输出窗口状态)

源码 Editor/Gui/Windows/Output/OutputWindowState.cs 定义了每个输出窗口的持久化状态,以 JSON 数组形式存储在.t3ui"OutputWindows"key 下,支持多个输出窗口

  • GizmoShowGizmos(默认On)、TransformGizmoMode(默认Move)——直接以State作为后备存储(无拷贝);
  • 背景与相机BackgroundColorCameraControlMode(默认AutoUseFirstCam,另有SceneViewerFollowing/UseViewer/PickedACamera)、CameraPosition(默认[0,0,2.414],即默认相机距离)、CameraTargetCameraRollCameraSpeed——基于拷贝,每帧通过SyncCopyFieldsToState()同步;
  • 分辨率ResolutionTitleResolutionWidth/HeightResolutionUseAsAspectRatio
  • Pinning(钉住)IsPinnedPinnedInstancePathGuid[])、PinnedOutputId

5.2 TimelineState(时间线视图状态)

源码 Editor/Gui/Windows/TimeLine/TimelineState.cs 存储每个 Symbol 的.t3ui中("Timeline"key)。刻意只持久化视图状态ScaleXScrollXModeDopeView/CurveEditor),而 Loop 范围与播放位置保持为Playback对象上的运行时状态,不落盘。切换 Composition 时通过SyncStateWithComposition完成保存/加载。此外还包含TimelineHeight(项目级窗口布局,仅根 Symbol 的副本会被写入)、InlineDataClipEditEnabledDetailsAreaHeight(内联编辑面板高度,默认 200px)、SourceExtent(Symbol 有意义内容的 bar 范围,供 TimeClip 使用)等字段。

5.3 窗口布局与可见性

  • SymbolUi.WindowLayout——ImGui 布局 INI 字符串(经SaveIniSettingsToMemory()获取);
  • SymbolUi.WindowLayoutImGuiVersion——版本标记,仅当 ImGui 主版本号一致时才恢复布局,避免跨版本布局错乱;
  • SymbolUi.WindowVisibility——Dictionary<string, bool>,窗口标题 → 可见性;
  • 保存时机为ProjectView.Close(),恢复时机为ProjectView.SetAsFocused()
  • 通过UserSettings.Config.SaveWindowLayoutsWithProjects(默认true,见 Editor/Gui/UiHelpers/UserSettings.cs)选择是否随项目保存布局。

规划文档还记录了该层的三处修复:ProjectView.Close不再调用Unpin()而是保存状态;Pinning 在项目关闭/重开后仍能存活(陈旧的 ProjectView 会重新解析到当前实例);切换项目时状态保存到正确的项目(跟踪_lastSyncedSymbolUi)。

六、渲染设置:RenderSettings 直接读写 SymbolUi

规划文档提到"移除了ForNextExport中间层——RenderSettings.Current直接从SymbolUi.RenderSettings读写"。源码 Editor/Gui/Windows/RenderExport/RenderSettings.cs 印证了这一点:

  • RenderSettings.Current聚焦的 Composition的 SymbolUi 读取;首次访问时以默认值 + 旧版路径迁移初始化(迁移UserSettings中遗留的RenderVideoFilePathRenderSequenceFilePathRenderSequenceFileNameRenderSequencePrefix),且不标记为已修改——因为该 getter 会被纯 UI 读取触发(如输出窗口的渲染提示),不应弄脏 Composition;
  • 字段集:TimeReference(Bars/Seconds/Frames)、StartInBarsEndInBars(默认8)、FrameRate(默认60)、OverrideMotionBlurSamples(默认-1,即不覆盖)、RenderMode(Video/ImageSequence)、VideoCodec(默认 H264)、Bitrate(默认25_000_000)、AutoIncrementVersionNumber/CreateSubFolder/AutoIncrementSubFolder(默认true)、ExportAudio(默认true)、FileFormatTimeRange(Custom/Loop/Soundtrack/Continuous)、连续捕获时钟(Realtime/Deterministic)与帧率模式(FixedFps/Variable,Variable 保留未实现)、ResolutionFactor(默认1)以及渲染路径VideoFilePath = "./Render/render-v01.mp4"SequenceFilePath = "./ImageSequence/"SequenceFileName = "v01"SequencePrefix = "render"
  • 序列化 key 为.t3ui中的"RenderExport"Clone()/CopyFrom()用于复制与切换 Composition 时隔离实例。

渲染导出时,输出窗口使用的是RenderProcess.GetActiveOrRequestedSettings()而不是Current,确保"正在渲染中"与"UI 当前显示"互不干扰。另外修复了AddInt重置按钮因缺少isDefault检查而从不高亮的问题。

七、Composition Settings 窗口:从弹窗到可停靠窗口

规划文档记录设置界面已从 Popup 升级为可停靠的Window(类ProjectSettingsWindow。源码 Editor/Gui/Windows/TimeLine/ProjectSettingsWindow.cs 显示其标题为 "Composition Settings",左侧导航分五个分类:

分类面板标题核心内容
PlaybackTiming项目模式(Animation=ProjectSoundTrack / Live Interactive=ExternalDevice)、BPM、On BPM Change 行为、Sync Mode、Beat Lock 源与平滑度、Beat Sync Offset
AudioProject AudioResync Threshold、Main Volume、输入设备选择、Gain/Decay、电平表、Main Soundtrack 管理
ProxiesVideo ProxiesUse proxies for preview、Proxy Format(ProRes/Hap/Hap Alpha/Hap Q)、Resolution 比例、最小可用磁盘、代理存储清理
RecordingRecordingRecord 按钮捕获范围:Capture Audio、Capture IO(MIDI/OSC 子开关)
ExecutableExportTitle/Author、Window Mode、键盘播放控制、Preferred 分辨率、Skip Startup Dialog、Show Logs、Strip Unused Operators 与导出按钮

该窗口的交互细节与文档互相印证:

  • 顶部主开关 "Specify settings for <SymbolName>" 控制Enabled;未定义时提示 "Currently inheriting settings from …";
  • Soundtrack 由 [AudioClip] Operator 拥有:窗口只负责"创建或聚焦"主配乐——"Create Soundtrack" 会以一条可撤销的宏命令添加AudioClipop(AutoPlay=true、Display=BackgroundImage、Style=Waveform),剪辑的路径、偏移、修剪全部交给该 op;CompositionSettings.TryGetMainSoundtrack也同时支持设置列表与 op 提供的剪辑两种来源;
  • 窗口各面板的修改统一通过SymbolUi.FlagAsModified()标记脏状态;
  • 切换项目模式时UpdatePlaybackAndTimeline会同步切换Playback.Current(时间线或打拍时钟)、时间线可见性、归一化遗留的Syncing值。

在菜单与入口方面:App 菜单显示 "Composition Settings" 并在当前聚焦 op 定义设置时带勾选标记;时间线齿轮图标切换窗口可见性;工具栏音频 toggle 切换的是应用级AppMute(而非 per-project 的 SoundtrackMute)。

八、访问模式速查

规划文档归纳了五个访问入口,全部与源码一一对应:

访问器语义源码依据
CompositionSettings.Currentper-symbol Composition 配置,null 安全回退 Defaults,breadcrumb 继承Core/Settings/CompositionSettings.cs
RenderSettings.Current直接读写聚焦 SymbolUi 的渲染设置,lazy-init + 旧版迁移Editor/Gui/Windows/RenderExport/RenderSettings.cs
OutputWindow.State_lastSyncedSymbolUi读写根 op SymbolUi 的输出窗口状态Editor/Gui/Windows/Output/OutputWindowState.cs
CoreSettings.ConfigApp 级设置(projectSettings.jsonCore/IO/CoreSettings.cs
UserSettings.Config用户偏好(含SaveWindowLayoutsWithProjectsViewedCanvasAreaForSymbolChildId等)Editor/Gui/UiHelpers/UserSettings.cs

九、后续演进方向

规划文档的 "Future Work" 部分列出了尚未完成的演进计划(从源码结构看,其中部分已有雏形):

  • Graph View State:每个 Symbol 的画布位置与缩放(UserSettings.ViewedCanvasAreaForSymbolChildId已有部分实现)、选中 Operator 集合的持久化;
  • 默认 Composition 设置:计划在UserSettings.ConfigData增加DefaultCompositionSettings字段,让新 Composition 从用户默认值初始化,并使 Composition Settings 窗口的"恢复默认"按钮使用该默认值;
  • 自动视觉测试:以编辑器状态持久化为前提——加载测试项目 → 恢复编辑器状态 → 截图 → 与参考图对比(见规划文档中提及的另一份文档 Plan_AutomaticTests.md),状态持久化是确定性测试截图的前置条件;
  • 其他:每项目首选分辨率/宽高比列表、每项目 key-value 参数存储、已钉住的动画参数(DopeSheetArea.PinnedParametersHashes)。

这些内容属于规划中的方向而非已落地功能,读者在阅读源码时应以当前实际实现为准。

总结

TiXL 的设置系统经过本轮重构后形成了清晰的分层边界:应用级数据进projectSettings.json,随 Composition 的数据进.t3CompositionSettings,JSON key"ProjectSettings"兼容旧版),项目全局的窗口/布局/输出窗口状态进根 Operator 的.t3uiCompositionSettings.Current的 breadcrumb 继承、RenderSettings.Current的直接读写、基于序列化的深拷贝克隆、以及Settings<T>基类的损坏保护与退出自动保存,共同构成了这套既可移植共享、又可逐机定制的持久化架构。对于想要为 TiXL 贡献新设置项或排查状态持久化问题的开发者而言,本文的层级对照表与访问模式速查是快速定位的入口。

【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 4:16:46

Unity资源管理避坑指南:从引用混乱到内存泄漏的实战解析

1. 从一次崩溃说起&#xff1a;Unity资源管理到底难在哪如果你做过一段时间的Unity项目&#xff0c;大概率经历过这样的场景&#xff1a;编辑器里跑得好好的&#xff0c;打包出来一加载场景就闪退&#xff1b;或者美术同学发来一批新模型&#xff0c;导入之后工程体积直接翻倍&…

作者头像 李华
网站建设 2026/9/19 4:15:10

温湿度传感器以太网通信中CRC16与CRC32选型实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 4:14:30

免费激活 Beyond Compare 5:BCompare_Keygen 密钥生成器完整入门指南

免费激活 Beyond Compare 5&#xff1a;BCompare_Keygen 密钥生成器完整入门指南 【免费下载链接】BCompare_Keygen Keygen for BCompare 5 项目地址: https://gitcode.com/gh_mirrors/bc/BCompare_Keygen BCompare_Keygen 是一个基于 Python3 的免费密钥生成器项目&…

作者头像 李华
网站建设 2026/9/19 4:12:54

Qoder 安装与 API 配置实战:从下载到上手的完整指南

程序员圈子里最近冒出来一个说法&#xff0c;叫“国民编程神器”&#xff0c;说的就是 Qoder。我第一次听到是在一个技术群里&#xff0c;有朋友晒了张截图&#xff0c;说两个下午用 Qoder 把一个内网运维脚本改成了带界面的小工具&#xff0c;群里瞬间就炸了。抱着试试看的心态…

作者头像 李华