☰
REPENTOGON NightmareScene 全局类完全指南:操控过渡场景精灵与回调
2026/10/16 2:56:44 网站建设 项目流程
  • 游戏开发
  • 插件系统

【免费下载链接】REPENTOGON

Script extender for The Binding of Isaac: Repentance

项目地址:https://gitcode.com/gh_mirrors/re/REPENTOGON
点击查看免费下载

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回调名参数返回
1102MC_POST_NIGHTMARE_SCENE_RENDERvoidvoid
1103MC_POST_NIGHTMARE_SCENE_SHOW(boolean IsDogmaNightmare)void
  • 1102MC_POST_NIGHTMARE_SCENE_RENDER:在NightmareScene::Render渲染之后触发,是向过渡画面叠加自定义绘制/修改精灵属性的最佳时机。它的实现采用的是 ASM 补丁:在 repentogon/Patches/ASMPatches/ASMCallbacks.cpp 中,通过签名扫描定位NightmareScene::Render,在函数入口插入调用RunPostNightmareSceneRender,并把EDI寄存器中的NightmareScene*作为参数传入后触发回调 1102。
  • 1103MC_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标志下的重复加载不被重复处理。

注意事项与最佳实践

  1. 必须等待游戏初始化完成:文档明确警告"本类的函数在游戏完全初始化之前不应被调用",所有函数内部都会访问g_Manager->GetNightmareScene(),脱离回调调用会存在空指针风险。
  2. 用.而非::NightmareScene是全局函数表而非实例元表,误用:会因多传 self 导致参数错位或报错。
  3. 区分两个类:配置过渡行为用 StageTransition(如SetSameStage控制头像是否移动),操控过渡画面内容用NightmareScene,二者互补而非替代。
  4. 副本数据勿回写:GetProgressBarMap()返回的是 Lua 侧新建的表,修改它不会影响游戏内部状态。
  5. 优先在回调内读取:由于过渡场景生命周期短暂,建议在 1102/1103 回调中获取并缓存所需精灵引用,避免跨场景持有过期对象。
  6. 精灵为共享引用:返回的 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

项目地址:https://gitcode.com/gh_mirrors/re/REPENTOGON
点击查看免费下载

相关推荐

上一篇:RIOT 上的 NASA bplib DTN 实战:基于 UDP Convergence Layer 的 BPv7 示例深入解析
下一篇:Fail2Ban 异常体系解析:fail2ban.exceptions 模块与 Jail 异常处理机制

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询