うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【Unity】MonoBehaviour の メソッドを登録してデバッグしやすくする

あらまし

開発中にデバッグメニューを作成時に機能を追加する度に
どのウィンドウに表示させるか決めて、表示名を決めて等の作業が面倒になる

そのため、フラグだけ建てておけば自動的に登録されていく機構があれば便利である

実装

メソッドに利用できる Attribute を作成し、
Awake時に それらのメソッドを利用できるように登録して管理する

メソッドに利用できる Attribute は以下のようになる
特に情報は必要無いので Attribute を継承しただけである

using System;

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class InvokeMethodAttribute : Attribute
{
}

メソッドの管理を行うのが MethodObserver である
Awake時に Register、 Destroy時に Unset を呼び出してもらう

実行するときは Invoke を呼び出す

※特定のUnityVersionで GetCustomAttributes で指定した CustomAttribute を所持していない場合エラーになるので注意してください

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Object = System.Object;

public static class MethodObserver
{
    private static readonly Dictionary<Object, MethodInfo[]> dic = new Dictionary<Object, MethodInfo[]>();

    /// <summary>
    /// 登録を解除
    /// </summary>
    public static void Unset(Object obj)
    {
        if (!dic.ContainsKey(obj))
            return;

        dic.Remove(obj);
    }
    
    /// <summary>
    /// メソッドを登録
    /// </summary>
    public static void Register(Object obj)
    {
        var findMethods = obj.GetType()
            .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)
            .Where(m => m.GetCustomAttributes<InvokeMethodAttribute>(true).Any()).ToArray();

        if (findMethods.Length <= 0)
            return;
        
        if (dic.ContainsKey(obj))
            return;
        
        dic.Add(obj, findMethods);
    }

    /// <summary>
    /// メソッドを実行
    /// </summary>
    public static void Invoke(Type type, string methodName, params Object[] paramerters)
    {
        var key = dic.Keys.First(o => o.GetType() == type);
        if (key == null)
            return;

        var method = dic[key].First(m => m.Name == methodName);
        method?.Invoke(key, paramerters);
    }
}

メソッドを登録するための汎用的な MonoBehaviour 継承クラスが以下になる

using UnityEngine;

public abstract class MethodBehaviourAbstract : MonoBehaviour
{
    protected virtual void Awake()
    {
        MethodObserver.Register(this);
    }

    protected virtual void OnDestroy()
    {
        MethodObserver.Unset(this);
    }
}

上記を継承したサンプルクラスが以下になる このサンプルクラスであれば、SampleInvoke が Awake 時に登録される

using UnityEngine;

public class SampleMethodMonoBehaviour : MethodBehaviourAbstract
{
    [InvokeMethod]
    private void SampleInvoke()
    {
        Debug.Log("Sample");
    }

    private void Start()
    {
        MethodObserver.Invoke(GetType(), "SampleInvoke");
    }
}