【Unity】UniRxでシーン遷移を管理する

# はじめに

何らかのトリガーによりシーンを遷移する処理をUniRxを用いて実装する方法を書きました。

# 実行環境

- [Unity](https://unity.com) 2019 1.6f1 (c) Unity Technologies ApS.

- [UniRx](https://github.com/neuecc/UniRx) 6.2.2 The MIT License (MIT) Copyright (c) 2018 Yoshifumi Kawai.

- [Zenject](https://github.com/modesttree/Zenject) 7.3.1 Copyright (c) 2010-2019 Modest Tree Media Inc.

# 方法

主なクラスは`SceneEntity`と`ChangeSceneUseCase`の２つです。

### SceneEntity

`SceneEntity`の役割はシーンのデータの管理とそのデータの更新です。

`SceneEntity`はデータをReactivePropertyとして持ち、またこのデータを更新するためのメソッドを持ちます。

### ChangeSceneUseCase

`ChangeSceneUseCase`は`SceneEntity`が持っているReactivePropertyを購読し、シーン遷移の具体的な処理を行います。

# コード

### SceneEntity

```C#
using UnityEngine;
using UniRx;

namespace Domain.Entity
{
    public class SceneEntity : ISceneEntity
    {
        public IReactiveProperty<Scene> SceneRP { get; }

        public SceneEntity()
        {
            SceneRP = new ReactiveProperty<Scene>(Scene.TitleScene);
        }

        public void ChangeScene(Scene targetScene)
        {
            SceneRP.Value = targetScene;
        }
    }

    public enum Scene
    {
        TitleScene,
        GameScene
    }

    public interface ISceneEntity
    {
        IReactiveProperty<Scene> SceneRP { get; }
        void ChangeScene(Scene targetScene);
    }
}
```


### ChangeSceneUseCase
```C#

using UnityEngine;
using Zenject;
using Domain.Entity;
using UniRx;
using UnityEngine.SceneManagement;

namespace Domain.UseCase
{
    public class ChangeSceneUseCase : IInitializable, IChangeSceneUseCase
    {
        [Inject] private ISceneEntity sceneEntity;

        void IInitializable.Initialize()
        {
            sceneEntity
                .SceneRP
                .SkipLatestValueOnSubscribe() //初期値のプッシュを無視する
                .Subscribe(scene => LoadScene(scene.ToString()));
        }

        public void LoadScene(string sceneName)
        {
            SceneManager.LoadScene(sceneName);
        }
    }

    public interface IChangeSceneUseCase
    {

    }
}

```

# 説明

### SceneEntity

シーンのデータを管理するために`IReactivePropety<Scene>`型の`SceneRP`を宣言しています。

```C#
public IReactiveProperty<Scene> SceneRP { get; }
```

`Scene`というのはこのような列挙型です。

```C#
public enum Scene
{
     TitleScene,
     GameScene
}
```
この例ではTitleSceneとGameSceneのみですが、他のシーンを追加したりする場合はここに新たな変数を追加します。

また、`SceneEntity`は`SceneRP`を更新するために`ChangeScene`というメソッドを持ちます。

```C#
public void ChangeScene(Scene targetScene)
{
     SceneRP.Value = targetScene;
}
```

遷移先のシーンを引数で指定して`SceneRP`を更新します

`SceneEntity`は次のようなインターフェースを実装し、`ChangeSceneUseCasse`の`sceneEntity`という変数にInjectします。

```C#
public interface ISceneEntity
{
     IReactiveProperty<Scene> SceneRP { get; }
     void ChangeScene(Scene targetScene);
}
```

### ChangeSceneUseCase

Zenjectの`IInitializable`を実装しているので、開始時に`Initialize`メソッドが実行されます。

この`Initialize`メソッド内で`sceneEntity.SceneRP`を`Subscribe`して、
新しいシーンへ遷移する処理を登録します。



# 使い方

うっかり忘れてしまいがちですが、ZenjectのInstaller内でそれぞれのクラスを忘れずにBindします。

```C#
Container.Bind<ISceneEntity>().To<SceneEntity>().AsCached();

Container.BindInterfacesTo<ChangeSceneUseCase>().AsCached();
```

そして、他のクラスで

```C#
[Inject] ISceneEntity sceneEntity;
```
で`SceneEntity`を取得し、遷移したいタイミングで

```C#
sceneEntity.ChangeScene(Scene.GameScene);
```
などとすればシーン遷移できるはずです。

# 備考

今回は`SceneEntity`と`ChangeSceneUseCase`という2つのクラスをわざわざ作って関心の分離を試みましたが、そこまで拘らない場合は1つのクラスにまとめてしまっても良いと思います。