演出、镜头与音频
返回总览。本篇分析剧情过场、游戏内同步场景、镜头和音频如何被玩法编排。它们复用原生资源系统,但具有不同的占用与退出协议;以下均为静态源码结论,未播放或验证实际素材。
1. 演出是玩法状态与表现资源的交接#
| 机制 | 脚本控制内容 | 原生系统承担内容 |
|---|---|---|
| Cutscene | 请求、开始条件、角色绑定、跳过与完成后的玩法状态 | 过场资源、场景角色和播放生命周期 |
| 同步场景 | 场景原点、参与实体、动画、阶段及退出条件 | 多实体动画相位与运动协调 |
| 脚本镜头 | 创建、激活、插值、动画、回交游戏镜头 | 相机实体、ScriptDirector、插值及最终镜头选择 |
| 对话与环境语音 | 说话人、会话内容、触发条件、优先级和网络选项 | 语音资源、音频实体及播放 |
| Audio scene / 音乐事件 | 启停情境、修改场景变量、发送事件 | 混音场景和音乐系统的具体响应 |
任务脚本决定“现在应当演什么”,而不是在脚本中逐骨骼求解动画或混音。演出结束还必须恢复玩家控制、HUD、实体行为和后续任务条件;只处理播放完成,会留下比视觉错误更难追踪的玩法占用。
2. 非任务过场有独立预加载仲裁#
cutscene_control.sch 明确处理多个脚本同时请求预加载 off-mission cutscene 的情况,一次只许可一个请求。[S1] 请求 ID、请求类型、allowed ID 和 active ID 分开维护:取得加载许可与实际活跃是两件事。
UPDATE_CUTSCENE_CONTROL 首先检查当前玩法类型是否允许相关内容,或是否正在加载 Flow 的 intro;只在请求列表变化时扫描。优先级来自请求类型的枚举数值。代码找到一个比当前许可更高的候选就停止本轮搜索,不能将其描述为每帧对全部请求完成排序、选出绝对最高项。列表为空时同时清空 allowed 和 active。
这是对共享表现资源的调度,不是简单给每个任务加一个独立 REQUEST_CUTSCENE。常驻世界内容越多,这种集中仲裁越有价值。
3. 角色绑定包含资源、时序和所有权语义#
REGISTER_ENTITY_FOR_CUTSCENE 最终进入 CommandRegisterEnityWithCutScene。[S3,L634] 对已有实体,它检查模型、drawable 和 skeleton;随后验证模型 hash、scene handle,以及与场景开始相关的同帧约束。脚本若在角色登记和开始之间任意 WAIT,需要先核对接口约定,不能把它们当成完全无关的异步步骤。
登记 flags 还决定实体生命周期:已有脚本实体交给过场动画、交给过场动画并删除、由过场创建脚本实体、或不参与动画。因而“过场结束后任务角色消失”应同时检查登记选项和任务清理,不能只查动画资源。珠宝店抢劫的角色登记是具体内容侧入口。[S7]
cutscene_private.sch 则处理横跨脚本的边界:[S2]
- 标为 cutscene-safe 期间,每帧隐藏 feed,避免常驻通知侵入演出。
- 加载失败保护激活时,通过淡出降低世界呈现压力、协助 streaming。
- 过场开始播放或被放弃后,都恢复淡入并清除 failsafe,防止取消路径留下黑屏。
这体现了“成功、跳过、取消、未加载完成”必须共同设计。场景脚本不能只依赖动画自然走到末尾。
4. 镜头是受脚本资源管理的原生对象#
CREATE_CAM 构造 CScriptResource_Camera,登记到当前脚本的 handler。[S4,L122] 源码特意指出相机引用使用真实 pool index,与其他资源的引用约定并不完全一致;不能假定所有 Native handle 都有相同内部表示。
RENDER_SCRIPT_CAMS 经 camInterface::GetScriptDirector() 启停脚本镜头。停止可作用于本线程,也有跨所有线程的强制路径;原生注释明确指出后者可能与并发脚本冲突。[S4,L72] 对商店、手机、任务和切人而言,局部结束自身镜头与全局抢夺镜头应严格区分。
SET_CAM_ACTIVE_WITH_INTERP 不仅调用插值,还检查源、目标相机及脚本控制资格,并处理“源镜头正从目标镜头插值过来”的循环引用情况。[S4,L1724] PLAY_CAM_ANIM 查询已加载的动画 clip,将场景位置与欧拉角转换成原点矩阵,再交给 ScriptDirector。动画名存在于脚本并不表示对应动画字典已经可用。
游戏内同步场景与完整 cutscene 不必使用同一控制器。player_timetable_scene.sc 中同步镜头的插回、同步 task 的混合退出,展示了“接回日常玩法”的具体处理。[S8] 内容作者既要设计入场动作,也要设计在场景中途恢复常态时的角色姿态和镜头过渡。
5. 音频把内容标识与播放机制分开#
script_audio.sch 将会话 root、可选行号和后缀组合后交给 GET_FILENAME_FOR_AUDIO_CONVERSATION,让脚本围绕对话标识组织内容。[S5] 两个 ambient speech wrapper 显式暴露 bSyncOverNetwork,而且默认值不同:普通指定 voice 的包装默认 FALSE,speech-and-clone 包装默认 TRUE。不能只看函数名相近就忽略网络行为。
长对话由 dialogue_handler 协调面对面、电话、multipart 会话和邀请,详见手机通讯与社交。会话能否启动、播放是否中断、任务等待什么结果,属于玩法协议;音频存在与否只是其中一个条件。
START_AUDIO_SCENE 经名称 hash 调用 g_ScriptAudioEntity.StartScene,原生断言还检查相应 MixerScene 是否存在;另有停止和设置变量接口。[S6,L3137] 这允许任务以“进入某种声音情境”组织表现,而把底层混音配置留给音频资源。音乐事件也是触发接口,不能据脚本中的事件名推导完整音乐编排内容。
6. 内容扩展与故障定位#
新增一段演出,可按以下顺序检查:准入与占用、资源请求、角色绑定、演出开始、正常结束与跳过、异常撤销、任务状态回交。镜头/音频的资源释放与玩法状态恢复应逐项对应。
加载卡住优先查许可和 streaming;角色错位查 scene handle、原点和骨架;演后无法操作查控制权、camera director 和全局占用;多人重复语音查同步选项、host/owner 和本地触发次数。后几项是由上述机制推导的排查方法,不代表已在本版本复现缺陷。
源码证据#
| 编号 | 阅读位置 |
|---|---|
| [S1] | cutscene_control.sch L7 设计说明;L17 清理;L70 请求仲裁 |
| [S2] | cutscene_private.sch L19 cutscene-safe;L28 加载 failsafe |
| [S3] | commands_cutscene.cpp L634 角色登记;L1229/1276 Native 注册 |
| [S4] | commands_camera.cpp L72 渲染控制;L122 资源登记;L1724 插值;L2046 动画 |
| [S5] | script_audio.sch L5 对话资源名;L26/30 网络语音包装 |
| [S6] | commands_audio.cpp L3137 Audio scene;L4243 音乐事件注册 |
| [S7] | jewelry_heist.sc L828 起过场角色登记 |
| [S8] | player_timetable_scene.sc L7925 同步镜头;L8048 同步 task 混合退出 |
[S1]: <../../../GTAV Source/script/dev_ng/singleplayer/include/private/cutscene_control.sch> [S2]: <../../../GTAV Source/script/dev_ng/singleplayer/include/private/cutscene_private.sch> [S3]: <../../../GTAV Source/src/dev_ng/game/script/commands_cutscene.cpp> [S4]: <../../../GTAV Source/src/dev_ng/game/script/commands_camera.cpp> [S5]: <../../../GTAV Source/script/dev_ng/shared/include/public/script_audio.sch> [S6]: <../../../GTAV Source/src/dev_ng/game/script/commands_audio.cpp> [S7]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/heists/heistJewelry/jewelry_heist.sc> [S8]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/playercontrol/player_timetable_scene.sc>