マルチプレイの確認に便利なUnity Multiplayer Play Modeパッケージ!にて、ソースコードを編集してもVirtual Playerに反映されないバグの対処法
Unity6から使えるMultiplayer Play Modeという公式パッケージがあります。
Editor上のプレイボタンを押すと最大3つのプレイヤーが自動で立ち上がり(Editor本体を入れて4つ)、
マルチプレイゲームのデバッグが簡単にできる!という非常に便利なパッケージです。
この時、Editor以外のプレイヤーのことをVirtual Playerと呼びます。
今まではParrelSyncを使っていたのですが、
Editorを丸ごと複製するためアセットのリロード作業は個別に行わないといけなかったりと少し面倒でした。
Multiplayer Play Modeパッケージを使うと、プレイヤー部分だけが複製されるため非常に楽です。
コードを読む限りUnity MultiplayだとかUnity Gaming Serviceと呼ばれているものとの依存関係はなく
ただプレイヤー部分を複製するだけのようです。
これだけで終わればハッピーだったのですが、
1.3.0-pre.1で発生している、ソースコードを編集してもVirtual Playerに反映されないバグの対処法を紹介します。
致命的すぎて、このために導入を見送っていた人も少なくないはず。自分もその一人でした。
フォーラムには1年前から書き込みがありますが未だに治っていません。
どうやらRiderを使っていると発生するそうなのですが、
JetBrains側でも"Third Party Problem"ということでCloseされてしまっています。
以下のEditorScriptを配置しておくだけでオッケーです。
リフレクションで無理矢理呼び出してるのでバージョンアップで使えなくなる可能性は高いです。
ForceSyncVirtualPlayer.cs
// https://discussions.unity.com/t/multiplayer-playmode-does-not-update-assembly-properly/922976
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
internal class ForceSyncVirtualPlayer : AssetPostprocessor
{
private const string SessionStateKey = "ForceSyncVirtualPlayer.CachedTotalCount";
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload)
{
var totalCount = importedAssets.Length + deletedAssets.Length + movedAssets.Length + movedFromAssetPaths.Length;
if (!didDomainReload)
{
SessionState.SetInt(SessionStateKey, totalCount);
return;
}
var cachedTotalCount = SessionState.GetInt(SessionStateKey, 0);
if (cachedTotalCount == 0) return;
_targetType ??= Type.GetType("Unity.Multiplayer.Playmode.VirtualProjects.Editor.AssetDatabaseCallbacks, Unity.Multiplayer.Playmode.VirtualProjects.Editor");
if (_targetType == null)
{
Debug.LogError("Target type not found.");
return;
}
_eventField ??= _targetType.GetField("OnPostprocessAllAssetsCallback", BindingFlags.Static | BindingFlags.NonPublic);
if (_eventField == null)
{
Debug.LogError("Event field not found.");
return;
}
_eventDelegate = (Delegate) _eventField.GetValue(null);
if (_eventDelegate == null)
{
Debug.LogError("Event delegate not found.");
return;
}
_eventDelegate.DynamicInvoke(true, cachedTotalCount);
SessionState.EraseInt(SessionStateKey);
}
private static Type _targetType;
private static FieldInfo _eventField;
private static Delegate _eventDelegate;
}