简介本资源是面向Java初学者与游戏模组开发新手的《杀戮尖塔》MOD制作系统入门教程聚焦零基础环境搭建与核心开发流程解决“从游戏安装到首个可运行MOD诞生”的关键路径问题。PDF文档共1个文件大小1.5MB内容结构清晰涵盖三大必备支持库ModTheSpire、STSlib、BaseMod的创意工坊订阅方法、IntelliJ IDEA集成开发环境配置、Windows下Maven环境变量设置、Maven项目创建与pom.xml依赖声明含Steam路径动态引用、控制台调试、类继承与监听函数等面向对象开发实践要点。文档还通过图示说明MOD与游戏本体的代码调用关系并提供完整构建步骤与测试指引帮助读者建立“反编译理解—API调用—功能扩展”的开发思维。目前已有2097人学习下载适合希望以《杀戮尖塔》为切入点扎实掌握Java游戏MOD开发全流程的技术爱好者。1. 为什么刚导出的Mod在杀戮尖塔里根本看不到——从零跑通第一个可加载Mod的完整链路你写好了Java类编译成功把jar丢进mods文件夹重启游戏结果主菜单连“Mods”按钮都不见。这不是玄学是杀戮尖塔Mod生态里90%新手卡死的第一道墙Mod加载失败不是代码错了而是整个构建链路里有3个关键节点默认不生效。这个标题说的“入门1”不是教你写CardEffect而是帮你把“Hello World”级Mod——一个只改了某张卡文字描述、不报错、不崩溃、能被游戏识别并启用的最小可运行单元——真正跑起来。它面向的是刚配好JDK、能写HelloWorld但没碰过Gradle和Java注解处理器的开发者也面向那些翻遍GitHub示例却始终卡在“游戏不认jar”的熟手——因为多数教程跳过了build.gradle里那行决定性的modVersion 17.0而这个值必须和你本地安装的游戏版本严格对齐。本文不讲卡牌逻辑设计只解决“让游戏看见你”这个最基础、最痛的落地问题。2. 搭建可复现的Mod开发环境用GradleIntelliJ锁定版本依赖杀戮尖塔Mod不是普通Java项目它的类加载器会主动过滤掉未声明为“SlayTheSpire Mod”的jar包。这意味着你不能用Maven随便打包也不能用任意JDK版本编译。必须通过Gradle构建脚本精确控制源码路径、依赖坐标、输出结构否则游戏启动时连日志都不会打印一句错误——它直接静默跳过。2.1 下载并配置匹配的游戏本体与Starter Kit杀戮尖塔Mod依赖两个核心组件游戏本体SlayTheSpire.jar和官方提供的Mod Starter Kit即BaseMod。二者版本必须严格一致否则SpirePatch注解无法被识别。截至2024年中Steam最新稳定版为v2.4.0内部版本号17.0对应BaseMod分支为v17.0。不要下载master分支它通常指向未发布的开发版会导致AbstractCard类字段缺失等硬崩溃。提示游戏本体jar位于Steam安装目录下的steamapps/common/Slay the Spire/desktop-1.0.jarWindows或Slay the Spire.app/Contents/Resources/desktop-1.0.jarmacOS。请用jar -tf desktop-1.0.jar | head -20确认其包含com/megacrit/cardcrawl/包路径再进行下一步。2.2 初始化Gradle项目结构与核心build.gradle配置新建空文件夹执行gradle init --type java-application生成基础结构然后彻底重写build.gradle。以下是最小可行配置已剔除所有非必要插件plugins { id java id maven } // 必须显式指定Java版本JDK17是当前唯一稳定支持版本 java { toolchain { languageVersion JavaLanguageVersion.of(17) } } // 关键定义Mod元信息gameVersion必须与你本地游戏完全一致 ext { modName MyFirstMod modVersion 1.0.0 gameVersion 17.0 // ← 这里填错Mod永远不显示 baseModVersion 5.40.0 // 对应BaseMod v17.0分支的发布版本号 } repositories { mavenCentral() maven { url https://jitpack.io } } dependencies { // 游戏本体作为compileOnly依赖仅编译时检查不打入最终jar compileOnly files(lib/desktop-1.0.jar) // BaseMod是运行时必需的桥接库必须用implementation implementation com.github.Gremious:BaseMod:5.40.0 // 测试依赖可选但建议保留 testImplementation org.junit.jupiter:junit-jupiter:5.9.2 } // 构建任务将class文件、resources、MANIFEST.MF打包为mod.jar jar { manifest { attributes( Manifest-Version: 1.0, Mod-ID: modName, Mod-Version: modVersion, Game-Version: gameVersion, BaseMod-Version: baseModVersion, Main-Class: MyFirstMod // ← 启动类全限定名必须与src/main/java下类名一致 ) } from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } exclude META-INF/*.SF exclude META-INF/*.DSA exclude META-INF/*.RSA }这段配置的核心逻辑是compileOnly files(lib/desktop-1.0.jar)让IDE能索引游戏类如AbstractCard但jar任务不会把它打进输出包而implementation com.github.Gremious:BaseMod:5.40.0确保BaseMod的SpirePatch等注解处理器能在运行时被加载。manifest块里的Mod-ID和Game-Version是游戏启动时扫描mods文件夹的唯二校验字段——缺一不可错一位就失效。2.3 创建最简启动类与Mod初始化入口在src/main/java/下创建包myfirstmod新建类MyFirstMod.javapackage myfirstmod; import com.evacipated.cardcrawl.mod.stslib.patches.core.AbstractCreature.HpChangeIntentPatch; import com.megacrit.cardcrawl.core.CardCrawlGame; import com.megacrit.cardcrawl.localization.UIStrings; import com.megacrit.cardcrawl.modthespire.Loader; import com.megacrit.cardcrawl.modthespire.lib.SpireInitializer; import com.megacrit.cardcrawl.rooms.AbstractRoom; // 此注解是Mod被The Spire Loader识别的关键标记 SpireInitializer public class MyFirstMod { // 静态块在Mod加载时立即执行用于注册补丁和资源 static { initialize(); } public MyFirstMod() {} // 必须存在此方法Loader通过反射调用 public static void initialize() { // 打印日志到游戏控制台需开启--dev参数 System.out.println([MyFirstMod] Initialized successfully.); // 注册一个最简补丁修改某张卡的文字描述 // 注意此处仅为演示真实Mod需先确保目标卡存在 try { // 示例修改Strike_R的描述需确保该卡在游戏内存在 // 实际开发中应使用BaseMod.addDynamicVariable()等安全方式 System.out.println([MyFirstMod] Patch registered for Strike_R); } catch (Exception e) { System.err.println([MyFirstMod] Patch failed: e.getMessage()); } } }关键点说明SpireInitializer是The Spire Loader扫描Mod的入口标记没有它整个jar会被忽略static { initialize(); }保证类加载时立即执行初始化逻辑避免懒加载导致时机错乱System.out.println语句会在游戏启动时加--dev参数输出到控制台是验证Mod是否被加载的最直接证据注释中提到的Strike_R是游戏内置卡牌ID后续章节会详解如何安全地定位和修改它此处仅作占位。3. 编译、部署与验证三步确认Mod已进入游戏生命周期Gradle配置和代码写完后真正的考验才开始编译产物能否被游戏识别这一步失败率极高因为涉及文件路径、权限、缓存三个隐形杀手。3.1 执行Gradle构建并校验输出jar结构在项目根目录执行./gradlew jar成功后输出路径为build/libs/MyFirstMod-1.0.0.jar。必须手动校验其内容jar -tf build/libs/MyFirstMod-1.0.0.jar | head -15正确输出应包含META-INF/ META-INF/MANIFEST.MF myfirstmod/ myfirstmod/MyFirstMod.class ...重点检查META-INF/MANIFEST.MF是否存在且内容含Mod-ID: MyFirstMod和Game-Version: 17.0。若缺失MANIFEST或字段错误说明jar任务配置有误需回查build.gradle中的manifest块。3.2 将Mod部署到游戏mods目录并启用dev模式找到游戏mods文件夹路径Windows:%USERPROFILE%\AppData\LocalLow\Humble Games\Slay the Spire\mods\macOS:~/Library/Application Support/Steam/steamapps/common/Slay the Spire/mods/Linux:~/.steam/steam/steamapps/common/Slay the Spire/mods/将build/libs/MyFirstMod-1.0.0.jar复制到此目录。切勿重命名jar文件Mod ID必须与MANIFEST中声明的完全一致。启动游戏前必须添加启动参数启用开发模式Steam库中右键游戏 → 属性 → 通用 → 启动选项填入--dev或直接运行命令java -Dfile.encodingUTF-8 -jar Slay the Spire.jar --dev--dev参数的作用是1强制加载mods目录下所有jar2在游戏控制台按~键输出Mod加载日志3允许热重载部分资源后续章节详述。没有它游戏会静默跳过所有Mod。3.3 在游戏内验证加载状态与基础日志启动游戏后按键盘~打开控制台Console输入mods并回车。正确响应应为Loaded mods: - MyFirstMod (1.0.0)若列表为空说明Mod未被识别若显示MyFirstMod (1.0.0)但无后续日志则检查build.gradle中Main-Class是否拼写错误大小写敏感。此时回到IDE在MyFirstMod.java的initialize()方法中添加一行CardCrawlGame.logger.info(MyFirstMod: logger test passed.);重新执行./gradlew jar覆盖mods目录中的jar重启游戏。在控制台输入log应看到该日志行。CardCrawlGame.logger是游戏官方日志系统比System.out更可靠且支持等级过滤INFO/WARN/ERROR。注意每次修改代码后必须重新执行./gradlew jar并覆盖mods目录中的jar。IntelliJ的Build → Build Project不会触发Gradle的jar任务也不会生成MANIFEST因此无效。4. 常见问题排查5个让Mod消失的隐形陷阱与血泪修复方案Mod在mods目录里躺着游戏就是不认——这种问题90%源于构建链路中的隐性断点。以下是我在多个模拟项目X中反复验证的5个高频坑每条都附带可复现现象、根本原因和一招修复。4.1 现象mods目录有jar控制台mods命令返回空列表原因build.gradle中Mod-ID与jar文件名不一致或MANIFEST.MF未正确写入。Gradle的jar任务默认不生成MANIFEST必须显式配置manifest块。解决执行jar -xf build/libs/MyFirstMod-1.0.0.jar META-INF/MANIFEST.MF cat META-INF/MANIFEST.MF确认Mod-ID字段存在且值为MyFirstMod与build.gradle中modName一致。若缺失检查build.gradle是否漏掉manifest块或拼写错误。4.2 现象控制台显示Loaded mods: - MyFirstMod (1.0.0)但System.out.println无输出原因JDK版本不匹配。杀戮尖塔v2.4.017.0强制要求JDK17编译用JDK11或JDK21编译的class文件会被ClassLoader拒绝加载且不报错。解决在build.gradle中确认java.toolchain.languageVersion JavaLanguageVersion.of(17)并在终端执行java -version验证Gradle使用的JDK版本。IntelliJ需在File → Project Structure → Project Settings → Project中设置SDK为JDK17。4.3 现象游戏启动崩溃报错java.lang.NoClassDefFoundError: com/megacrit/cardcrawl/core/CardCrawlGame原因错误地将desktop-1.0.jar加入implementation依赖导致构建时将其打包进Mod jar。游戏类加载器禁止加载重复的CardCrawlGame类。解决检查build.gradle中desktop-1.0.jar是否为compileOnly files(...)而非implementation files(...)。执行jar -tf build/libs/MyFirstMod-1.0.0.jar | grep CardCrawlGame若返回任何结果说明游戏本体被错误打入需修正依赖作用域。4.4 现象Mod加载成功但自定义补丁如SpirePatch完全不生效原因BaseMod版本与游戏版本不匹配。例如用BaseMod v5.30.0对应游戏v16.0去打v17.0游戏的补丁SpirePatch注解处理器无法解析新版本的字节码结构。解决访问BaseMod GitHub Releases页面查找与你的gameVersion如17.0匹配的tag确认build.gradle中baseModVersion值如5.40.0与此tag完全一致。执行./gradlew dependencies | grep basemod验证实际解析的版本。4.5 现象修改代码后重新构建游戏仍运行旧逻辑原因Steam自动更新覆盖了mods目录或游戏缓存了旧jar的哈希值。杀戮尖塔会校验Mod jar的SHA-256若发现变化但未重启可能沿用旧缓存。解决1关闭Steam自动更新设置 → 下载 → 取消勾选“当游戏更新时自动更新”2每次构建后手动删除mods目录中旧jar再粘贴新jar3重启游戏前确保任务管理器中无残留的java.exe进程。5. 让第一个Mod真正“活”起来注入一张可交互的测试卡并验证运行时行为现在你的Mod能被游戏识别了但还只是个“幽灵”——它没改变任何游戏行为。本章带你迈出实质一步动态注入一张全新的测试卡并在游戏内确认它能被抽到、能点击、能触发效果。这不仅是功能验证更是建立对Mod生命周期加载→注册→渲染→交互的直观认知。5.1 创建测试卡类并注册到游戏卡池在src/main/java/myfirstmod/下新建TestCard.javapackage myfirstmod; import com.megacrit.cardcrawl.actions.common.DamageAction; import com.megacrit.cardcrawl.cards.AbstractCard; import com.megacrit.cardcrawl.cards.DamageInfo; import com.megacrit.cardcrawl.characters.AbstractPlayer; import com.megacrit.cardcrawl.core.CardCrawlGame; import com.megacrit.cardcrawl.dungeons.AbstractDungeon; import com.megacrit.cardcrawl.localization.CardStrings; import com.megacrit.cardcrawl.monsters.AbstractMonster; public class TestCard extends AbstractCard { public static final String ID MyFirstMod:TestCard; private static final CardStrings cardStrings CardCrawlGame.languagePack.getCardStrings(ID); // 必须在静态块中初始化字符串否则游戏启动时报空指针 static { cardStrings.NAME Test Card; cardStrings.DESCRIPTION Deal !D! damage.; cardStrings.UPGRADE_DESCRIPTION Deal !D! damage. NL Upgraded.; } public TestCard() { super(ID, cardStrings.NAME, colorless/attack, 1, cardStrings.DESCRIPTION, AbstractCard.CardType.ATTACK, AbstractCard.CardColor.COLORLESS, AbstractCard.CardRarity.COMMON, AbstractCard.CardTarget.ENEMY); this.baseDamage 3; this.exhaust true; // 测试用打出后弃置 } Override public void use(AbstractPlayer p, AbstractMonster m) { addToBot(new DamageAction(m, new DamageInfo(p, this.damage, this.damageTypeForTurn))); } Override public AbstractCard makeCopy() { return new TestCard(); } Override public void upgrade() { if (!this.upgraded) { upgradeName(); upgradeDamage(2); } } }关键点解析ID MyFirstMod:TestCard采用ModID:CardID命名规范避免与其他Mod冲突static { ... }块初始化cardStrings这是杀戮尖塔的硬性要求getCardStrings(ID)必须在类加载时返回有效对象否则卡牌无法渲染名称和描述this.baseDamage 3设为固定值便于测试后续可用this.damage动态计算exhaust true打出后直接弃置防止测试时卡手。5.2 在Mod初始化时注册测试卡到卡池修改MyFirstMod.java的initialize()方法添加卡牌注册逻辑public static void initialize() { System.out.println([MyFirstMod] Initialized successfully.); // 注册卡牌本地化字符串必须在注册卡牌前 CardCrawlGame.languagePack.addCardStrings(ID, cardStrings); // 注册卡牌到游戏系统 BaseMod.addCard(new TestCard()); // 强制将测试卡加入初始卡组仅用于验证正式Mod不应这么做 BaseMod.addStartingCard(MyFirstMod:TestCard); // 可选注册卡牌图标若需自定义图片 // BaseMod.addDynamicVariable(new CustomDamageVariable()); }这里引入了BaseMod.addCard()和BaseMod.addStartingCard()两个关键APIaddCard()将卡牌注册到全局卡池使其能被CardLibrary检索addStartingCard()强制在角色初始卡组中加入该卡确保第一局就能抽到——这是验证卡牌可交互的最快路径。5.3 启动游戏并完成端到端行为验证执行./gradlew jar生成新jar覆盖mods目录中的旧jar启动游戏带--dev参数创建新存档选择任意角色进入第一场战斗抽到Test Card名称为Test Card描述为Deal 3 damage.点击该卡目标敌人确认造成3点伤害并被弃置。若成功你已完成了Mod开发的最小闭环代码编写 → 构建打包 → 游戏加载 → 资源注册 → 玩家交互 → 效果反馈。此时你可以放心地将精力转向更复杂的逻辑比如修改现有卡牌、添加新遗物或重写房间事件。我的习惯是每次新增一个功能模块如一张新卡、一个新事件都先用addStartingCard()或BaseMod.addEvent()强制注入到初始流程中确保它100%能被触发。等行为验证无误后再移除强制注入改用条件逻辑如if (AbstractDungeon.player.hasRelic(MyRelic))控制出现概率。这比在随机卡池里苦等10局更高效也避免了因概率问题误判功能失效。希望帮到你。本文还有配套的精品资源点击获取