- これらは2026/7/27現在の情報です
- 今後のアップデートによって破壊的変更があることがありえます
- 実際、先日のpublic beta配布時にbaselibが未対応であり、STS2の起動すらできなくなった
- 今は解消されており、今回利用させていただく
- 今後のアップデートによって破壊的変更があることがありえます
はじめに
- Slay the Spire 2のMOD開発に興味を持った
- いきなりカードやキャラクターを追加するのではなく、最小構成から試す
- 今回の記事の位置づけ
- 初心者向け
- 自分の学習記録
- 環境構築の振り返り
- エラー対応の記録
- 今回扱わない内容
- 本格的なカード追加
- キャラクター追加
- 複雑なHarmony Patch
- Workshop公開
今回の目標
- こちらのwiki等を参考に進める
- Empty Modテンプレートから空MODを作成する
- 以下のファイルを生成する
- DLL
- PCK
- manifest JSON
- STS2にMODとして読み込ませる
- 自分で追加したC#コードを実行する
-
godot.logで実行結果を確認する - 最終的な成功条件
- manifestが検出される
- DLLが読み込まれる
- PCKが読み込まれる
- 初期化メソッドが呼ばれる
- 自作ログが出力される
- ゲーム画面に読み込み済みMODとして表示される
STS2のMOD構成
- 今回生成された主なファイル(MOD名をMyFirstSts2Modとして、)
MyFirstSts2Mod.dllMyFirstSts2Mod.pckMyFirstSts2Mod.jsonMyFirstSts2Mod.pdb
- DLL
- Dynamic Link Library
- C#コードをコンパイルしたもの
- MODの初期化処理を含む
- Harmony Patchなどの処理を含められる
- PCK
- 画像
- ローカライズ
- Godotシーン
- その他の非コード資産
- manifest JSON
- MOD ID
- 表示名
- 作者
- 説明
- バージョン
- 対応ゲームバージョン
- DLL・PCKの有無
- 依存MOD
- 補足
- すべてのMODが必ずDLL・PCK・JSONの3種類を持つとは限らない
- コードがなければDLL不要の場合がある
- 非コード資産がなければPCK不要の場合がある
使用した開発環境
- Windows版Slay the Spire 2
- steamで購入
- Steam Workshop版BaseLib
- .NET 9 SDK
- 9以上ならなんでもよい(はず)
-
https://dotnet.microsoft.com/ja-jp/download/dotnet/9.0?utm_source=chatgpt.com
- 最新のものを選択
- ランタイム9.0.18, SDK9.0.316
- ランタイムではなくSDKを選択すること
- ランタイム9.0.18, SDK9.0.316
- 最新のものを選択
- JetBrains Rider
- MegaDot
-
https://megadot.megacrit.com/?utm_source=chatgpt.com
- 現時点で最新のVersion 4.5.1-m.14を選択
-
https://megadot.megacrit.com/?utm_source=chatgpt.com
Alchyr.Sts2.Templates- 注意点
- .NET Runtimeではなく.NET SDKを入れる
- MegaDotはパスが変わらない場所へ置く
- Riderはプロジェクトを開くまでTerminalが表示されない
- STS2のアップデート直後はBaseLibやMODが壊れる場合がある
BaseLibの導入確認
-
Steam WorkshopからBaseLibを購読
-
BaseLib単体でSTS2を起動
-
確認した内容
- ゲームが起動する
- MOD環境で起動する
- BaseLibが正常に読み込まれる
-
先にBaseLib単体を確認する理由
- STS2本体の問題を切り分ける
- BaseLib側の問題を切り分ける
- 自作MOD側の問題を切り分ける
-
実際に起きた問題
- ベータ版アップデート直後にMOD全般が起動不能
- BaseLib修正後に再び起動可能
-
学んだこと
- ゲーム更新とMOD側対応には時間差がある
- Early Access中は環境が頻繁に変わる可能性がある
STS2用テンプレートの導入
- 使用したコマンド
- powershell等で
dotnet new install Alchyr.Sts2.Templates
-
dotnet newについて- .NET SDKに含まれるテンプレート管理機能
- NuGetからカスタムテンプレートを取得できる
- プロジェクトのひな型を生成できる
-
導入された主なテンプレート
- Empty Mod
- Content Mod
- Character Mod
-
今回選んだもの
- Empty Mod
-
選んだ理由
- 最小構成で試せる
- コードやアセットを増やす前に環境確認できる
- エラー原因を切り分けやすい
-
確認方法
- RiderのCustom Templates欄
dotnet new list sts2 // output Slay the Spire 2 Mod Slay the Spire 2 Content Slay the Spire 2 Character
Empty Modプロジェクトの作成
-
Riderを起動&新規ソリューション
- 既にプロジェクトがあるが気にしないでください
-
プロジェクト名
- なんでもいい
-
MyFirstSts2Modとした
-
作成時の設定
- プロジェクト名に空白を入れない
- Solution形式は
.sln - SolutionとProjectを同じディレクトリに置く
- 作者名を設定
- MOD IDは初期値を利用
-
生成された主なファイル
MyFirstSts2Mod.slnMyFirstSts2Mod.csprojDirectory.Build.propsproject.godotMyFirstSts2Mod.jsonMyFirstSts2ModCode/MainFile.cs
-
少し迷った点
- Rider上で
.csprojが見えない - Buildログでは実際に
.csprojが使用されていた
- Rider上で
STS2とMegaDotのパス設定
-
自分の場合は以下のように編集
<Project> <PropertyGroup> <!-- Megadot current version is 4.5.1, and the game won't load your .pck if the Godot version used is newer. --> <!-- MegaDot / Godot 4.5.1 mono executable used for export-pack when publishing --> <GodotPath>C:/Users/XXXXX/Downloads/megadot-4.5.1-m.14-windows-x86_64-llvm-editor-csharp/MegaDot_v4.5.1-stable_mono_win64.exe</GodotPath> <!-- If sts2 is not found automatically, uncomment the following and set it manually --> <!-- <Sts2Path>?/steamapps/common/Slay the Spire 2</Sts2Path> --> </PropertyGroup> </Project> -
編集するファイル
Directory.Build.props
-
主な設定項目
GodotPath- 場合によっては
Sts2Path
-
GodotPath- MegaDotの実行ファイルを指定
- フォルダではなく
.exeまで指定
-
例
<GodotPath>C:\Tools\MegaDot\MegaDot.exe</GodotPath>
-
Sts2Path- STS2のインストールフォルダ
- 自動検出される場合は設定不要
- 検出失敗時のみ手動設定
- 今回の結果
- MegaDotのパスのみ設定
- STS2は自動検出成功
BuildとPublishの違い
- Build
- 画面上部のハンマーのマーク
- C#コードをコンパイル
- DLLを生成
- DLL・PDB・JSONをmodsフォルダへコピー
- コードのみ変更した場合に使用
- Publish
- 画面左上のソリューション(ファイル一覧)のトップフォルダを右クリック
- 公開(public)
- C#コードをコンパイル
- DLLを生成
- MegaDotでPCKを生成
- manifest JSONと一緒に配置
- 画像やローカライズ変更時に使用
- 画面左上のソリューション(ファイル一覧)のトップフォルダを右クリック
- 今回の使い分け
- 初回はPublish
- ログ出力追加後はBuild
- 理解した流れ
C#コード
↓
Build
↓
DLL
画像・ローカライズ・Godot資産
↓
Publish
↓
PCK
BuildとPublishの実行
- 最初にBuildを実行
- Build結果
- 成功
- エラー0
- 警告1
- 続いてPublishを実行
- 配置先
Slay the Spire 2/
└─ mods/
└─ MyFirstSts2Mod/
├─ MyFirstSts2Mod.dll
├─ MyFirstSts2Mod.pck
├─ MyFirstSts2Mod.json
└─ MyFirstSts2Mod.pdb
- Workshop版MODとの違い
- Workshop版
steamapps/workshop/content/
- ローカルMOD
- STS2実行ファイル本体直下の
mods/
- STS2実行ファイル本体直下の
- Workshop版
- 確認した内容
- ファイルが存在する
- ファイルサイズが0ではない
- 更新時刻がBuild・Publish実行時刻と一致する
MOD読み込みの確認
-
ゲーム画面から確認
-
最初の確認
- Settings → Mod Settingsを確認
- 自作MOD名が見当たらなかった
-
当初の誤解
- Mod Settingsが全MOD一覧だと思っていた
-
実際の確認方法
godot.log- ゲーム起動画面の読み込み済みMOD表示
-
ログで確認した内容
- manifest検出
- DLL読み込み
- PCK読み込み
- 初期化メソッド呼び出し
- 初期化完了
-
ログ例
Found mod manifest file ...MyFirstSts2Mod.json
Loading assembly DLL ...MyFirstSts2Mod.dll
Loading Godot PCK ...MyFirstSts2Mod.pck
Calling initializer method ...
Finished mod initialization for 'MyFirstSts2Mod'
- ゲーム画面での確認
- BaseLib
- MyFirstSts2Mod
- 合計2つのMOD読み込み済み
- 学んだこと
- Mod Settingsに表示されなくても読み込み失敗とは限らない
- MOD設定画面とMOD読み込み一覧は別物
- ログ確認が重要
自作C#コードの実行確認
- 使用したファイル
MyFirstSts2ModCode/MainFile.cs
- 初期状態の役割
- MOD読み込み時の初期化処理
- Harmony Patchの適用
- 主要部分
[ModInitializer(nameof(Initialize))]
public partial class MainFile : Node
{
public static void Initialize()
{
Harmony harmony = new(ModId);
harmony.PatchAll();
}
}
- 追加したコード
Logger.Info("Hello from MyFirstSts2Mod!");
- 全コード
using Godot;
using HarmonyLib;
using MegaCrit.Sts2.Core.Modding;
namespace MyFirstSts2Mod.MyFirstSts2ModCode;
//You're recommended but not required to keep all your code in this package and all your assets in the MyFirstSts2Mod folder.
[ModInitializer(nameof(Initialize))]
public partial class MainFile : Node
{
public const string ModId = "MyFirstSts2Mod"; //At the moment, this is used only for the Logger and harmony names.
public static MegaCrit.Sts2.Core.Logging.Logger Logger { get; } =
new(ModId, MegaCrit.Sts2.Core.Logging.LogType.Generic);
public static void Initialize()
{
//If you want to use scripts defined in your mod for Godot scenes, uncomment the following line.
//Godot.Bridge.ScriptManagerBridge.LookupScriptsInAssembly(Assembly.GetExecutingAssembly());
Harmony harmony = new(ModId);
harmony.PatchAll();
// add
Logger.Info("Hello from MyFirstSts2Mod!");
}
}
- 実行手順
- コードを保存
- Build
- STS2を完全終了
- STS2を再起動
-
godot.logを検索
- PowerShellでの確認
Select-String `
-Path "$env:APPDATA\SlayTheSpire2\logs\godot.log" `
-Pattern "Hello from MyFirstSts2Mod"
- 確認結果
[INFO] [MyFirstSts2Mod] Hello from MyFirstSts2Mod!
- 確認できた流れ
- C#コード編集
- Build
- DLL更新
- STS2がDLLを読み込み
- 初期化処理を実行
- 自作ログを出力
発生した警告とエラー
ローカライズ関連警告
- 警告内容
STS002: Localization files must be added as additional files for analysis
- 状況
- Empty Mod
- ローカライズファイル未使用
- Build成功
- エラー0
- 今回の判断
- 一旦無視
- カードやテキスト追加時に再確認
Publish時のsts2.dll読み込み例外
- 表示内容
Could not load file or assembly 'sts2, Version=0.1.0.0'
- 状況
- 例外後もPCK生成は継続
- ゲーム側ではDLL読み込み成功
- PCK読み込み成功
- 初期化成功
- 今回の判断
- 空MODの動作には影響なし
- Godotシーン連携時には再調査候補
他MOD由来のエラー
- 自作MOD以外のHarmonyエラーがログに存在
- 問題点
- 複数MODがあると原因特定しにくい
- 対応
- BaseLibと自作MODだけにする
- ログを取り直す
- 学んだこと
- 開発中は最小構成で検証する
今回理解したMOD開発の流れ
- 全体像
C#コードを書く
↓
BuildでDLL生成
↓
必要に応じてPublishでPCK生成
↓
manifest JSONと一緒にmodsフォルダへ配置
↓
STS2がmanifestを検出
↓
DLL・PCKを読み込む
↓
ModInitializerで指定された処理を実行
- BaseLibの役割
- 開発時
- NuGetパッケージとして参照
- MOD開発用APIや補助機能を利用
- 実行時
- Workshop版BaseLibをSTS2が読み込む
- 自作MODの依存関係として動作
- 開発時
- テンプレートの役割
- プロジェクト構成を用意
- BaseLib参照を用意
- manifestを用意
- Build・Publish処理を用意
- modsフォルダへのコピーを自動化
まとめ
- 今回できたこと
- BaseLib導入
- .NET SDK導入
- Rider導入
- MegaDot導入
- STS2テンプレート導入
- Empty Mod作成
- Build成功
- Publish成功
- DLL・PCK・JSON生成
- STS2でMOD読み込み
- 自作C#コード実行
- ログで実行確認
- 特に重要だった学び
- 最初から大きなMODを作らない
- BaseLib単体で先に確認する
- Mod Settingsだけで判断しない
-
godot.logを見る - 他MODを外して切り分ける
- コード変更とアセット変更でBuild・Publishを使い分ける
- 今回の到達点
- MOD開発環境が動作
- 自分のC#コードがSTS2上で実行
- 次の開発に進める状態
今後試したいこと
- MOD名・説明・画像の変更
- ゲーム内イベント発生時のログ出力
- Harmony Patchの基本確認
- Content Modテンプレートの作成
- Empty Modとの構成比較
- 最小構成のカード追加
- 最小構成のレリック追加
- ローカライズ追加
- 最終的なキャラクターMODへの発展


