UI、输入、本地化、过场与媒体
本文分析这些表现系统的引擎接口与生命周期,不逐个评价 GTA 菜单页面。静态研究,未执行中间件或媒体文件。入口:Summary。
1. 输入是动作映射,UI 是独立消费方#
Base input 对键盘、鼠标、pad、joystick、虚拟键盘等设备做适配;ioSource 除设备类型和参数外还保存 device index,并区分系统/游戏来源。ioValue 跟踪前一帧和当前值,让上层判断状态变化;mapper 的目标包括多个来源映射到动作、玩家各自映射和 O(1) 动作读取。
死区、按钮阈值、模拟量归一化与 disable 策略是动作语义的一部分。DisableOptions 可控制禁用时是否仍更新、是否允许 sustained 读取。由此不能把“界面打开时禁用玩法输入”简单实现成丢掉所有设备事件:恢复时是否产生边沿、按住是否延续,需要明确策略。
头文件仍保留断开设备时行为的 TODO。这只能证明需要审慎检查此路径,不等于游戏没有其他断线处理。输入延迟还取决于采样阶段、游戏消费和渲染排队,不能只看设备 API 的速度。
2. Scaleform:资源、实例和线程窗口#
sfScaleformMovie 是共享内容,sfScaleformMovieView 是显示/执行实例。LoadMovie 可加载 .swf/.gfx 并关联纹理字典;DeleteMovie 注释明确其他引用可能让内存延迟释放,而纹理字典会被 release。
MovieView 的 Active 控制 manager 是否更新,Visible 控制 manager 是否绘制,两者彼此独立;直接调用 Update/Draw 不负责检查这两个标记。ApplyInput 也不是直接 Update 自动执行的步骤,manager 路径负责输入及相应 gating。因此“隐藏 UI”不一定停止 ActionScript 或原生回调开销。
Manager 的 BeginFrame/EndFrame 明确要求 render thread;PerformSafeModeOperations 要求没有 update/render/invoke 等其他 Scaleform 操作。这个安全窗口比“给 LoadMovie 加锁”更完整:中间件内部缓存、绘制命令和脚本 VM 都有生命周期。
世界空间 UI 需要 TWIPS 到世界单位的缩放,viewport 尺寸还影响细分和字体。UI 成本因此可能来自 ActionScript、几何细分、字体/纹理、绘制提交与 overdraw,不只是控件数量。
3. Framework UI 基础设施有明确配置范围#
fwui 提供消息、环形缓冲、导航 context/params/result 和输入量化。fwuiMultiProducerMessageQueue 是多生产者、单消费者模型:QueueMessage 持生产者临界区,Peek/Release 转发给底层 queue。不能把它称为完整无锁 MPMC 队列;Flush/Shutdown 的安全性也不能从单消费者读取推断为任意线程可调用。
模拟量经量化进入 UI 导航,滚轮示例按 ±0.5 阈值映射到 -1/0/1。连续设备信号与离散导航之间需要 repeat、deadzone 和上下文处理,避免摇杆微抖反复换焦点。
特别注意:fwui_config.h 的 FW_UI_ENABLED 由 IS_GEN9_PLATFORM || GEN9_UI_SIMULATION_ENABLED 控制,文件标记 2021+。当前源码集合包含不同阶段的实现;这套 Framework UI 不能被当作所有平台、所有 GTA V 版本的共同 UI 栈,也不能仅靠它否定 Scaleform 路径。
4. 本地化不仅是字符串表#
fwLanguagePack 管理语言标识、平台/服务语言映射、字体库、货币分隔、日期格式和单位转换。字符串内容、数字格式、字体和布局需要共同处理;只翻译文本无法保证显示正确。
fwTextDatabaseStreamingWrapper 将 text Store 封装成异步请求接口,Update 明确仅能在 update thread 调用。加载 extra-text metafile 只挂载 RPF 并注册内容,仍需请求具体数据库;Request 与 IsLoaded 分开。AddTextDatabaseRef 要求先确认已加载,Remove 必须与 Add 配对。
immediateLoad 为兼容旧阻塞文本路径而保留,接口直接提示谨慎使用。切语言会清理并重载文本。因此菜单打开、任务字幕和语言切换也可能触发 IO 与同步等待;文本体积小并不能证明不会卡顿。
对新项目,推荐 UI 持有稳定 key/handle,明确“尚未装载”和“key 不存在”的区别;语言切换时重建字体与布局依赖,并避免把短期字符串裸指针跨越数据库卸载。后者是设计建议,本次未逐个审计所有字符串调用者。
5. 过场是世界控制与资源编排#
cutsManager 具有 Load/ResumeLoad/Unload、Play/Pause、skip/cancel、对象级 loading/loaded、PreSceneUpdate/PostSceneUpdate 和分段时间接口。Load 的 bJustCutfile 模式只加载 cutfile,且此时 IsLoaded 也可能返回 true;不能脱离调用模式把单个布尔值解释为所有角色、动画和音频已就绪。
Unload 在播放/暂停状态先 Stop,再卸载资产;fade 和播放事件参与切换。这说明过场不仅是播放一条动画,而是暂时接管相机、实体、灯光和时间的事务。跳过、取消装载和中途切换都要求恢复世界状态并处理在途资源。
过场时间、内部时间、音频起止时间与游戏 time step 分开存在,给暂停和分段控制提供基础,但也产生对齐问题。新项目应定义谁是主时钟,哪些轨道可以丢帧、哪些事件必须恰好消费一次,并在 seek/skip 时明确补发或抑制事件的规则。
6. 视频解码和录制有独立状态机#
Framework MediaDecoder 将 decode state 与 play state 分开:准备、文件元信息、缓冲建立、active、error、cleanup;播放另有 paused、seek-then-pause/play、EOF 和 cleanup。获得文件信息不意味着已经创建缓冲,缓冲建立不意味着已经激活解码。
接口暴露音频 ringbuffer、显示帧、目标 seek 时间及视频缓冲锁,并按平台有不同状态数据布局。Suite bink 与 Framework/base video 覆盖不同媒体接入路径,不能概括成“所有视频都走 Bink”。编码/录制还有 texture pool、buffer pool 和平台接口,属于独立的 CPU/GPU/IO 消费者。
本文对媒体编码算法和各平台 capture SDK 为接口/目录级覆盖,未验证硬件编码能力或精确延迟。
7. 新项目建议与验证#
| 设计关注 | 需要验证的场景(未执行) |
|---|---|
| 更新、可见和输入焦点独立 | 隐藏但活动 UI;菜单切换时按键仍按住 |
| 中间件安全操作窗口 | Invoke/Draw 进行时取消 UI 载入或销毁 |
| 文本和字体驻留 | 缺失数据库、快速切语言、大字符集、长文本 |
| 过场事务 | 只加载 cutfile 后取消、动画加载中 skip、暂停后卸载 |
| 媒体双状态机 | seek 后立即关闭、音频欠载、录制与 streaming 争抢带宽 |
| 输入时序 | 不同帧率下动作边沿、设备断开/重连、禁用后恢复 |
8. 源码证据#
| 文件 | 入口 |
|---|---|
| mapper.h | 35–80 source/action;114 起 disable 策略 |
| scaleform.h | 241–306 MovieView;388–447 manager 安全窗口和资源 |
| fwui_config.h | 12 功能启用范围 |
| fwuiMultiProducerMessageQueue.h | 60–85 生产者锁和单消费者操作 |
| fwuiInputQuantization.h | 26–37 量化接口 |
| languagePack.h | 字体、格式、单位和语言映射 |
| textDatabaseStreamingWrapper.h | 40–143 请求、引用、立即加载和线程限制 |
| cutsmanager.h | 390–427 Load/Unload/Play;513–521 更新阶段 |
| mediadecoder.h | 52–125 状态机与接口 |