docs.unity3d.com
    目次を表示する/隠す

    MOD 作成のサポート

    Unity 2021.1 以降では、追加の Burst コンパイル済みライブラリをロードできるため、そのライブラリを使用すると、Burst でコンパイルしたコードを使用する MOD を作成できます。

    Burst で提供されるのは追加のライブラリをロードする手段のみで、MOD を作成するツールは用意されていません。追加のライブラリをコンパイルするには、Unity エディターが必要です。

    このセクションでは、Burst での MOD 作成手法の例を示し、概念実証を行います。

    サポート対象の用途

    この関数は再生モード (またはスタンドアロンプレイヤー) でのみ使用できます。

    ライブラリはできるだけ早く、Burst でコンパイルした C# メソッドを初めて使用する前にロードするようにしてください。BurstRuntime.LoadAdditionalLibraries がロードした Burst ライブラリは、エディターで再生モードを終了したときや、スタンドアロンプレイヤーを終了したときにアンロードされます。

    MOD 作成システムの例

    Note

    この例は対象範囲が制限されています。この例を実行するにはアセンブリと asmdef に関する知識が必要です。

    以下の例では、MOD が準拠するインターフェースを宣言します。

    using UnityEngine;
    
    public interface PluginModule
    {
        void Startup(GameObject gameObject);
        void Update(GameObject gameObject);
    }
    

    このインターフェースを使用すれば、これらの仕様を満たす新しいクラスを作成し、アプリケーションとは別にリリースできます。1 つの GameObject を渡すことで、プラグインの影響を受ける状態が限定されます。

    MOD 作成マネージャー

    以下は、MOD 作成マネージャーの例です。

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using UnityEngine;
    using Unity.Burst;
    
    public class PluginManager : MonoBehaviour
    {
        public bool modsEnabled;
        public GameObject objectForPlugins;
    
        List<PluginModule> plugins;
    
        void Start()
        {
            plugins = new List<PluginModule>();
    
            // MOD が無効になっている場合はすぐに抜けます。そうすることで MOD の無効化、再生モードの開始と終了、
            //マネージドアセンブリがアンロードされたことの確認 (DomainReload が実行されることを想定) を行えます
            if (!modsEnabled)
                return;
    
            var folder = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "Mods"));
            if (Directory.Exists(folder))
            {
                var mods = Directory.GetDirectories(folder);
    
                foreach (var mod in mods)
                {
                    var modName = Path.GetFileName(mod);
                    var monoAssembly = Path.Combine(mod, $"{modName}_managed.dll");
    
                    if (File.Exists(monoAssembly))
                    {
                        var managedPlugin = Assembly.LoadFile(monoAssembly);
    
                        var pluginModule = managedPlugin.GetType("MyPluginModule");
    
                        var plugin = Activator.CreateInstance(pluginModule) as PluginModule;
    
                        plugins.Add(plugin);
                    }
    
                    var burstedAssembly = Path.Combine(mod, $"{modName}_win_x86_64.dll");      // Burst dll (Windows 64 ビット版を想定)
                    if (File.Exists(burstedAssembly))
                    {
                        BurstRuntime.LoadAdditionalLibrary(burstedAssembly);
                    }
                }
            }
    
            foreach (var plugin in plugins)
            {
                plugin.Startup(objectForPlugins);
            }
        }
    
        // Update が 1 フレームに 1 回呼び出されます
        void Update()
        {
            foreach (var plugin in plugins)
            {
                plugin.Update(objectForPlugins);
            }
        }
    }
    

    このコードでは "Mods" フォルダーをスキャンし、その中に見つかる各フォルダーで、マネージド dll と Burst でコンパイルされた dll の両方のロードを試みます。具体的には、これらの dll を内部リストに追加すると、そのリストで各インターフェース関数の呼び出しを繰り返すことができます。

    ファイルの名前は任意です。それらのファイルを生成したコードである、シンプルな MOD 作成メニューボタン を参照してください。

    このコードは、マネージドアセンブリを現在のドメインにロードするので、上書きする前にアンロードするために、ドメインの再ロードが必要です。再生モードを終了すると、Burst dll ファイルが自動的にアンロードされます。そのため、エディターでテストできるように MOD 作成システムを無効にするための Boolean が含まれています。

    Burst を使用する Mod

    このための個別の Unity プロジェクトを作成し、それを使用して MOD を生成します。

    以下のスクリプトは、Main UI Label というテキストコンポーネントを含む UI キャンバスにアタッチされ、MOD が使用されたときにテキストを変更します。テキストは Plugin Updated : Bursted または Plugin Updated : Not Bursted のいずれかです。デフォルトでは Plugin Updated : Bursted が表示されますが、上記の PluginManager で Burst ライブラリをロードする行をコメントアウトすると、Burst でコンパイルされたコードはロードされず、メッセージが適切に変更されます。

    using Unity.Burst;
    using Unity.Collections;
    using Unity.Jobs;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class MyPluginModule : PluginModule
    {
        Text textComponent;
    
        public void Startup(GameObject gameObject)
        {
            var childTextComponents = gameObject.GetComponentsInChildren<Text>();
            textComponent = null;
            foreach (var child in childTextComponents)
            {
                if (child.name == "Main UI Label")
                {
                    textComponent = child;
                }
            }
    
            if (textComponent==null)
            {
                Debug.LogError("something went wrong and i couldn't find the UI component i wanted to modify");
            }
        }
    
        public void Update(GameObject gameObject)
        {
            if (textComponent != null)
            {
                var t = new CheckBurstedJob { flag = new NativeArray<int>(1, Allocator.TempJob, NativeArrayOptions.UninitializedMemory) };
    
                t.Run();
    
                if (t.flag[0] == 0)
                    textComponent.text = "Plugin Updated : Not Bursted";
                else
                    textComponent.text = "Plugin Updated : Bursted";
    
                t.flag.Dispose();
            }
        }
    
        [BurstCompile]
        struct CheckBurstedJob : IJob
        {
            public NativeArray<int> flag;
    
            [BurstDiscard]
            void CheckBurst()
            {
                flag[0] = 0;
            }
    
            public void Execute()
            {
                flag[0] = 1;
                CheckBurst();
            }
        }
    
    }
    

    上記のスクリプトを、TestMod_Managed というアセンブリ名のアセンブリ定義ファイルとともにフォルダー内に配置すると、次のスクリプトでマネージド部分を特定できます。

    シンプルな MOD 作成メニューボタン

    以下のスクリプトでは、メニューボタンを追加します。このメニューボタンを使用すると、スタンドアロンプレイヤーがビルドされ、選択した MOD フォルダーに C# マネージド dll と lib_burst_generated.dll がコピーされます。この例は、Windows を使用していることを前提としています。

    using UnityEditor;
    using System.IO;
    using UnityEngine;
    
    public class ScriptBatch
    {
        [MenuItem("Modding/Build X64 Mod (Example)")]
        public static void BuildGame()
        {
            string modName = "TestMod";
    
            string projectFolder = Path.Combine(Application.dataPath, "..");
            string buildFolder = Path.Combine(projectFolder, "PluginTemp");
    
            // ファイル名を取得します。
            string path = EditorUtility.SaveFolderPanel("Choose Final Mod Location", "", "");
    
            FileUtil.DeleteFileOrDirectory(buildFolder);
            Directory.CreateDirectory(buildFolder);
    
            // プレイヤーをビルドします。
            var report = BuildPipeline.BuildPlayer(new[] { "Assets/Scenes/SampleScene.unity" }, Path.Combine(buildFolder, $"{modName}.exe"), BuildTarget.StandaloneWindows64, BuildOptions.Development);
    
            if (report.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded)
            {
                // マネージドライブラリをコピーします。
                var managedDest = Path.Combine(path, $"{modName}_Managed.dll");
                var managedSrc = Path.Combine(buildFolder, $"{modName}_Data/Managed/{modName}_Managed.dll");
                FileUtil.DeleteFileOrDirectory(managedDest);
                if (!File.Exists(managedDest))  // マネージド側がアンロードされない
                    FileUtil.CopyFileOrDirectory(managedSrc, managedDest);
                else
                    Debug.LogWarning($"Couldn't update manged dll, {managedDest} is it currently in use?");
    
                // Burst ライブラリをコピーします。
                var burstedDest = Path.Combine(path, $"{modName}_win_x86_64.dll");
                var burstedSrc = Path.Combine(buildFolder, $"{modName}_Data/Plugins/x86_64/lib_burst_generated.dll");
                FileUtil.DeleteFileOrDirectory(burstedDest);
                if (!File.Exists(burstedDest))
                    FileUtil.CopyFileOrDirectory(burstedSrc, burstedDest);
                else
                    Debug.LogWarning($"Couldn't update bursted dll, {burstedDest} is it currently in use?");
            }
        }
    }
    
    トップに戻る
    Copyright © 2023 Unity Technologies — 商標と利用規約
    • 法律関連
    • プライバシーポリシー
    • クッキー
    • 私の個人情報を販売または共有しない
    • Your Privacy Choices (Cookie Settings)