- 游戏开发
- 插件系统
【免费下载链接】REPENTOGON
Script extender for The Binding of Isaac: Repentance
NightmareScene是 REPENTOGON 为《以撒的结合:忏悔》(The Binding of Isaac: Repentance)注入的 Lua 全局类,用于访问游戏在**关卡过渡场景(stage transition / nightmare scene)**中使用的全部精灵(Sprite)与状态数据。本文以官方文档 docs/docs/NightmareScene.md 为核心骨架,结合仓库源码逐函数讲解 API 签名、返回值与底层实现,并演示如何配合MC_POST_NIGHTMARE_SCENE_RENDER、MC_POST_NIGHTMARE_SCENE_SHOW回调在过渡动画中注入自定义视觉内容。读完本文,你将能在 MOD 中自由替换过渡场景的背景、进度条、玩家肖像,读取进度条映射表,并判断当前是否处于 Dogma 噩梦场景。
NightmareScene 是什么:过渡场景的"画布"访问入口
在《以撒》中,每次进入新楼层时屏幕上会播放一段短暂的过渡动画:背景是楼层剪影,中间是带有楼层进度的进度条,玩家的头像(肖像)从上一个楼层"走"到下一个楼层。这段动画在游戏内部由一个名为NightmareScene的类负责渲染。
REPENTOGON 将其以全局表(global table)的形式暴露给 Lua 脚本。与 StageTransition 类形成明确分工——后者只负责"配置过渡如何播放"(如GetSameStage/SetSameStage控制 Isaac 的头像是否移动),而如果你想直接操控过渡画面里的内容(背景、气泡、进度条、肖像),就必须使用NightmareScene类。这两者一个管"行为参数",一个管"视觉元素",是设计上的一对互补接口。
获取方式与调用约定
依据文档 docs/docs/NightmareScene.md:
- 通过全局表
NightmareScene直接访问,无需实例化,即"可以用NightmareScene全局表获得这个类"。 - 调用这些函数必须使用
.(句点)而不是:(冒号),因为它们是静态全局函数,不接收 self 参数:
local sprite = NightmareScene.GetBubbleSprite()- 警告:在游戏完全初始化之前不要调用本类的函数!请确保只在回调(callback)上下文中使用。这是因为该类的全部函数最终都通过
g_Manager->GetNightmareScene()获取单例指针(见 repentogon/LuaInterfaces/LuaNightmareScene.cpp),在 Manager 尚未完成初始化时该指针不可用。
从源码看,这个全局表的注册发生在LuaEngine::RegisterClasses钩子中(LuaNightmareScene.cpp),共有 6 个函数被挂载到NightmareScene全局表:
| Lua 函数 | 返回类型 | 说明 |
|---|---|---|
GetBackgroundSprite() | Sprite | 过渡场景背景精灵 |
GetBubbleSprite() | Sprite | 过渡场景气泡精灵 |
GetPlayerExtraPortraitSprite() | Sprite | 玩家的额外肖像精灵 |
GetProgressBarSprite() | Sprite | 进度条精灵 |
GetProgressBarMap() | int[] | 进度条楼层映射表(14 个整数) |
IsDogmaNightmare() | boolean | 当前是否为 Dogma 噩梦场景 |
函数详解
1.GetBackgroundSprite()→ Sprite
local sprite = NightmareScene.GetBackgroundSprite()返回过渡场景的背景精灵。底层实现直接返回NightmareScene结构体内偏移0x20处的_backgroundANM2成员(见 libzhl/functions/NightmareScene.zhl),并以 Sprite 元表压入 Lua 栈(LuaNightmareScene.cpp)。
2.GetBubbleSprite()→ Sprite
local bubble = NightmareScene.GetBubbleSprite()返回过渡场景的气泡精灵,即包裹玩家肖像的泡泡/气罩元素。对应结构体偏移0x134处的_bubbleANM2(NightmareScene.zhl)。
3.GetPlayerExtraPortraitSprite()→ Sprite
local portrait = NightmareScene.GetPlayerExtraPortraitSprite()返回玩家的额外肖像精灵。注意它与背景、气泡、进度条不同,该函数直接访问结构体偏移0x4d4处的_playerExtraPortraitANM2成员(LuaNightmareScene.cpp)。
这个精灵与 REPENTOGON 的extrastageportrait玩家自定义标签直接相关:当某位玩家的 XML 配置了extrastageportrait属性时,过渡场景会额外加载一张肖像动画,加载后正是存放在_playerExtraPortraitANM2中(详见下文"延伸:REPENTOGON 对 NightmareScene 的扩展")。
4.GetProgressBarSprite()→ Sprite
local bar = NightmareScene.GetProgressBarSprite()返回过渡场景中进度条精灵,对应结构体偏移0x248处的_ProgressBarANM2。通过 Sprite 接口你可以控制其位置、颜色、透明度、播放动画帧等属性。
5.GetProgressBarMap()→int[]
local map = NightmareScene.GetProgressBarMap() for i = 1, #map do print("floor " .. i .. " -> " .. map[i]) end返回进度条楼层映射表:一张包含 14 个整数的 Lua 数组,描述了进度条上每个刻度对应的楼层编号。底层实现值得注意——它并非直接返回内部指针,而是在 LuaNightmareScene.cpp 中逐个复制结构体偏移0x36c处的int*数据:
int* content = nc->GetProgressBarMap(); for (int i = 0; i < 14; ++i) { lua_pushinteger(L, i + 1); lua_pushinteger(L, *content++); lua_rawset(L, -3); }也就是说,Lua 侧拿到的是副本,对其修改不会写回游戏内存;若要改变进度条显示内容,应通过精灵接口(如切换ProgressBarANM2的动画帧)来实现。
6.IsDogmaNightmare()→boolean
local isDogma = NightmareScene.IsDogmaNightmare()返回当前过渡场景是否为Dogma 噩梦(Dogma Nightmare):即进入《忏悔》DLC 中 Dogma/Boss 战前那段特殊的黑白噩梦式过渡。底层读取结构体偏移0x5e8处的布尔标志(NightmareScene.zhl),并通过lua_pushboolean返回(LuaNightmareScene.cpp)。利用此标志可以在回调中区分普通楼层过渡与 Dogma 特殊场景,从而决定是否叠加自定义特效。
底层结构:从源码看 NightmareScene 的内存布局
ZHL 定义文件 libzhl/functions/NightmareScene.zhl 完整描述了该类的内部布局,为上述函数提供了"硬核"佐证:
struct NightmareScene depends (ANM2) { inline ANM2* GetBackgroundSprite() { return &this->_backgroundANM2; } inline ANM2* GetBubbleSprite() { return &this->_bubbleANM2; } inline ANM2* GetProgressBarSprite() { return &this->_ProgressBarANM2; } inline int* GetProgressBarMap() { return (int*)((char*)this + 0x36c); } inline bool* IsDogmaNightmare() { return (bool*)((char*)this + 0x5e8); } }; ANM2 _backgroundANM2 : 0x20; ANM2 _bubbleANM2 : 0x134; ANM2 _ProgressBarANM2 : 0x248; ANM2 _playerExtraPortraitANM2 : 0x4d4; // 结构体总大小 0x5ec从中可以归纳出几个实现事实:
- 前三个"精灵"函数(背景、气泡、进度条)返回的都是
ANM2动画对象,是NightmareScene的内嵌成员而非指针,因此 Lua 绑定通过UserdataPtr::push包装后返回 Sprite 对象; GetPlayerExtraPortraitSprite在 ZHL 中未声明为 inline 方法,但 LuaNightmareScene.cpp 直接对_playerExtraPortraitANM2成员取址,佐证了该成员在结构体中的存在;GetProgressBarMap与IsDogmaNightmare返回的是裸指针/裸标志,Lua 层做了"按值复制"的转换;- 该类同时声明了三个原生方法
LoadConfig(char* xmlpath)、Render()、Show(bool unk)(NightmareScene.zhl),它们虽未直接暴露给 Lua,但被 REPENTOGON 用于挂钩回调(见下节)。
配套回调:MC_POST_NIGHTMARE_SCENE_RENDER 与 MC_POST_NIGHTMARE_SCENE_SHOW
NightmareScene类与两个 REPENTOGON 自定义回调紧密配套(见 docs/docs/enums/ModCallbacks.md):
| ID | 回调名 | 参数 | 返回 |
|---|---|---|---|
| 1102 | MC_POST_NIGHTMARE_SCENE_RENDER | void | void |
| 1103 | MC_POST_NIGHTMARE_SCENE_SHOW | (boolean IsDogmaNightmare) | void |
- 1102
MC_POST_NIGHTMARE_SCENE_RENDER:在NightmareScene::Render渲染之后触发,是向过渡画面叠加自定义绘制/修改精灵属性的最佳时机。它的实现采用的是 ASM 补丁:在 repentogon/Patches/ASMPatches/ASMCallbacks.cpp 中,通过签名扫描定位NightmareScene::Render,在函数入口插入调用RunPostNightmareSceneRender,并把EDI寄存器中的NightmareScene*作为参数传入后触发回调 1102。 - 1103
MC_POST_NIGHTMARE_SCENE_SHOW:在NightmareScene::Show执行之后触发,并把Show的bool unk参数作为IsDogmaNightmare布尔值传给 Lua(CustomCallbacks.cpp)。由于Show负责在播放过渡前组装场景内容,在此回调中修改精灵可以确保改动在动画开始前生效。
实战示例:在过渡场景中叠加自定义效果
综合上述 API 与回调,一个典型的 MOD 脚本可以这样使用NightmareScene:
-- 1) 在 Show 之后检查场景类型并记录状态 local nightmareMod = { isDogma = false, } function nightmareMod:onShow(isDogma) nightmareMod.isDogma = isDogma -- 普通过渡:把背景调成半透明 local bg = NightmareScene.GetBackgroundSprite() bg.Color = Color(1, 1, 1, 0.5) end nightmareMod:AddCallback(ModCallbacks.MC_POST_NIGHTMARE_SCENE_SHOW, nightmareMod.onShow) -- 2) 在 Render 之后逐帧驱动动画 function nightmareMod:onRender() local bar = NightmareScene.GetProgressBarSprite() bar:Play("BarAnimation", true) -- 播放/循环指定动画 local bubble = NightmareScene.GetBubbleSprite() bubble.Scale = bubble.Scale * 1.01 -- 让气泡缓慢膨胀 if nightmareMod.isDogma then -- Dogma 噩梦场景:给背景加一层红晕 NightmareScene.GetBackgroundSprite().Color = Color(1, 0.3, 0.3, 1) end end nightmareMod:AddCallback(ModCallbacks.MC_POST_NIGHTMARE_SCENE_RENDER, nightmareMod.onRender)实战要点:
- 精灵操作全部走 Sprite 接口(
Color、Scale、Rotation、Play、Load等),文档中所有返回值均为 Sprite 对象,Sprite 类文档提供了完整的可用方法清单; GetProgressBarMap()返回的是 14 元素副本,若想自定义进度条显示,应结合GetProgressBarSprite()的动画帧/精灵贴图来实现,而不是修改返回值;- 时机把控:
MC_POST_NIGHTMARE_SCENE_RENDER(1102)每帧渲染后触发,适合逐帧动画;MC_POST_NIGHTMARE_SCENE_SHOW(1103)只触发一次,适合一次性初始化。
延伸:REPENTOGON 对 NightmareScene 的原生扩展
除了 Lua API,REPENTOGON 内部还围绕NightmareScene做了三处值得了解的改动,均可在源码中验证:
1.stageportrait/extrastageportrait玩家自定义标签(PlayerTags.cpp)
通过 ASM 补丁挂钩NightmareScene::Show,支持玩家 XML 配置两个新属性:
stageportrait:指定过渡场景中玩家主肖像的文件名(基于bigportraitroot拼接路径),存在时替换默认肖像;extrastageportrait:额外加载一张肖像动画,加载后存放在_playerExtraPortraitANM2中——这正是 Lua 侧GetPlayerExtraPortraitSprite()能读到它的原因。加载逻辑为_playerExtraPortraitANM2.Load(result, true)并调用Play播放默认动画(PlayerTags.cpp)。
2.NoShake玩家标签(PlayerTags.cpp)
同样通过 ASM 补丁挂钩NightmareScene::Show,当玩家配置了noshake标签时,过渡场景(以及 Boss 入场动画)将跳过屏幕震动效果。
3.NightmareScene::LoadConfig的 MOD 路径支持(XMLData.cpp)
REPENTOGON 钩住了NightmareScene::LoadConfig(char* xmlpath),在加载过渡场景配置时同步处理 MOD 内容路径(ProcessModEntry),使得 MOD 能够为过渡场景注入/覆盖资源,同时保证xmlsloaded标志下的重复加载不被重复处理。
注意事项与最佳实践
- 必须等待游戏初始化完成:文档明确警告"本类的函数在游戏完全初始化之前不应被调用",所有函数内部都会访问
g_Manager->GetNightmareScene(),脱离回调调用会存在空指针风险。 - 用
.而非::NightmareScene是全局函数表而非实例元表,误用:会因多传 self 导致参数错位或报错。 - 区分两个类:配置过渡行为用 StageTransition(如
SetSameStage控制头像是否移动),操控过渡画面内容用NightmareScene,二者互补而非替代。 - 副本数据勿回写:
GetProgressBarMap()返回的是 Lua 侧新建的表,修改它不会影响游戏内部状态。 - 优先在回调内读取:由于过渡场景生命周期短暂,建议在 1102/1103 回调中获取并缓存所需精灵引用,避免跨场景持有过期对象。
- 精灵为共享引用:返回的 Sprite 指向游戏内部
ANM2对象,修改会直接影响游戏渲染,注意在过渡结束后恢复默认状态(如颜色、透明度),以免影响后续场景。
总结
NightmareScene全局类是 REPENTOGON 中操控关卡过渡场景视觉内容的唯一官方入口:GetBackgroundSprite、GetBubbleSprite、GetProgressBarSprite、GetPlayerExtraPortraitSprite四个函数分别暴露背景、气泡、进度条与额外肖像四个ANM2精灵;GetProgressBarMap提供 14 元素楼层映射表(Lua 侧副本);IsDogmaNightmare用于识别 Dogma 噩梦场景。配合MC_POST_NIGHTMARE_SCENE_RENDER(1102)与MC_POST_NIGHTMARE_SCENE_SHOW(1103)两个回调,MOD 开发者可以在过渡动画的组装阶段与每帧渲染阶段自由叠加自定义效果,实现完全个性化的楼层过渡体验。需要进一步查阅精灵可用方法时,请参考 Sprite 类文档。
- 游戏开发
- 插件系统
【免费下载链接】REPENTOGON
Script extender for The Binding of Isaac: Repentance
相关推荐
电脑怎么用键鼠玩手游:QtScrcpy 投屏与键鼠映射实践指南
电脑怎么用键鼠玩手游:QtScrcpy 投屏与键鼠映射实践指南 玩手游瞄准时,手指常常遮住屏幕大半,切换技能还要腾出拇指;手上同时管几台手机时,来回切窗口核对状
游戏开发插件系统REPENTOGON BossPoolManager 全局类完全指南:读取与操控 Boss 池
REPENTOGON BossPoolManager 全局类完全指南:读取与操控 Boss 池 BossPoolManager 是 REPENTOGON(《以撒
游戏开发插件系统Buzz 离线转录工具:基于 Whisper 的免费本地语音转文字,3 步搞定会议纪要与字幕
Buzz 离线转录工具:基于 Whisper 的免费本地语音转文字,3 步搞定会议纪要与字幕 还在把 3 小时的会议录音上传到云端,然后等 40 分钟出字幕?更
游戏开发插件系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考