Closed4

Unityにおける疎結合な実装

0y00y0

同シーン内の他オブジェクトにアタッチされているスクリプトの変数を取得

Unity Events / C# Events

Unity EventsやC#のイベントを使用して、イベントが発生したときにデータを渡すことができます

送信側スクリプト

using UnityEngine;
using UnityEngine.Events;

public class SenderScript : MonoBehaviour
{
    public UnityEvent<int> onValueChanged;

    private int value;

    public void SetValue(int newValue)
    {
        value = newValue;
        onValueChanged?.Invoke(value);
    }
}

受信側スクリプト

using UnityEngine;

public class ReceiverScript : MonoBehaviour
{
    public SenderScript sender;

    void Start()
    {
        sender.onValueChanged.AddListener(OnValueChanged);
    }

    void OnValueChanged(int newValue)
    {
        // ここでnewValueを使用する
    }
}

ScriptableObjects

ScriptableObjectを使用して、データを共有することができます

DataContainer(ScriptableObject)

using UnityEngine;

[CreateAssetMenu]
public class DataContainer : ScriptableObject
{
    public int value;
}

送信側スクリプト

using UnityEngine;

public class SenderScript : MonoBehaviour
{
    public DataContainer dataContainer;

    public void SetValue(int newValue)
    {
        dataContainer.value = newValue;
    }
}

受信側スクリプト

using UnityEngine;

public class ReceiverScript : MonoBehaviour
{
    public DataContainer dataContainer;

    void Update()
    {
        // dataContainer.valueを使用する
    }
}

インターフェース

インターフェースを使用して、具体的なクラスに依存しない方法でデータを取得することができます

インターフェース

public interface IValueProvider
{
    int GetValue();
}

送信側スクリプト

using UnityEngine;

public class SenderScript : MonoBehaviour, IValueProvider
{
    private int value;

    public int GetValue()
    {
        return value;
    }

    public void SetValue(int newValue)
    {
        value = newValue;
    }
}

受信側スクリプト

using UnityEngine;

public class ReceiverScript : MonoBehaviour
{
    public GameObject senderObject;

    void Start()
    {
        IValueProvider valueProvider = senderObject.GetComponent<IValueProvider>();
        if (valueProvider != null)
        {
            int value = valueProvider.GetValue();
            // ここでvalueを使用する
        }
    }
}

UniRx

UniRx(Unity Reactive Extensions)は、リアクティブプログラミングをUnityに導入するライブラリです。データの変更を監視して、変更があった場合に自動的に処理を行うことができます。

送信側スクリプト

using UniRx;

public class SenderScript : MonoBehaviour
{
    public ReactiveProperty<int> Value = new ReactiveProperty<int>(0);

    public void SetValue(int newValue)
    {
        Value.Value = newValue;
    }
}

受信側スクリプト

using UniRx;

public class ReceiverScript : MonoBehaviour
{
    public SenderScript sender;

    void Start()
    {
        sender.Value.Subscribe(newValue =>
        {
            // ここでnewValueを使用する
        }).AddTo(this);
    }
}

Extenject

Extenject(旧Zenject)は、依存性注入(DI)をUnityに導入するためのフレームワークです。これを使うと、コンポーネント間の依存関係を疎結合に保つことができます。

※この例では、MyInstallerスクリプトがSenderScriptの依存関係をReceiverScriptに注入しています。AsSingle()メソッドによって、SenderScriptはシングルトンとして扱われます。

送信側スクリプト

public class SenderScript : MonoBehaviour
{
    private int value = 0;

    public int GetValue()
    {
        return value;
    }

    public void SetValue(int newValue)
    {
        value = newValue;
    }
}

受信側スクリプト

using Zenject;

public class ReceiverScript : MonoBehaviour
{
    private SenderScript senderScript;

    [Inject]
    public void Construct(SenderScript senderScript)
    {
        this.senderScript = senderScript;
    }

    void Start()
    {
        int receivedValue = senderScript.GetValue();
        // ここでreceivedValueを使用
    }
}

Installer(依存関係を定義するスクリプト)

using Zenject;

public class MyInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<SenderScript>().FromComponentInHierarchy().AsSingle();
    }
}
0y00y0

MVPパターン

MVP(Model-View-Presenter)は、UIロジックを整理し、テスト可能で再利用可能なコードを書くためのデザインパターンです。

Model

データとビジネスロジックを扱います

public class DataModel
{
    public int Value { get; private set; }

    public void SetValue(int value)
    {
        Value = value;
    }
}

View

UIとユーザー入力を扱います

public interface IDataView
{
    void UpdateValue(int value);
}

Presenter

ModelとViewを繋ぐ役割を果たします

public class DataPresenter
{
    private DataModel model;
    private IDataView view;

    public DataPresenter(DataModel model, IDataView view)
    {
        this.model = model;
        this.view = view;
    }

    public void OnValueChanged(int newValue)
    {
        model.SetValue(newValue);
        view.UpdateValue(model.Value);
    }
}
0y00y0

MVPパターンに基づくExtenjectとUniRxの利用

MVPの役割を担うコード

  • Model: DataModel
  • View: ReceiverScript(IDataViewインターフェースを実装)
  • Presenter: DataPresenter

それ以外の役割を担うコード

  • 外部からModelにデータを設定するアクター: SenderScript

Model(DataModel)

using UniRx;

public class DataModel
{
    public ReactiveProperty<int> Value = new ReactiveProperty<int>(0);

    public void SetValue(int value)
    {
        Value.Value = value;
    }
}

View(IDataView Interface & ReceiverScript)

public interface IDataView
{
    void UpdateValue(int value);
}

public class ReceiverScript : MonoBehaviour, IDataView
{
    public void UpdateValue(int value)
    {
        // ここで取得した値を何らかの形で表示する
    }
}

Presenter(DataPresenter)

using UniRx;
using Zenject;

public class DataPresenter
{
    private DataModel model;
    private IDataView view;

    [Inject]
    public void Construct(DataModel model, IDataView view)
    {
        this.model = model;
        this.view = view;

        this.model.Value.Subscribe(newValue =>
        {
            view.UpdateValue(newValue);
        });
    }
}

Installer(MyInstaller)

using Zenject;

public class MyInstaller : MonoInstaller
{
    public ReceiverScript receiverScript;
    public SenderScript senderScript;

    public override void InstallBindings()
    {
        Container.Bind<DataModel>().AsSingle().NonLazy();
        Container.Bind<IDataView>().FromInstance(receiverScript).AsSingle();
        Container.Bind<SenderScript>().FromInstance(senderScript).AsSingle();
        Container.Bind<DataPresenter>().AsSingle().NonLazy();
    }
}

送信側スクリプト(SenderScript)

using Zenject;

public class SenderScript : MonoBehaviour
{
    private DataModel dataModel;

    [Inject]
    public void Construct(DataModel dataModel)
    {
        this.dataModel = dataModel;
    }

    public void SomeMethod()
    {
        // 何らかの操作で値を変更する
        dataModel.SetValue(5);
    }
}

ReceiverScriptがViewの役割を果たし、DataModelがModelの役割を果たしています。SenderScriptはある種のアクターまたはコントローラとして、Model(DataModel)にデータを設定する役割を担っています。
SenderScriptはMVPのどのコンポーネントにも完全には当てはまりませんが、システム全体の動作には必要です。

※今回のシナリオでは、MVPパターンの各コンポーネントと「送信側」や「受信側」という概念が完全に一致するわけではありません。

このスクラップは5ヶ月前にクローズされました