かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Unity でも DI 使ったりしたいし画面もいい感じに作りたい「Zenject & UIWidgets」 その 1

表題の通りです。Zenject を使ったり UIWidgets を使ってみたいと思います。

ここで使うライブラリと環境

環境

  • Windows 10 1909
  • Unity 2019.2.12f1

Zenject

Unity 向けに特化された DI コンテナで、Unity 向けの各種便利機能が追加されています。他の .NET Standard 対応の DI コンテナを Unity で使うことも出来ると思いますが、ビヘイビアー系を DI コンテナに追加したり、シーンを跨いでシングルトンで管理したいものとかまで考えると大変なので、おとなしく Zenject を使うのがいいと思いました。

github.com

UIWidgets

いい UI コンポーネントのアセット無いかなぁと探していたら見つけました。Flutter と同じノリで Unity でコード書けるらしいです。いいね。

github.com

Zenject の導入

Zenject の GitHub のリリースページから最新のバージョンの Zenject のサンプルの入っていないパッケージをダウンロードしてインポートします。

github.com

基本となるシーンの作成

インポートしたら Scripts フォルダーを作って、そこに Assembly Definition を作成して選択します。Assembly Definition References に項目を一つ追加して Zenject を追加しましょう。 シングルトンで管理したいオブジェクトを管理するためのシーンを作ります。base という名前で作って Hierarchy で右クリックして Zenject の SceneContext を作成します。

Scripts の下に BaseInstaller.cs を作って以下のように適当にシングルトンとして登録します。今回はカウンタークラスを作っておきました。

using Zenject;

namespace UnityAppTest
{
    public class BaseInstaller : MonoInstaller
    {
        public override void InstallBindings()
        {
            Container.Bind<Counter>().AsSingle();
        }
    }

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

        public void Increment() => Value++;
    }
}

SceneContext に BaseInstaller.cs をアタッチして、SceneContext の MonoInstallers に対して BaseInstaller を設定します。最後に他のシーンから参照するために Contaract Names の Size を 1 にして base と入れましょう。

それぞれのシーンの作成

では、main という名前のシーンをさk末井して Hierarchy に追加しましょう。これで base と main が Hierarchy に表示されます。main にも SceneContext を作って Parent Contract Names の Size を 1 にして base と入れます。

そして、適当に Text を置いて画面の中央に表示されるようにします。

f:id:okazuki:20191218165727p:plain

MonoBehaviour を一つ作ります。TextUpdater という名前にしました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Zenject;

namespace UnityAppTest.Main
{
    public class TextUpdater : MonoBehaviour
    {
        [Inject]
        public Counter Counter { get; set; }

        private Text _text;

        private void Start()
        {
            _text = GetComponent<Text>();
        }

        private void Update()
        {
            _text.text = $"The counter value is {Counter.Value}";
        }
    }
}

そして、Text に TextUpdater を割り当てます。この時点で実行するとカウンターは 0 のままです。適当に Button を画面において以下のようなスクリプトを書いて

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace UnityAppTest
{
    public class SceneLoader : MonoBehaviour
    {
        public string UnloadSceneName;
        public string LoadSceneName;

        public void Execute() => StartCoroutine(ExecuteImpl());

        private IEnumerator ExecuteImpl()
        {
            yield return SceneManager.LoadSceneAsync(LoadSceneName, LoadSceneMode.Additive);
            yield return SceneManager.UnloadSceneAsync(UnloadSceneName);
        }
    }
}

ボタンに張り付けて UnloadSceneName に main、LoadSceneName に next を設定します。そして、onClick に Execute メソッドを割り当てておきます。

next という名前のシーンを作って、SceneContext を追加します。SceneContext の Parent Contract Names の Size を 1 にして値に base を設定します。 そしてボタンを適当に 2 つ配置します。位置とテキストを調整します。

f:id:okazuki:20191218171950p:plain

Incrementer という MonoBehaviour を作って以下のような感じでカウンターをインクリメントします。

using UnityEngine;
using Zenject;

namespace UnityAppTest.Next
{
    public class Incrementer : MonoBehaviour
    {
        [Inject]
        public Counter Counter { get; set; }

        public void Execute() => Counter.Increment();
    }
}

Increment と書かれたボタンにアタッチして Execute を onClick に割り当てます。Back というボタンには前に作成した SceneLoader を追加して UnloadSceneName に next を、LoadSceneName に main を指定します。

動かしてみよう

base と main が読み込まれた状態で next は unload して実行してみましょう。

f:id:okazuki:20191218173143g:plain

いい感じですね!!ということで次は UIWidgets を導入して画面をそれっぽくしてみます。次の記事でね!

次の記事

blog.okazuki.jp