2
1

More than 1 year has passed since last update.

【Unity】asmdef内クラスからどうにかしてasmdef外クラスのメソッドを呼びたい!

Last updated at Posted at 2022-06-25

asmdef内クラスからasmdef外クラスのメソッドを呼びたい!

という状況に遭遇することがあります。

たとえば、

  • 利用しているサードパーティー製のライブラリがasmdefに対応していない
  • 古めの自社コードがasmdefに対応しておらず、すぐに対応させるのも困難

みたいな状況です。

結論から書きます。

asmdef内クラスからasmdef外クラスのメソッドを 直接呼ぶことは不可能です!

しかし、 間接的にメソッドを呼ぶことはできます!

asmdefがなければ何も気にしなくていい

まず、asmdefがない状況を考えます。

たとえば、ユーザーの身長・体重の値を更新したら、BMIを計算する処理を実装したいとします。

具体的には、以下のように、 UserProfile から BmiUtility を参照したい!という状況です。

public class UserProfile
{
    public double Height { get; private set; }
    public double Weight { get; private set; }
    public double Bmi { get; private set; }

    public UserProfile(double height, double weight)
    {
        Height = height;
        Weight = weight;
    }

    public void SetHeight(double height)
    {
        Height = height;
        Bmi = BmiUtility.CalcBmi(Height, Weight);
    }

    public void SetWeight(double weight)
    {
        Weight = weight;
        Bmi = BmiUtility.CalcBmi(Height, Weight);
    }
}
public static class BmiUtility
{
    public static double CalcBmi(double height, double weight)
    {
        // 体重[kg] ÷ (身長[m])^2
        return weight * 10000 / (height * height);
    }
}

asmdefがなければ、直接参照することができます!

asmdef内クラスからasmdef外クラスのメソッドは呼べない!

しかし、たとえば、

  • UserProfile がasmdef内
  • BmiUtility がasmdef外

の場合、参照エラーが発生してしまいます!

困りました!

asmdef外からasmdef 内を参照するようにしてみる

発想を逆転させてしまいましょう!

asmdef外に中継クラスを作って、asmdef外からasmdef内を参照するようにしてみるんです!

図にするとこんな感じです!

まず、以下のように UserProfileAction を導入しましょう!

using System;

public class UserProfile
{
    public double Height { get; private set; }
    public double Weight { get; private set; }
    public double Bmi { get; private set; }

    public Action OnSetHeight();
    public Action OnSetWeight();

    public UserProfile(double height, double weight)
    {
        Height = height;
        Weight = weight;
    }

    public void SetHeight(double height)
    {
        Height = height;

        // アクションを発火する
        OnSetHeight?.Invoke();
    }

    public void SetWeight(double weight)
    {
        Weight = weight;

        // アクションを発火する
        OnSetWeight?.Invoke();
    }

    public void SetBmi(double bmi)
    {
        Bmi = bmi;
    }
}

次に、 UserBehaviour というクラスをasmdef外に作って、 以下のように UserProfile を参照するようにします。

using UnityEngine;

public class UserBehaviour : MonoBehaviour
{
    public UserProfile User { get; set; }

    private void Awake()
    {
        User.OnSetHeight += CalcBmi;
        User.OnSetWeight += CalcBmi;
    } 

    private void OnDestroy()
    {
        User.OnSetHeight -= CalcBmi;
        User.OnSetWeight -= CalcBmi;
    } 

    private void CalcBmi()
    {
        var bmi = BmiUtility.CalcBmi(User.Height, User.Weight);
        User.SetBmi(bmi);
    }
}

これで、身長・体重が更新されると、 UserBehaviour.CalcBmi() が実行されるようになります!

asmdef内から間接的にasmdef外のメソッドを呼ぶことができました!

public static class BmiUtility
{
    public static double CalcBmi(double height, double weight)
    {
        // 体重[kg] ÷ (身長[m])^2
        return weight * 10000 / (height * height);
    }
}

BmiUtility は、とくに変更する必要はありません。

Action を利用することで、クラスの参照方向を逆転させることができました!

図にまとめると、こんな感じです!

デメリット1: Actionにメソッドを登録するまでは、メソッドを呼べない

すごく便利ですが、デメリットもあります。

一番のデメリットは、Actionにメソッドが登録されるまでは、アクションを発火させても何も起きないということです。

つまり、

User.OnSetHeight += CalcBmi;

が実行されるよりも前に、

OnSetHeight?.Invoke();

が実行されてしまうと、どうしようもないのです。

たとえば、

public UserProfile(double height, double weight)

のコンストラクターが呼ばれた時点では、Actionにメソッドが登録されていないため、コンストラクター内でActionを発火させても、 BmiUtility.CalcBmi を実行することはできません。

クラス設計する際に、このあたりの実行順には、かなり気を使う必要があります。

デメリット2: OnDestroy書くの面倒

今回のサンプルコードだと、 UserBehaviourMonoBehaviour を継承しています。

そこで、

private void Awake()
{
    User.OnSetHeight += CalcBmi;
} 

と書いたなら、

private void OnDestroy()
{
    User.OnSetHeight -= CalcBmi;
} 

も同時に書いた方が安全です。

しかし、書かなくても動くので、書き忘れやすいというデメリットがあります。
(もっとスマートな方法ないかな)

デメリット3: 技術的負債になる可能性がある

もし、リファクタリングがあり、 BmiUtility がasmdef内クラスになった場合、 UserProfile から直接参照できるようになるはずですが、そのことに気づかなければ以下の図のようになります。

この状態でも動作はするので、そのまま放置される可能性があります。
そして長い月日が経つと、中継クラスが追加された経緯を知る人がいなくなり、技術的負債となります。

これが3つ目のデメリットです!

もし、この状態に気づけたら、中継クラスをばっさりと廃止しましょう!

すごくシンプルになりましたね!

さいごに

Actionを使えば、間接的にasmdef内から外のメソッドを呼べるよ!というお話でした!

ご参考になれば幸いです!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1