原因と仕組み
Domain Reloadを無効にすると、Playの終了後もstatic変数の値やstaticイベントの購読が残ります。2回目の再生だけカウンターが初期値に戻らない、同じ通知が複数回届くといった症状の候補です。staticを使うこと自体が原因とは限りません。
まず設定を比較する
- 調査用のシーンで、Project SettingsのEditorにあるEnter Play Mode設定を確認します。
- Scene Reloadは有効のまま、Domain Reloadの有効・無効で、同じ操作を複数回Playして比較します。
- どちらでも重複するなら、同じオブジェクトが複数ある、Inspectorとコードの両方で登録した、解除がないなど、通常の購読経路も確認します。
設定名はUnityの版で異なります。以下はUnity 6と2022.3のランタイムスクリプト向けの最小例です。
値とイベントを所有するクラスで初期化する
SessionCounter.csとして保存します。SubsystemRegistrationで値を0に戻し、このクラス自身が宣言したイベントを初期化します。他のクラスやUnityのイベントをまとめて消す例ではありません。
using System;
using UnityEngine;
public static class SessionCounter
{
public static int Value { get; private set; }
public static event Action<int> Changed;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetState()
{
Value = 0;
Changed = null;
}
public static void AddOne()
{
Value++;
Changed?.Invoke(Value);
}
}
購読と解除を対にする
SessionCounterListener.csとして保存し、シーン内の1つのGameObjectへ付けます。UI ButtonのOn Click()には、このComponentのSessionCounterListener.AddOneを1回だけ登録します。OnEnableの追加前に同じ購読を解除し、OnDisableでも解除します。
using UnityEngine;
public class SessionCounterListener : MonoBehaviour
{
private void OnEnable()
{
SessionCounter.Changed -= OnChanged;
SessionCounter.Changed += OnChanged;
}
private void OnDisable()
{
SessionCounter.Changed -= OnChanged;
}
private void OnChanged(int value)
{
Debug.Log($"Counter: {value}", this);
}
public void AddOne()
{
SessionCounter.AddOne();
}
}
修正の確認と、この例の範囲
- 1回目と2回目のPlayで、最初のクリックがCounter: 1になるか確認する。
- 1クリックに対するログが1件か確認する。
- GameObjectの無効・有効を繰り返しても購読が増えないか確認する。
- 対象Playerビルドで、起動時とシーン再読み込み時の動作を別々に確認する。
この初期化は各シーン遷移で自動的に実行されるリセットではありません。Scene Reloadも無効にする場合は、オブジェクトの残存と初期化・購読のタイミングを別途設計します。実行途中でイベントを消すと必要な購読者まで失うため、所有者によるセッション開始時の初期化に限定します。Editor専用の静的状態にはInitializeOnEnterPlayModeなど、用途に合ったEditor APIを使います。
このListenerは通常のランタイムMonoBehaviourを想定しており、ExecuteAlwaysやExecuteInEditModeを付けていません。Edit Modeでも動くコードやScene Reloadを無効にした構成では、Play切替でOnEnable/OnDisableが呼ばれると決めつけず、EditorのPlay Mode状態通知など用途に合う経路で解除と再登録を設計してください。