mcpField
U
unity-modding

unity-modding

github.com/josiahsiegel開発59126 回インストール19 週間前に更新
リポジトリ

unity-modding について

コミュニティによる MOD 対応や既存ゲームの改造を設計する Unity 開発者向けのアーキテクチャガイドです。アセット読み込み、スクリプト API、MOD 管理、配布方法と安全面を扱います。

主な機能

  1. 01データ、アセット、スクリプト、コード MOD の段階
  2. 02Addressables と Asset Bundles による読み込み
  3. 03MoonSharp の Lua スクリプト API
  4. 04マニフェスト、依存関係、読み込み順
  5. 05Steam Workshop と Harmony の解説
  6. 06スクリプトのサンドボックスに関する注意

活用シーン

  1. 01新しい Unity ゲームの MOD 対応設計
  2. 02実行時の MOD アセットと設定の読み込み
  3. 03Lua MOD API と管理機能の構築
  4. 04既存ゲーム向けコードパッチの設計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill unity-modding

インストール後、Claude Code で/unity-moddingを入力して読み込まれたか確認します。

詳細

Unity の Mod 対応アーキテクチャ

概要

コミュニティによる Mod 制作に対応した Unity ゲームを設計するためのガイドです。アセット読み込みシステム、スクリプト Mod API、Mod マネージャーのパターン、配布プラットフォーム、既存ゲームを Mod 対応にする手法を扱います。

Mod 対応アーキテクチャの段階

段階Mod 制作者ができること複雑さ
データ Modテクスチャ、サウンド、設定ファイル(JSON/XML)の置き換え低
アセット ModAsset Bundle を使った新しいモデル、マップ、アイテムの追加中
スクリプト ModLua/スクリプト API を使ったカスタムゲームロジック高
コード ModHarmony/BepInEx を使ったゲームアセンブリのパッチ適用エキスパート

最初から少なくとも段階 1~2 に対応するよう、ゲームを設計してください。段階 3~4 には、意図的な API 設計が必要です。

Mod 用のアセット読み込み

Addressables(推奨)

Addressables は、ローカルバンドル、リモートサーバー、Mod ディレクトリからアセットを読み込むための統一 API を提供します。

// Load a mod's asset catalog at runtime
public async Awaitable LoadModCatalog(string modPath)
{
    string catalogPath = Path.Combine(modPath, "catalog.json");
    var locator = await Addressables.LoadContentCatalogAsync(catalogPath);
    Debug.Log($"Loaded mod catalog with {locator.Keys.Count()} assets");
}

// Load an asset by address (works for both base game and mods)
var prefab = await Addressables.LoadAssetAsync<GameObject>("enemies/goblin");
Instantiate(prefab);

Mod 制作のワークフロー:

  1. Mod 制作者は Addressables を設定した Unity プロジェクトを使用します
  2. Addressable グループを Asset Bundle にビルドします
  3. catalog.json と .bundle ファイルを配布します
  4. ゲームは実行時に Mod ディレクトリからカタログを読み込みます
Asset Bundles(レガシーですが、よりシンプル)
public async Awaitable<AssetBundle> LoadModBundle(string path)
{
    var request = AssetBundle.LoadFromFileAsync(path);
    await request;
    return request.assetBundle;
}

// Load specific asset from bundle
var bundle = await LoadModBundle("mods/weapons/swords.bundle");
var swordPrefab = bundle.LoadAsset<GameObject>("BroadSword");

主なルール:

  • 同じバンドルを二度読み込まないでください(読み込み済みのバンドルを追跡します)
  • Mod が無効化されたらバンドルをアンロードします:bundle.Unload(true)
  • ローカルファイルには AssetBundle.LoadFromFileAsync を使用します(LoadFromMemory は使用しません)
ホットリロード可能なデータ Mod

シンプルなデータ Mod(JSON 設定ファイル、CSV テーブル)の場合:

public T LoadModConfig<T>(string modPath, string fileName)
{
    string filePath = Path.Combine(modPath, fileName);
    if (!File.Exists(filePath)) return default;
    string json = File.ReadAllText(filePath);
    return JsonUtility.FromJson<T>(json);
}

FileSystemWatcher を使って変更を検出し、開発中にホットリロードします。

MoonSharp を使った Lua スクリプト

MoonSharp は、すべての Unity プラットフォームで動作する C# 製の Lua インタープリターです。

セットアップ

NuGet からインストールするか、DLL をダウンロードします。Mod 制作者向けに安全な API サーフェスを登録します:

public class ModScriptEngine
{
    Script _lua;

    public void Initialize()
    {
        _lua = new Script();

        // Register safe game API
        _lua.Globals["SpawnEntity"] = (Func<string, float, float, float, bool>)SpawnEntity;
        _lua.Globals["GetPlayerHealth"] = (Func<float>)(() => player.Health);
        _lua.Globals["ShowMessage"] = (Action<string>)ShowMessage;
        _lua.Globals["RegisterEvent"] = (Action<string, DynValue>)RegisterEvent;
    }

    public void LoadMod(string luaFilePath)
    {
        string code = File.ReadAllText(luaFilePath);
        _lua.DoString(code);
    }

    bool SpawnEntity(string id, float x, float y, float z)
    {
        // Validate and spawn - never trust mod input
        if (!entityRegistry.Contains(id)) return false;
        var pos = new Vector3(x, y, z);
        if (!IsValidSpawnPosition(pos)) return false;
        entityRegistry.Spawn(id, pos);
        return true;
    }
}
Mod 制作者側の Lua
-- my_mod/init.lua
RegisterEvent("OnPlayerEnterZone", function(zoneName)
    if zoneName == "boss_arena" then
        SpawnEntity("dragon_boss", 0, 5, 0)
        ShowMessage("A dragon appears!")
    end
end)
セキュリティに関する考慮事項
リスク軽減策
ファイルシステムへのアクセスLua に System.IO を公開しないでください
無限ループ命令数の上限を設定します:script.Options.InstructionLimit = 100000
メモリの不正使用テーブルサイズを制限し、割り当てを監視します
ネットワークアクセスHTTP/ソケット API は公開しないでください
リフレクションの不正使用MoonSharp はデフォルトでサンドボックス化されていますが、登録する型を検証してください

Mod マネージャーのパターン

アーキテクチャ
Game
├── ModManager (singleton)
│   ├── DiscoverMods(modsFolder)     // Scan for mod manifests
│   ├── ValidateMod(manifest)        // Version compat, dependencies
│   ├── LoadMod(modId)               // Load assets + scripts
│   ├── UnloadMod(modId)             // Clean teardown
│   └── GetLoadOrder()               // Dependency-sorted order
├── ModManifest (ScriptableObject / JSON)
│   ├── id, name, version, author
│   ├── dependencies[]
│   ├── gameVersionMin / gameVersionMax
│   └── entryPoint (script path)
└── ModSandbox
    ├── Lua scripting environment
    └── Restricted API surface
Mod マニフェストの形式
{
    "id": "com.author.mymod",
    "name": "My Awesome Mod",
    "version": "1.2.0",
    "author": "ModAuthor",
    "description": "Adds new enemies and weapons",
    "gameVersionMin": "1.0.0",
    "gameVersionMax": "2.0.0",
    "dependencies": ["[email protected]"],
    "entryPoint": "scripts/init.lua",
    "assets": "bundles/"
}

Steam Workshop との統合

Steam Workshop との統合には、Steamworks.NET または Facepunch.Steamworks を使用します:

  1. アップロード: Mod 制作者が Mod フォルダーをパッケージ化し、SteamUGC.CreateItem + SteamUGC.SubmitItemUpdate を呼び出します
  2. サブスクライブ: プレイヤーは Steam Workshop の UI からサブスクライブします
  3. ダウンロード: ゲームは起動時に SteamUGC.GetSubscribedItems() を照会します
  4. 読み込み: SteamUGC.GetItemInstallInfo のパスから ModManager に読み込みます

Harmony によるパッチ適用(既存ゲームの Mod 対応)

Mod 制作者は、実行時にコンパイル済みゲームコードへパッチを適用するため、Harmony(BepInEx/MelonLoader 経由)を使用します。

[HarmonyPatch(typeof(PlayerHealth), "TakeDamage")]
class DamageModifier
{
    // Prefix: runs before original method
    static bool Prefix(ref float damage)
    {
        damage *= 0.5f; // Halve all damage
        return true;    // true = continue to original
    }

    // Postfix: runs after original method
    static void Postfix(PlayerHealth __instance)
    {
        Debug.Log($"Health after damage: {__instance.CurrentHealth}");
    }
}

Harmony による Mod 対応を想定してゲームを設計する場合、過度なコード難読化を避け、アップデート後もメソッドシグネチャを安定させてください。

追加リソース

参考ファイル
  • references/mod-framework-detail.md -- Mod マネージャーの完全な実装、依存関係の解決アルゴリズム、Mod の読み込み順序、バージョン管理された API のパターン、ホットリロードシステム、Mod テスト用フレームワーク、BepInEx/MelonLoader のセットアップガイド

コメント