2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

農工大Advent Calendar 2024

Day 6

C#のイベントとR3で綺麗なコードを書きたい

Last updated at Posted at 2024-12-06

農工大アドカレ6日目、当日生成記事でございます!

内容は薄め、C#初心者向けだよ!

イベントとは

C# でイベントはこんな感じのやつだよ!

using System;

public class Example
{
    public event Action OnEventTriggered;

    public void TriggerEvent()
    {
        OnEventTriggered?.Invoke();
    }
}

OnEventTriggered に処理を登録しておくと、Invoke() でイベント発火されたときに処理を実行できる!あとでもうちょっとちゃんと説明するよ!

イベントを使うと何が嬉しいのか

一言でいうと、コードが綺麗になるよ!

イベントを使わない場合

ゲームの中で状態を遷移させたいことがあるよね?例えば、待機状態からプレイ状態に変えたいとするよ。これに伴ってメニューのUIも変化するとしよう。こんな風にステートが定義されている。

public enum GameState
{
  Waiting,
  Playing,
}

GameStateManager クラスからステートを変えられるようにしよう。

using System;

public class GameStateManager
{
    private MenuPresenter menuPresenter;

    public void ChangeGameState(GameState gameState)
    {
        switch (gameState)
        {
            case GameState.Waiting:
                break;
            case GameState.Playing:
                // プレイ状態になるときの処理
                menu.toPlaying();
                break;
        }
    }
}

一見問題なさそうだけど、GameStateManagerMenuPresenter のメソッドを直接呼び出しているよね?これはよくないよ!なぜなら、

  • メニュー以外にも UI がある場合、GameStateManager はどんどん肥大化していく
  • このままだと、MenuPresenter が変わったら GameStateManager も変えないといけない
  • これは 「GameStateManagerMenuPresenterに依存している状態」だよ

イベントを使う場合

まずは GameStateManagerOnGameStateChanged というイベントを追加するよ。ステートを変更するときはこのイベントを発火するだけ!

using System;

public class GameStateManager
{
    public event Action<GameState> OnGameStateChanged;

    public void ChangeGameState(GameState gameState)
    {
        OnGameStateChanged?.Invoke(gameState);
    }
}

MenuPresenter はこのイベントをサブスクライブするよ!上の OnGameStateChanged.Invoke(gameState) が呼ばれると、
OnGameStateChanged に登録されているメソッドが呼ばれるよ!+= で登録、-= で解除するよ!-= を忘れるとメモリリークする可能性があるから気をつけてね!

using System;

public class MenuPresenter: MonoBehaviour
{
    private GameStateManager gameStateManager; // DI などで注入すると良い(今回は省略)

    private void Start()
    {
        // シーン開始時にイベントに登録
        gameStateManager.OnGameStateChanged += OnGameStateChanged;
    }

    private void OnDestroy()
    {
        // シーン終了時にイベントから解除
        gameStateManager.OnGameStateChanged -= OnGameStateChanged;
    }

    public void toPlaying()
    {
        // プレイ状態になるときのUI処理。
    }

    private void OnGameStateChanged(GameState gameState)
    {
        switch (gameState)
        {
            case GameState.Waiting:
                break;
            case GameState.Playing:
                toPlaying();
                break;
        }
    }
}

これで、GameStateManagerMenuPresenter に依存していないよ!だから、

  • MenuPresenter が変わっても、GameStateManager は変更しなくていい
  • メニュー以外の UI があっても、GameStateManager は変更しなくていいし、肥大化しない

ちなみに、IGameStateManager というインターフェースを作ってあげると、MenuPresenterGameStateManager の依存関係をさらに緩和できるよ!

using System;

public interface IGameStateManager
{
    event Action<GameState> OnGameStateChanged { get; }
}

R3 を用いる

R3はC#でリアクティブプログラミングを行うためのライブラリだよ!R3を使うと、イベントを使ったコードをさらに綺麗に書くことができるよ!

上記の例を R3 を使って書くとこんな風に書けるよ!

using System;
using R3;

public class MenuPresenter: MonoBehaviour
{
    private GameStateManager gameStateManager; // DI などで注入すると良い(今回は省略)

    private void Start()
    {
        Observable.FromEvent<GameState>(h => gameStateManager.OnGameStateChanged += h, h => gameStateManager.OnGameStateChanged -= h)
            .Where(gameState => gameState == GameState.Playing)
            .Subscribe(_ => toPlaying());
            .AddTo(this); // このコンポーネントが破棄されたら購読を解除する
    }

    public void toPlaying()
    {
        // プレイ状態になるときのUI処理。
    }
}

なんかスッキリしたね!Where でフィルタリング、Subscribe で購読、AddTo で購読解除を自動化しているよ!OnDestroy での購読解除を忘れる心配もなくなった!

R3は普通のイベントだけでなく、Unity の Update やインターバルなど色々なものをリアクティブに扱うことができるよ!ぜひ使ってみてね!

まとめ

自信ないので何か間違ったことを言っていたら教えてください!

2
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?