Unity

MVVM 4 uGUI – デモ

M4u 目次
MVVM 4 uGUI – uGUIにMVVM(Model-View-ViewModel)パターンを導入
MVVM 4 uGUI – デモ
MVVM 4 uGUI を無料化
M4u 〜 あとがき

ここではM4uの使用方法を説明します。

はじめに

まずはuGUIにデータをバインドするための準備です。自分が普段行っているやり方を紹介します。

M4uContextの作成

M4uContextを継承したクラスを作成します。このクラスがルートオブジェクトなります(このクラスのデータをバインドオブジェクトが参照する)。
M4uContextは複数作ることも可能ですが、ルートオブジェクトが増えすぎると分かりづらくなるので、自分は基本この1クラスで管理しています。
MonoBehaviourを使用したい場合はM4uContextの代わりにM4uContextMonoBehaviourをご使用下さい。

App.cs

namespace M4u.Demo
{
    public class App : M4uContext
    {
        public static readonly App Instance = new App();

        private App() { }
    }
}

Demoクラスの作成

シーン内で処理されるDemoクラスを作成し、Appクラスに参照させます。

Demo.cs

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        void Awake()
        {
            App.Instance.Demo = this;
        }
    }
}

App.cs

namespace M4u.Demo
{
    public class App : M4uContext
    {
        public static readonly App Instance = new App();

        public Demo Demo { get; set; }

        private App() { }
    }
}

M4uContextRootの設定

GameObjectを作成し、そこにDemoクラスと「Component/M4u/ContextRoot」を設定します。
M4uContextRootのContextプロパティにAppクラスを設定します。
M4uContextRootは複数作ることも可能です。

以上で、M4uContextRoot(Demo)配下のGameObjectに対して、Appクラスのデータを関連づけさせる準備が整いました。

Demo.cs

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }
    }
}

Hierarchyアイコン

「Tools/M4u/Show Hierarchy Icon」を選択することで、Hierarchy上にバインドアイコンが表示されるようになります。バインドオブジェクトが増えていくと迷子になりがちなので設定しておくと便利です。

バインドフローエディタ

「Tools/M4u/Open BindFlow」を選択することで、現在のバインド状況が確認出来ます。

TextBinding(s)

Textへのバインディング
バインドイメージ)Text.text = string.Format (Format, Path);

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:string
Formatテキストフォーマット。string.Formatと同じ使い方。

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        M4uProperty<string> message = new M4uProperty<string>("");

        public string Message { get { return message.Value; } set { message.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Message = Random.Range (1, 100).ToString();
        }
    }
}

  • スクリプトを上記のように修正
  • uGUIのButtonとTextを追加し、Button.onClickにDemo.OnUpdateを設定
  • Textに「Component/M4u/TextBinding」を追加し、プロパティを動画のように設定

この状態で実行してみると、ボタンをクリックする度にテキストが更新されていることが分かると思います。
データの更新のみでUI(View)の値が自動的に変更されています。

末尾に s が付いているもの(TextBindings等)は複数データのバインド用です。出来ることは変わりません。

ImageBinding

Imageへのバインディング
バインドイメージ)Image.sprite = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:Sprite

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        public Sprite[] SpriteData;

        M4uProperty<Sprite> sprite = new M4uProperty<Sprite>();
        int spriteIdx;

        public Sprite Sprite { get { return sprite.Value; } set { sprite.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Sprite = SpriteData [spriteIdx++ % SpriteData.Length];
        }
    }
}

RawImageBinding

RawImageへのバインディング
バインドイメージ)RawImage.texture = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:Texture

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        public Texture[] TextureData;

        M4uProperty<Texture> texture = new M4uProperty<Texture>();
        int textureIdx;

        public Texture Texture { get { return texture.Value; } set { texture.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Texture = TextureData [textureIdx++ % TextureData.Length];
        }
    }
}

ActiveBinding

GameObjectのアクティブフラグへのバインディング。子要素のGameObjectに対しても適応される。
バインドイメージ)GameObject.SetActive (Path);

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:bool/double/string/enum
CheckTypeBool:bool
Equal:==
Greater:>
Less:<
Empty:空文字
String:文字列
Enum:enum
CheckValueCheckTypeがEqual・Greater・Lessの場合に使用する数値
CheckStringCheckTypeがString・Enumの場合に使用する文字列
Invert判定結果を逆にする場合は true

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        M4uProperty<bool> isActive = new M4uProperty<bool> (true);

        public bool IsActive { get { return isActive.Value; } set { isActive.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            IsActive = !IsActive;
        }
    }
}

EnableBinding

MonoBehaviourの有効フラグへのバインディング。子要素のGameObjectに対しても適応される。
バインドイメージ)MonoBehaviour.enabled = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:bool/double/string/enum
CheckTypeBool:bool
Equal:==
Greater:>
Less:<
Empty:空文字
String:文字列
Enum:enum
CheckValueCheckTypeがEqual・Greater・Lessの場合に使用する数値
CheckStringCheckTypeがString・Enumの場合に使用する文字列
Invert判定結果を逆にする場合は true

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        public enum HeartAbility { Sun, Moon }

        M4uProperty<HeartAbility> ability = new M4uProperty<HeartAbility> ();

        public HeartAbility Ability { get { return ability.Value; } set { ability.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Ability = Ability == HeartAbility.Sun ? HeartAbility.Moon : HeartAbility.Sun;
        }
    }
}

TransformBinding、TransformLocalBinding

Transformへのバインディング。TransformBindingとTransformLocalBindingは設定方法が違うだけ。
バインドイメージ)Transform.localPosition = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:float/Vector3
Type(TransformBinding)Px・Py・Pz:localPosition.x〜z
Rx・Ry・Rz:localRotation.x〜z
Sx・Sy・Sz:localScale.x〜z
Type(TransformLocalBinding)Postion:localPosition
Rotation:localRotation
Scale:localScale

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        M4uProperty<float> x = new M4uProperty<float> ();
        M4uProperty<Vector3> scale = new M4uProperty<Vector3>(Vector3.one);

        public float X { get { return x.Value; } set { x.Value = value; } }
        public Vector3 Scale { get { return scale.Value; } set { scale.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            X = Random.Range (-200f, 200f);
            Scale = new Vector3 (Random.Range (0.3f, 1f), Random.Range (0.3f, 1f), 0);
        }
    }
}

ColorBinding

Graphicのカラーへのバインディング。子要素のGameObjectに対しても適応される。
バインドイメージ)Graphic.color = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:Color

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        M4uProperty<Color> color = new M4uProperty<Color> (Color.white);

        public Color Color { get { return color.Value; } set { color.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Color = new Color (Random.Range (0f, 1f), Random.Range (0f, 1f), Random.Range (0f, 1f));
        }
    }
}

CollectionBinding

Collectionへのバインディング。Pathに設定されている要素数分Dataを生成する。主にScrollViewで使用する。
バインドイメージ)Collection = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:int/Array/ICollection
Data生成するデータ(Prefab)。M4uContextRootが存在し、PathデータがM4uContextを継承したクラスの場合、自動的にM4uContextRoot.Contextに値を設定する。
SavePath
※省略可能
生成されたデータを保存するパス
型:IList(Value:GameObject)/IDictionary(Key:Path、Value:GameObject)
OnChanged
※省略可能
データ変更後に呼ばれるコールバック

using UnityEngine;

namespace M4u.Demo
{
    public class Monster : M4uContext
    {
        M4uProperty<string> name = new M4uProperty<string> ();
        M4uProperty<Texture> texture = new M4uProperty<Texture> ();

        public string Name { get { return name.Value; } set { name.Value = value; } }
        public Texture Texture { get { return texture.Value; } set { texture.Value = value; } }
    }
}
using UnityEngine;
using System.Collections.Generic;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        public Texture MonsterTextureData;

        M4uProperty<List<Monster>> monsters = new M4uProperty<List<Monster>>(new List<Monster>());
        List<GameObject> monsterList = new List<GameObject>();

        public List<Monster> Monsters { get { return monsters.Value; } set { monsters.Value = value; } }
        public List<GameObject> MonsterList { get { return monsterList; } set { monsterList = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Monsters.Clear ();
            int count = Random.Range (0, 6);
            for(int i = 0; i < count; i++)
            {
                var monster = new Monster();
                monster.Name = "Aruka" + Random.Range (1, 100);
                monster.Texture = MonsterTextureData;
                Monsters.Add(monster);
            }
        }

        void OnChangedMonsterList()
        {
            Debug.Log ("MonsterCount = " + MonsterList.Count);
        }
    }
}

EventBinding(s)

UnityEventへのバインディング
バインドイメージ)UnityEvent.AddListener(Path);

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:UnityAction
TypeButtonClick、ToggleValueChanged、SliderValueChanged、ScrollbarValueChanged、DropdownValueChanged、InputFieldEndEdit、ScrollRectValueChanged、EventTrigger
TriggerTypeTypeがEventTriggerの場合のトリガータイプ。UnityEngine.EventSystems.EventTriggerTypeと同じ

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        void OnClickEvent()
        {
            Debug.Log ("OnClickEvent");
        }
    }
}

ToggleBinding

Toggleへのバインディング
バインドイメージ)Toggle.isOn = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:bool/double/string/enum
CheckTypeBool:bool
Equal:==
Greater:>
Less:<
Empty:空文字
String:文字列
Enum:enum
CheckValueCheckTypeがEqual・Greater・Lessの場合に使用する数値
CheckStringCheckTypeがString・Enumの場合に使用する文字列
Invert判定結果を逆にする場合は true

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        M4uProperty<bool> isActive = new M4uProperty<bool> (true);

        public bool IsActive { get { return isActive.Value; } set { isActive.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            IsActive = !IsActive;
        }
    }
}

InputFieldBinding(s)

InputFieldへのバインディング
バインドイメージ)InputField.text = string.Format(Format, Path);

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:string
Formatテキストフォーマット。string.Formatと同じ使い方。

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        M4uProperty<string> message = new M4uProperty<string>("");

        public string Message { get { return message.Value; } set { message.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Message = Random.Range (1, 100).ToString();
        }
    }
}

ScrollbarBinding

Scrollbarへのバインディング
バインドイメージ)Scrollbar.value = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:float

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        M4uProperty<float> progress = new M4uProperty<float>();

        public float Progress { get { return progress.Value; } set { progress.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Progress = Random.Range (0f, 1f);
        }
    }
}

SliderBinding

Sliderへのバインディング
バインドイメージ)Slider.value = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:float

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        M4uProperty<float> progress = new M4uProperty<float>();

        public float Progress { get { return progress.Value; } set { progress.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Progress = Random.Range (0f, 1f);
        }
    }
}

SpecialBinding(s)

スペシャルバインディング。どんなものでもバインド可能なスペシャルなバインド。バインドされているGameObjectのComponentデータへのパスを指定する。
バインドイメージ)TargetPath = Path;

プロパティ解説
PathM4uContextRoot.Context内にあるデータへのパス
型:TargetPathの型
TargetPathターゲットパス。「Component名.プロパティ名」のように記述。

using UnityEngine;

namespace M4u.Demo
{
    public class Demo : MonoBehaviour
    {
        public Sprite[] SpriteData;

        M4uProperty<Sprite> sprite = new M4uProperty<Sprite>();
        int spriteIdx;
        M4uProperty<Vector2> size = new M4uProperty<Vector2>(Vector2.one);

        public Sprite Sprite { get { return sprite.Value; } set { sprite.Value = value; } }
        public Vector2 Size { get { return size.Value; } set { size.Value = value; } }

        void Awake()
        {
            App.Instance.Demo = this;
            GetComponent<M4uContextRoot>().Context = App.Instance;
        }

        public void OnUpdate()
        {
            Sprite = SpriteData [spriteIdx++ % SpriteData.Length];
            Size = new Vector2 (Random.Range (64, 128), Random.Range (64, 128));
        }
    }
}

最後に

現在、M4u 1.2.0を作成中。要望のあったパスを省略できるMasterPath機能と細かい改善を行う予定。
MVVM、みんなもっと使えばいいのになぁ。

M4u 目次
MVVM 4 uGUI – uGUIにMVVM(Model-View-ViewModel)パターンを導入
MVVM 4 uGUI – デモ
MVVM 4 uGUI を無料化
M4u 〜 あとがき
【エビでもわかる】オセロプログラミング
〜オセロを作りながらゲームのプログラムを学ぼう〜
「Unityで初めてゲームを作ってみたい!」

そんな人のためにこの本を作りました。
オセロを一から作りながら実践形式でプログラムを学べる本です。
すでに完成したプロジェクトを説明するのではなく、実際に作りながら説明していきます。
一緒に手を動かしながら、プログラムを覚えていきましょう🌟