見出し画像

UniRx: Unityでリアクティブプログラミングをマスターする

Photo by Mohammad Rahmani on Unsplash

1. UniRxライブラリの概要

UniRxは、Unity向けのリアクティブ拡張ライブラリです。Rx(Reactive Extensions)のアイデアをUnityに適用し、ゲーム開発者がデータストリームとイベント処理をより効率的に管理できるように支援します。これにより、非同期タスクとイベントベースのプログラミングが簡素化され、コードの可読性とメンテナンス性が大幅に向上します。


2. UniRxのReactivePropertyとReactiveCommandの概念

ReactiveProperty
ReactivePropertyは、UniRxが提供する特別なタイプで、データの変化を検出して反応するプロパティです。これはObservableを拡張したもので、値の変更をリアルタイムで検出し、サブスクライバーに通知する役割を果たします。

  1. ReactivePropertyの定義: ReactivePropertyが作成され、初期値が設定されます。

  2. サブスクライバーの登録: ReactivePropertyの値の変化を検出するサブスクライバーが登録されます。

  3. 状態の変化: ReactivePropertyの値が変更されます。

  4. サブスクライバーへの通知: 変更が検出されると、登録されたサブスクライバーに通知が送信されます。

ReactiveCommand
ReactiveCommandは、UniRxのタイプで、コマンドの実行とその結果を管理するために使用されます。主にボタンクリックなどのユーザー入力を処理するために使用され、コマンドの実行可能状態と結果をObservableとして管理できます。

  1. ReactiveCommandの定義: ReactiveCommandが作成されます。

  2. 実行条件の定義: コマンドが実行される条件が設定されます。

  3. サブスクライバーの登録: コマンド実行の結果を処理するサブスクライバーが登録されます。

  4. コマンドの実行: 特定の条件が満たされるとコマンドが実行されます。

  5. サブスクライバーへの通知: コマンド実行の結果がサブスクライバーに送信されます。


3. ReactivePropertyの過剰使用のデメリットとReactiveCommandの代替利点

ReactivePropertyの過剰使用のデメリット

  • メモリリーク: ReactivePropertyは、サブスクライブメカニズムを通じて値の変化を検出し、反応します。しかし、サブスクライブを解除しないとメモリリークが発生する可能性があります。特に、Unityではゲームオブジェクトが破壊される際にサブスクライブを明示的に解除しないとメモリリークの原因になります。

  • 複雑なデバッグ: ReactivePropertyを通じて状態変化を管理すると、複数のReactivePropertyが互いに影響を与えることがあります。これにより、どこで状態変化が発生したのかを追跡するのが難しくなり、複雑なデバッグ作業が必要になります。特に、さまざまな状態が絡み合っている場合、デバッグがさらに難しくなります。

  • パフォーマンスの低下: ReactivePropertyは値が変更されるたびにすべてのサブスクライバーに通知を送信するため、頻繁な状態変化が発生する場合、パフォーマンスの低下が発生する可能性があります。特に、大規模なゲームプロジェクトでは、数百のReactivePropertyが同時に動作する可能性があり、パフォーマンスの問題が深刻になることがあります。

ReactiveCommandの代替利点

  • コマンド中心の設計: ReactiveCommandはコマンド中心で設計されており、特定のイベントを処理します。この構造により、ロジックが明確に分離されているため、メンテナンス性が向上します。たとえば、ボタンクリックイベントなどの明確なユーザーアクションに反応する場合に有効です。

  • 条件付き実行: ReactiveCommandは、特定の条件が満たされた場合にのみ実行されるように設計できます。たとえば、プレイヤーが特定のアイテムを所持している場合にのみ攻撃コマンドを実行するようにできます。これは不要なコマンド実行を防ぎ、パフォーマンスの最適化に役立ちます。

  • コードの可読性: コマンド中心のReactiveCommandはコードの可読性を向上させます。イベント処理ロジックがコマンド単位で分離されているため、コラボレーション時に各コマンドの役割と動作を簡単に理解できます。


4. ReactivePropertyとReactiveCommandの活用例とサンプルコード

ReactiveCommandを使用せずにReactivePropertyのみを使用した例

using UnityEngine;
using UnityEngine.UI;
using UniRx;

public class ButtonHandler : MonoBehaviour
{
    [SerializeField] private Button button;
    
    public ReactiveProperty<bool> IsButtonClicked { get; private set; } = new();

    private void Start()
    {
        // ボタンクリック時にReactivePropertyの値を変更
        button
            .OnClickAsObservable()
            .Subscribe(delegate { OnClickButton(); })
            .AddTo(this);

        // 値の変化に反応
        IsButtonClicked
            .AsObservable()
            .Subscribe(IsButtonClickedHandler)
            .AddTo(this);
    }

    private void OnClickButton()
    {
        IsButtonClicked.Value = true;
    }

    private void IsButtonClickedHandler(bool isButtonClicked)
    {
        if (isButtonClicked)
        {
            Debug.Log("Button Clicked!");
            IsButtonClicked.Value = false; // 状態をリセット
        }
    }
}

ReactivePropertyとReactiveCommandを組み合わせた例

using UnityEngine;
using UnityEngine.UI;
using UniRx;

public class ButtonHandler : MonoBehaviour
{
    [SerializeField] private Button button;

    public ReactiveCommand OnClickButtonCommand { get; private set; } = new();
    public ReactiveProperty<bool> IsButtonEnabled { get; private set; } = new();

    private void Start()
    {
        // ボタンクリック時にコマンドを実行
        button
            .OnClickAsObservable()
            .Subscribe(delegate { OnClickButton(); })
            .AddTo(this);

        // コマンド実行時に反応
        OnClickButtonCommand
            .Subscribe(delegate { OnClickButtonExecuted(); })
            .AddTo(this);

        // ReactivePropertyとボタンの相互作用
        IsButtonEnabled
            .AsObservable()
            .Subscribe(IsButtonEnabledHandler)
            .AddTo(this);
    }

    private void OnClickButton()
    {
        OnClickButtonCommand.Execute();
    }

    private void OnClickButtonExecuted()
    {
        Debug.Log("ButtonHandler.OnClickButtonExecuted(): Entered");
        IsButtonEnabled.Value = !IsButtonEnabled.Value; // 状態値をトグル
    }

    private void IsButtonEnabledHandler(bool isEnabled)
    {
        button.interactable = isEnabled;
    }
}

5. 結論

UniRxを活用すると、Unityでのリアクティブプログラミングが非常に簡単になります。ReactivePropertyとReactiveCommandを適切に使用することで、コードの可読性とメンテナンス性を向上させ、効率的なデータ処理が可能になります。

この記事が気に入ったらサポートをしてみませんか?