【免费下载链接】universal-modder
Point Claude at any game. Skills, tools and the fal MCP that let Claude Code mod almost any PC game you own: recon, reverse engineering, fal-generated art/3D/audio, in-game testing, showcase videos.
本篇以 universal-modder 仓库中的 examples/terraria-tmodloader/README.md 为主线,完整拆解一个名为 Fal Arsenal 的 tModLoader 模组:5 件武器、3 只敌人、1 个 Boss 的玩法机制与源码实现,fal(FLUX dev)生图配合um sprite的像素图制作管线,以及在 Windows / Linux / WSL 下的构建部署流程。读完你可以掌握从"AI 生成 1024px 概念图"到"符合 Terraria 帧布局的模组贴图"的完整链路,并能复刻 README 中沉淀的一批 tModLoader 踩坑经验。
一、Fal Arsenal 的内容总览:这个模组做了什么
该示例位于 examples/terraria-tmodloader/,是一个可以直接编译进 tModLoader 的完整模组(FalArsenal/目录),外加一个资产生成脚本和一个参考代码目录。README 说明它移植自 Terraria 展示视频背后的那个模组(单人、tModLoader 2026.07 配合 Terraria 1.4.4),脚本化的场景、RL 智能体和游戏内录制器被剥离,仅保留在reference/中供学习。
五件武器
| 武器 | 机制 | 源码位置 |
|---|---|---|
| Homing Missile Launcher | 导弹转向最近敌人,命中后范围溅伤并挖出小弹坑 | Weapons.cs |
| Tactical Nuke | 弹头落在玩家前方 60 格处,半径约 50 格内单位(含 Boss)全灭,地面汽化成弹坑并留下灰烬边缘;像素火球与蘑菇云升起,屏幕闪白、镜头平移过去再回来;物品不消耗 | Weapons.cs |
| Tesla Rifle | 即时命中链式闪电:命中瞄准方向 30 度锥内第一个敌人,最多再跳 4 个目标,每跳伤害衰减 15% | EnergyWeapons.cs |
| Singularity Launcher | 射出一颗能量球,2.5 秒后展开成黑洞:吸入敌人与散落战利品(Boss 仅轻微受力),磨碎近处目标,最后内爆造成 3 倍伤害 | EnergyWeapons.cs |
| Orbital Strike | 在准星附近锁定目标(优先 Boss),随后一道天光横扫到目标并持续 2 秒,烧毁其下区域、在地面留下焦痕;被光束罩住的 Drone Mothership 无法移动或冲刺 | EnergyWeapons.cs |
从源码可以核对这些参数的具体落点:Tesla Rifle 在Shoot中用Vector2.Dot(...) > 0.86f实现 30 度锥筛选,链式循环dmg *= 0.85f即"每次跳 15% 衰减"(EnergyWeapons.cs);SingularityOrb 用const int Travel = 36, Hole = 150(合计 2.5 秒 @60fps)划分飞行与黑洞阶段,内爆时对 200px 内敌人打出Projectile.damage * 3;OrbitalBeam 用const int Lock = 45, Fire = 120, Fade = 20三阶段状态机驱动锁敌、扫射与消散。
三只敌人与一个 Boss
- Scrap Drone:在玩家头顶盘旋、左右平移并射击激光(原版
PinkLaser弹射物)。SetDefaults中noGravity = true,SpawnChance为SpawnCondition.Overworld.Chance * 0.04f,即白天地表低概率刷新(Mobs.cs)。 - Neon Slime:发光史莱姆,直接复用原版史莱姆 AI(
NPC.aiStyle = NPCAIStyleID.Slime; AIType = NPCID.BlueSlime),刷新条件为OverworldDaySlime.Chance * 0.1f,白天生成。 - Mech Walker:步行追击、跳跃翻越台阶与缺口。它使用自写 AI(
aiStyle = -1),原因在于原版战斗者 AI(aiStyle 3)在白天地表会放弃追击、游走并消失——README"Lessons"一节专门解释了这个坑,也解释了为什么这只怪必须自定义。
- Boss:Drone Mothership(5200 生命)。第一阶段在高处环绕,放出 Scrap Drone 并发射扇形激光;血量低于一半后转红(
GetAlpha混色)、开始冲刺并射出制导火箭。它接入了原版 Boss 全套管线:血条(NPC.boss = true)、音乐(Music = MusicID.Boss2)、"has awoken! / has been defeated!" 消息与掉落——源码 Boss.cs 中ModifyNPCLoot给出金币、灵魂之飞翔(Souls of Flight)、神圣锭(Hallowed Bars)三档ItemDropRule.Common,BossLoot返回治疗药水作为 Boss 战利品。多玩家客户端通过NPCID.Sets.MPAllowedEnemies[Type] = true允许向服务器请求召唤。
README 明确提示:爆炸会挖毁世界,请在备用世界尝试;多人模式未测试,网络代码只是按原版模式编写(方块编辑像原版炸药一样逐格发送,Boss 由服务器侧生成)。
二、安装与构建:tModLoader 官方流程 + 命令行四种方式
前置条件
- 从 Steam 安装 tModLoader(免费,app 1281930)。它必须出现在你的 Steam 库里,否则游戏根本启动不了——README 强调"把它加进库,别试图绕过这个检查"。
- 把
FalArsenal/拷进你的 ModSources 目录。tModLoader 在你打开Workshop > Develop Mods时会创建该目录,并写入FalArsenal.csproj所导入的tModLoader.targets:- Windows:
Documents\My Games\Terraria\tModLoader\ModSources\ - Linux:
~/.local/share/Terraria/tModLoader/ModSources/ - macOS:
~/Library/Application Support/Terraria/tModLoader/ModSources/
- Windows:
- 游戏内进入Workshop > Develop Mods > FalArsenal > Build + Reload,完成编译、启用与重载。
命令行构建
README 给出四类等价方式,全部把.tmod输出到<存档目录>/Mods并启用它:
# 在 tModLoader 安装目录下,用它首次启动时装的 .NET 运行时(无需 SDK) .\dotnet\dotnet.exe tModLoader.dll -build "<ModSources>\FalArsenal" # Windows (cmd 或 PowerShell) dotnet tModLoader.dll -build "<ModSources>/FalArsenal" # 任意 .NET 8 (Linux、WSL) # 在 FalArsenal/ 目录内,用 .NET 8 SDK(IDE 也是这么构建的) dotnet build # 在 ModSources/ 内部 dotnet build -p:TmlInstallDir="<tModLoader 安装目录>" # 在其他任意位置-build命令可加-tmlsavedirectory <dir>(或给dotnet build传-p:ExtraBuildModFlags="-tmlsavedirectory <dir>")把产物写到非默认存档目录。
examples/terraria-tmodloader/FalArsenal/FalArsenal.csproj 是理解这些命令的关键,全文只有两条 Import:
<Import Project="..\tModLoader.targets" Condition="'$(TmlInstallDir)' == ''" /> <Import Project="$(TmlInstallDir)/tMLMod.targets" Condition="'$(TmlInstallDir)' != ''" />第一条解释"在 ModSources 里直接dotnet build"为何可行;第二条解释TmlInstallDir属性如何支持"在任意位置构建"。csproj 注释还留了两条来自实战的经验:tModLoader 会主动提示"升级"缺少<Import Project="..\tModLoader.targets" />或缺少Properties/launchSettings.json的模组(仓库中 launchSettings.json 已按模板提供 Terraria / TerrariaServer 两个启动档案);以及不要把自己的覆盖属性命名为TMLPath——tMLMod.targets已经在用tMLPath,而 MSBuild 属性名不区分大小写,重名会冲突。
description.txt 提供模组列表页的展示文案,Localization/en-US_Mods.FalArsenal.hjson 则集中了全部显示名与 Tooltip——hjson 文件头部注释指出,构建后新条目由 tModLoader 自行追加到这里。
三、游戏内获取内容:命令与配方
两种途径(README"Getting the items"一节):
- 聊天输入
/arsenal各得一件武器,/mothership在玩家附近召唤 Boss。 - 或在铁砧/铅砧(Iron 或 Lead Anvil)处合成。配方刻意做得便宜,方便无命令也能试玩:
| 物品 | 材料 |
|---|---|
| Homing Missile Launcher | 8 铁/铅锭 + 10 史莱姆凝胶 |
| Tactical Nuke | 12 铁/铅锭 + 5 坠落之星 |
| Tesla Rifle | 8 铁/铅锭 + 3 坠落之星 |
| Singularity Launcher | 8 铁/铅锭 + 2 透镜 + 3 坠落之星 |
| Orbital Strike | 6 铁/铅锭 + 1 透镜 + 5 坠落之星 |
命令实现见 Commands.cs:ArsenalCommand遍历Mod.GetContent<ModItem>(),用QuickSpawnItem在玩家脚边投放(多玩家下会同步);MothershipCommand走原版 Boss 召唤路径——单人/主机直接NPC.SpawnOnPlayer,多玩家客户端则发送MessageID.SpawnBossUseLicenseStartEvent请服务器生成,这与MPAllowedEnemies的配合保证了合法性。配方代码与表格一一对应,如 Tesla Rifle 的CreateRecipe().AddRecipeGroup(RecipeGroupID.IronBar, 8).AddIngredient(ItemID.FallenStar, 3).AddTile(TileID.Anvils),AddTile(TileID.Anvils)即"铁砧或铅砧"。
四、资产管线:fal FLUX dev → um sprite → 模组贴图
这是本示例最能体现 universal-modder 工作流的部分。examples/terraria-tmodloader/assets/make_art.sh 一条命令即可重建FalArsenal/Assets/下全部 PNG 与icon.png,支持GEN(原始图目录)、OUT、ICON重定向。
4.1 第一步:fal 生图(白底 1024×1024)
每个物件都由 fal 的 FLUX dev 画在纯白底上,共用一条风格后缀:
16-bit pixel art game sprite in the style of Terraria, crisp dark outline, limited palette, centered, plain flat white background, no shadow, no text
调用示例(make_art.sh 中实际内容):
um fal run fal-ai/flux/dev "prompt=a single small guided missile seen from the side pointing right, red nose cone, white and grey body, small tail fins, flame at the back. <style>" \ image_size=square_hd num_images:=1 num_inference_steps:=40 output_format=png --out gen --name missile两个值得注意的工程细节:
- 脚本用
um fal run而不用um fal image——因为image配方会为其默认模型附加本例不需要的输入字段;run只发送这里列出的这几个输入。 - 仓库
assets/gen/已内置十张成稿(.jpg,flux/dev 默认输出格式),脚本只对"被删除的文件"才真正调用 fal(需FAL_KEY环境变量)。由于没有固定种子,重跑会画出新图,而不是逐像素复刻——这是生成式资产管线的固有属性,README 如实说明。
4.2 第二步:um sprite 处理成引擎帧
um sprite(实现见 um/sprite.py,子命令一览即该文件头部 docstring)把每张原图加工成模组使用的帧。README 给出的完整链路:
um sprite cutout gen/missile.jpg t/missile.png --bg ffffff --tol 0 --grey 232 # 从边界泛洪去底:三通道均 >= 232 的像素清空 um sprite rotate t/missile.png t/missile_level.png -45 # flux 画成了斜的,先转正 um sprite fit t/missile_level.png t/missile_small.png --size 14x5 # 最近邻,只缩放一次 um sprite fit t/missile_small.png Assets/HomingMissile.png --size 38x16 --no-upscale um sprite flip t/scrap_drone.png t/drone_left.png # NPC 贴图默认朝左 um sprite fit t/drone_left.png t/drone.png --size 46x30 um sprite frames t/drone.png t/drone --n 2 --kind bob # 两帧悬浮动画 um sprite sheet Assets/ScrapDrone.png t/drone/drone_0.png t/drone/drone_1.png --verticalmake_art.sh 对十张图各自微调了参数,这些差异本身就是"AI 出图不规整"的应对策略,值得逐条对照:
cut missile_launcher --holes:把手柄与枪管之间的封闭白区也清掉;cut nuke --grey 150:flux 在弹体周围加了一圈柔和灰影,阈值提到 150 才能清掉;cut orbital_remote --grey 225 --spread 31:这张图的"白底"偏灰,用"每通道 >= 225 且色差 < 31"判定;cut scrap_drone --keep-top 0.76:裁掉物体下方的地面阴影;cut neon_slime --grey 90 --spread 60:史莱姆的辉光渐隐进白底,需要连淡光环一起清;- missile / singularity_launcher 被 flux 画成倾斜:先
rotate -45/-16转正,fit到小尺寸,再--no-upscale居中放大到最终帧(missile 按 1.2 倍画出,因为发射器的持握偏移是按这个尺寸调过的)。
动画与帧布局全部由um sprite frames(--kind bob/squash)与三帧旋转(rotate -5/0/5制造摇摆步态)生成,sheet --vertical纵向堆叠——这正是 Terraria NPC 的帧布局要求。最终帧尺寸(README 列出,与SetDefaults中的NPC.width/height互相印证):launcher 64x26、missile 38x16、nuke 40x84、Tesla 62x30、Singularity 70x34、remote 24x38、drone 2×46x30、slime 2×38x34、walker 3×40x46、Mothership 2×240x150。Mothership 第二帧(红色核心增亮)与模组图标是脚本里仅有的两段 Pillow 代码:前者只把"饱和红"像素提亮,后者从 Boss 第 2 帧裁出船体、叠上夜空渐变与高斯模糊红晕,合成 80×80 图标。
关于可复现性,README 说得很诚实:仓库里的 PNG 来自首次构建(当时用的是 Pillow 脚本),重跑make_art.sh对同样的原图能复现它们,其中两张逐字节一致,其余因um sprite fit取整(原脚本是截断)差一两个像素,drone 的悬浮与 slime 的压扁也略更平缓——也就是说重跑会改写已提交 PNG 的少量像素。
4.3 引擎侧的帧约定(为什么这样处理)
这些"土办法"背后是 Terraria 的绘制约定,Mobs.cs 头部注释与 README 互相印证:
- NPC 帧纵向堆叠,游戏用
texture height / Main.npcFrameCount推帧高,所以任何一致尺寸的帧都能工作,不需要对齐原版数值(如Main.npcFrameCount[Type] = 3配合 3 帧图)。 - 物品贴图朝右;NPC 贴图朝左,
spriteDirection = 1时翻转为朝右。 - 弹射物贴图方向必须与绘制代码一致:本模组弹射物朝右,因为
PreDraw里用velocity.ToRotation()旋转;许多原版弹射物图集朝上,绘制时另加 90 度。
五、特效与网络:Fx.cs 里能学到什么
Fx.cs 是全部武器共用的ModSystem:像素火球、核爆弹坑与蘑菇云、屏幕闪光、镜头聚焦、绘制工具。其中两处实现细节直接对应 README 的"Lessons",也最能体现"按原版模式写多玩家"的思路:
- 弹坑的同步策略:
Crater只依赖世界坐标,注释写明"每台机器挖出同一个坑(无需方块同步)";而Blast每破坏一格都在非单机下补发MessageID.TileManipulation——"像原版炸药一样,逐格发送"。这就是 README 所说"tile edits sent like vanilla explosives"的源码出处。 - 镜头聚焦为什么用偏移量:
ModifyScreenPosition在屏幕抖动之后执行,直接改写Main.screenPosition会抵消抖动,所以核爆镜头是Main.screenPosition += camOffset叠加一个缓动偏移,而不是覆写。 - 闪光层要除以 UI 缩放:
PostDrawInterface在应用了 UI scale 的上下文里绘制,全屏白闪的矩形因此写成(int)(Main.screenWidth / Main.UIScale) + 1。 PostDrawTiles没有现成 SpriteBatch:代码里手动sb.Begin(...)/sb.End(),用SamplerState.PointClamp保持像素感。
多玩家数据同步的另一个例子是 Tesla 电弧:OnSpawn只在生成端执行、早于网络同步发出,其他玩家需要的数据必须放Projectile.ai[]——TeslaArc的注释直接写明 "The span rides in ai (not velocity) because ai is what gets synced to other players"(EnergyWeapons.cs),闪电两端点就存在ai[0]/ai[1]里,弧线纯程序化绘制,贴图只是占位。
六、README"Lessons"全清单:构建与录制这个模组的踩坑笔记
README 最后一节是这个示例的精华,逐条整理如下(每条都在FalArsenal/源码或脚本中有对应证据):
- 读原版代码要反编译:用 ilspycmd 把游戏反编译到仓库外的目录再 grep,例如
ilspycmd -p -o ~/tml-decomp "<tModLoader 安装目录>/tModLoader.dll"(原版则对 Terraria.exe)。反编译产物绝不提交——um publish check会标记反编译器输出。 - 录像:用 ffmpeg 的 gfxcapture(Windows.Graphics.Capture)按 HWND 抓游戏窗口(
um win record)——gdigrab 只能拿到黑帧;在游戏内用 FNA3D 的 D3D11ReadBackbuffer则会每次调用泄漏一整帧。单独的游戏音轨靠进程回环捕获(um/ps1/ProcLoopback.ps1)。ffmpeg 停止时切勿阻塞游戏主线程——gfxcapture 对冻结的窗口会卡住。按精确 PID 杀进程(um win kill <pid>),不要用pkill -f <pattern>:从 Agent 的 shell 里发这条命令时,模式会匹配到正在执行它的 shell 本身,把它一起杀掉。 - 防爆毁存档:保持一份完好世界副本,每次开拍前恢复:
um backup create "<存档目录>/tModLoader/Worlds" --name worlds,再um backup restore worlds(Windows 存档目录为Documents\My Games\Terraria)。 - Steam 检查:tModLoader 启动前强制要求免费 tModLoader 应用在 Steam 库中(GitHub 构建也一样)。命令行
-build在任何 Steam 代码运行前就退出,所以构建不需要它。 - WSL 限制:WSL 下对着 Windows 安装目录,
dotnet tModLoader.dll -build可用;dotnet build能编译,但其打包步骤(-server -build)因加载不到 FNA3D(PNG 转换需要)而失败——要么在 Windows 上打包,要么 WSL 下用-build。 - flux/dev 默认输出 JPEG:除非显式
output_format=png。首次构建把 JPEG 存成了.png文件名,Pillow 照样能打开;仓库现在按真实扩展名存放。
七、reference/ 目录:被剥离但值得读的两份参考实现
reference/不在模组构建内(刻意放在模组目录外,因为 tModLoader 会编译模组目录下所有.cs文件),它保留了"这个示例的来源"最有信息量的两块:
- reference/InModRecorder.cs:游戏内录制器。文件头注释是一段浓缩的录制经验:视频用 gfxcapture 按 HWND 抓(被遮挡也能拿到该窗口 GPU 帧);D3D11
ReadBackbuffer每调用泄漏一张整尺寸 staging 纹理(约 3.7 MB/帧,"一次拍摄把进程撑到 17 GB");音频用 WASAPI 进程回环只抓本进程;停止 ffmpeg 要发裸q并在主线程外等待(窗口冻结会让 gfxcapture 卡住,丢掉收尾画面);两路采集都打 QPC 时间戳,配合um video mux对齐(ffmpeg 从启动到出第一帧约 0.24–0.3 s);写.mkv并逐包 flush,被杀掉的 ffmpeg 也留下可播放文件;未聚焦窗口会被节流,拍摄时要设Main.instance.InactiveSleepTime = TimeSpan.Zero。 - reference/AgentBridge.cs:JSON 行协议的本地 socket 桥,让外部 Agent(带工具的 LLM 或训练出的策略)把屏幕读成文本、点菜单、玩游戏。协议包括
observe(菜单项 id+label 或世界状态)、click、type、controls(移动/跳跃/开火/瞄准角)、use_item、step(控制+观察一次往返,60 Hz 循环)。关键做法:所有请求挂Main.DoUpdate后在主线程处理,保证每条回复对应一致的一帧;菜单从 UI 树泛化读取(OnLeftClick与OnLeftMouseDown两种事件都要查);瞄准靠每帧写Main.mouseX/Y实现。
八、目录结构与继续深入
FalArsenal/ 模组本体(拷入 ModSources) Weapons.cs Homing Missile Launcher、Tactical Nuke EnergyWeapons.cs Tesla Rifle、Singularity Launcher、Orbital Strike Mobs.cs Scrap Drone、Neon Slime、Mech Walker Boss.cs Drone Mothership 与其火箭 Fx.cs 爆炸、核爆弹坑与蘑菇云、闪光、镜头聚焦、绘制工具 Commands.cs /arsenal、/mothership Localization/ 显示名与 Tooltip Assets/、icon.png 贴图(由 assets/make_art.sh 生成) assets/make_art.sh fal 提示词 → um sprite 管线 assets/gen/ 管线起点的 fal/flux 原始图 reference/ 不参与构建:InModRecorder.cs(窗口+游戏音频捕获)、 AgentBridge.cs(外部 Agent 读菜单/点击/游玩的 JSON 行 socket)入口类 FalArsenal.cs 是空的Mod子类——tModLoader 会自动发现程序集里所有ModItem/ModNPC/ModProjectile/ModSystem/ModCommand,注释中即给出上表的文件分工。想继续深挖时,建议的路径是:make_art.sh的提示词与um sprite参数对照 um/sprite.py 的 docstring 逐个理解;Fx.cs的弹坑/同步逻辑对照 Weapons.cs 的NukeWarhead看"地面零点如何从发射端算出并放进ai[0]随网络同步";reference/两份文件则覆盖了"录干净画面"与"让 Agent 操作游戏"这两个展示视频类工作流的通用问题。所有资产均生成自 fal(fal-ai/flux/dev)并经 universal-modder 的um sprite处理,复现入口始终是 examples/terraria-tmodloader/assets/make_art.sh。
【免费下载链接】universal-modder
Point Claude at any game. Skills, tools and the fal MCP that let Claude Code mod almost any PC game you own: recon, reverse engineering, fal-generated art/3D/audio, in-game testing, showcase videos.
相关推荐
universal-modder 的 AoE2 DE 完整文明模组示例:从 fal 生成美术到 .sld 精灵与 Genie 数据模组的完整管线
universal modder 的 AoE2 DE 完整文明模组示例:从 fal 生成美术到 .sld 精灵与 Genie 数据模组的完整管线 example
AoE2DE 模组演示视频制作:用 universal-modder 的 um win 从 WSL 驱动游戏,um video 完成录制到成片
AoE2DE 模组演示视频制作:用 universal modder 的 um win 从 WSL 驱动游戏,um video 完成录制到成片 本文基于仓库中的
AdGuardHomeRules社区贡献指南:如何参与规则维护和项目发展
AdGuardHomeRules社区贡献指南:如何参与规则维护和项目发展 AdGuardHomeRules是一个由社区驱动的广告拦截规则项目,致力于打造全网最强
网络安全
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考