読者です 読者をやめる 読者になる 読者になる

RE:ADV

UnityでADVを作るブログ

UnityのアセットMarkUXのドキュメント(アドバンスドな方)を訳した

MarkUXはXMLC#のview-modelでGUIを構築するUnityのアセット。www.markux.com


公式ドキュメントのAnatomy Of Viewを日本語訳したのでメモとして残します。

 

Anatomy of a View

ビューの解剖学

Introduction

このチュートリアルはアドバンストなビューを作るためにあなたたちが知るべきことを見ていく。
通して見終わったとき、あなたはMarkUXのAPIをフルに活用できるようになっているだろう。

Visual Hierarchy

ビューの構築手続きを述べる前に、インポートもしくはプロセスが走ったときにシステムがビューをどのように表現するのか簡単に述べておこう。
ビューのプロセスが走ると、それはview objects(unity GameObjects)に変換され、シーンキャンバスのLayoutRoot以下にビジュアルヒエラルキーとして表示される。

以下のようなXMLがあるとして:

MainMenu.xml
<MainMenu>
<Group Spacing="10px">
<Button Text="Play" />
<Button Text="Options" />
<Button Text="Quit" />
</Group>
</MainMenu>

以下のようなビジュアルヒエラルキーとなる。

Main Camera
Directional Light
Canvas
└LayoutRoot
└MainMenu
└Group
├Button
├Label
└ContentContainer
├Button
├Label
└ContentContainer
└Button
├Label
└ContentContainer
EventSystem

ビューモデル(View-Models)はコンポーネントとしてオブジェクトにaddされる。

View-Model Declaration

Viewクラスを継承するか、そのサブクラスはビューモデルクラスとして宣言される。
このクラスの名前はXML上で参照しているViewの名前となる。

Label.cs
[InternalView]
[RemoveComponent(typeof(UnityEngine.UI.Image))]
[AddComponent(typeof(Text))]
public class Label : View

上のようにビューモデルの宣言時にアトリビュートによって情報を付与することができる。

Attribute/Description
[InternalView]
このアトリビュートが付与されたビューモデルコンポーネントを持つビューは、Mainビューとして使えなくなり、ビュープレゼンターのドロップダウンリスト上から隠される。
[AddComponent(type)]
ビューオブジェクトが作成された時に(type)で指定されたコンポーネントを付与することを示す。
[RemoveComponent(type)]
ビューオブジェクトが作成された時に(type)で指定されたコンポーネントをリムーブすることを示す。
[CreateView(type)]
(type)に基づくほかのビューを動的に作成することを示す。(type)がテンプレートとなる。
そのテンプレートが動的にインスタンス化される。

View Field Declaration

ビューフィールドはビューモデル上のクラスに宣言される。
この名前がViewXMLからフィールドを参照するときに使う名前となる。

Group.cs

[ChangeHandler("UpdateLayouts")]
public Orientation Orientation;

[ChangeHandler("UpdateLayouts")]
public ElementSize Spacing;

[ChangeHandler("UpdateLayout")]
public Alignment ContentAlignment;
public bool ContentAlignmentSet;

ビューモデルクラスの宣言のように、フィールド宣言時にもいくつかのアトリビュートを使うことができる。

Attribute/Description
[NotSetFromXml]
このフィールドがXMLからセットすることを許していないことを示す。
[ChageHandler(name)]
このフィールドがチェンジハンドラを持つことを示す。
そのパラメータの名前はビューモデルにあるチェンジハンドラメソッドのものになる。
[ValueConverter]
デフォルトバリューコンバータがこのフィールドを用いたものに上書きされることを示す。

これらのアトリビュートの効果を知るには、ビューフィールドのViewConvertersとChangeHandlersという二つのコンセプトに注目する必要がある。
まずはビューの改装のために有用であろうチェンジハンドラから見ていこう。

Chage Handlers

チェンジハンドラはビューが変更されたときに呼び出されるビューモデル内のメソッドだ。
スタンダードなビューは三つのチェンジハンドラ──アップデートレイアウト、アップデートレイアウツ、アップデートビヘイバー──を持つことができる。

View.cs

public virtual void UpdateLayout()
public virtual void UpdateLayouts()
public virtual void UpdateBehavior()

UpdateLayout
そのビューのレイアウトが変更されたときに(そのクラス内の)どのフィールドにおいても呼び出すことができる:
Width,Height,Margin,Alignment,Offset,そして、OffsetParent

UpdateLayouts
アレントのどこかでレイアウト変更があった場合でも上記UpdateLayoutが呼び出される。

UpdateBehaviour
Alpha,BackgroundColor,etcの、見た目の変更(ただし、レイアウトの変更でない!)があった場合に呼び出される。

これらのフィールドの変化に対応したメソッドはビューモデルでオーバーライド可能だ。
このチェンジハンドラアトリビュートを使うことによってカスタムフィールドにチェンジハンドラを適用することが可能になる。ビューモデルでのチェンジハンドラの使用例を示す。

CustomView.cs

[ChangeHandler("UpdateLayouts")]
public int MyInt;

public override UpdateLayout()
{
//Called when MyInt cahnges or layout fields such as Width
//int値が変更されたり、横幅等のレイアウトのフィールド値が変更されたときに呼ばれる

//important that you call base.UpdateLayout if
//you don't want to override the default layout behavior
//重要。もしデフォルトレイアウトビヘイバーを上書きしたくない場合は、base.UpdateLayoutを呼び出すこと。
base.UpdateLayout();
}

気をつけるのは、もしアップデートレイアウツ("s"がついている方)を付け加えようとした場合でも、UpdataLayoutの方を上書きするということだ。
なぜなら、UpdateLayoutはUpdateLayoutsによっても引き起こされるからだ。(その場合でもUpdateLayoutsを上書きする必要はない。)デフォルトチェンジハンドラはもちろん使えるし、望むならどんなメソッドでもチェンジハンドラとして用いることができる。

Setting Field Values

フィールド値のセット

チェンジハンドラの設定をするためには、特別なジェネリックメソッドのSetValue()を使う必要がある。

CustomView.cs

SetValue(()=>MyInt,7);//MyInt=7 と同じ

//ChildView.Orientation = Orientation.Horizontal; と同じ
SetValue(()=> ChildView.Orientation, Orientation.Horizontal);

もし、インターナルな値の変更を行いチェンジハンドラによるトリガーを発動したくない場合は、通常行うように値をセットすればいい。

Value Converters

バリューコンバータは、異なったtype間のコンバートを行う。
フィールドバリューはviewXMLからstring型で値をセットされることがある:

Group.xml

<Group Spacing="10px" Orientation="Horizontal" ContentAlilgnment="Top">

string型をviewフィールドの値に変換するために、システムはValueConvertersを使っている。
ValueConvertersはベースクラスにValueConverterを持ったクラス群だ。

ColorValueConverter.cs

public class ColorValueConverter : ValueConverter
{
public ColorValueConverter()
{
_type = typeof(Color);
}

public override ConversionResult Convert(object value, ValueConverterContext context){
//カラー型への変換
}
}

上記のコードはカラー型で構成されたColorValueConverterをつくる。
もしシステムがカラー型のフィールドに出会った場合、自動的に値のコンバージョン・変換にColorValueConverterを用いる。

MarkUX APIは スタンダードバリュー(標準型)に対するバリューコンバージョン(型変換)を用意する。
bool,int,float,ElementSize,Vector3,Sprite,Font,Margin,Orientation,Color,Alignment,etc.
もしあなたが新しいviewのためのフィールド型(:一番多い例はenum(列挙型)だろう。)を設定した場合のみ、そのカスタムバリューに対するコンバーターを用意すればいい。
AlignmentValueConverterがどのように実装されているか、例を示しておく。

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
#endregion

namespace MarkUX.ValueConverters
{
//Value converter for Alignment type.
public class AlignmentValueConverter : ValueConverter
{
#reagin Constructor
//Initializes a new instance of the class.
public AlignmentValueConverter()
{
_type = typeof(Alignment);
}
#endregion

#region Methods
//Value converter for Alignment type.
public override ConversionResult Convert(object value, ValueConverterContext context)
{
//nullだったら
if(value == null)
{
return base.Convert(value,context);
}

//stringだったら
if(value.GetType() == typeof(string))
{
var stringValue = (string)value;
try
{
var convertedValue = Enum.Parse(typeof(Alignment),stringValue,true);
return new ConversionResult(convertedValue);
}
catch(Exception e)
{
return ConversionFailed(value, e);
}
}
return ConversionFailed(value);
}
#endregion
}
}

Veiw References

ビューフィールドはview XMLのビューを参照しているかもしれない。
参照したいXML上のビューにIdをつけるだけでそれを実現することができる。

CustomView.xml
<CustomView>
<Group Id="MyGroup" Spacing="10px">
<Button Id="PlayButton" Text="Play" />
<Button Text="Options" />
<Button Text="Quit" />
</Group>
</CustomView>

参照したいviewと同じ名前、同じ型のクラスフィールドを設定しよう。

CustomView.cs
public class CustomView : View
{
public Group MyGroup;
public Button PlayButton;
}

また同じ方法でGameObjectの型を変更することで、view objectを参照することもできるだろう。

Set-fields

Set-fieldsは値がset済みかどうか指し示すことに使われる特別なbooleanフィールドだ。
プログラマティカリに、もしくはXMLで)
値をセットした(はずの時に)正しいロジックでプログラムが動いているか確かめるのに有用だ。
Set-fieldはあなたがトラック(追跡)したいと思うフィールド名のポストフィックス(語尾)にSetをつけて使う。

Group.cs
[ChangeHandler("UpdateLayout")]
public Alignment ContentAlignment;
public bool ContentAlignmentSet;

このときフィールドContentAlignmentSetは、ContentAlignmentに値がセットされていたときのみTrueを返す。

View Action Declaration

View Actions は View-models に追加のロジックをバインドするときに使う。
ビューアクションはクラス内でViewAction型のフィールドとして宣言される。
このクラスフィールドは、参照するXML内のアクションと同名にする。

CostomView.cs
public ViewAction MyCustomAction;

XMLで参照されるアクションを定義しよう。

AnotherView.xml
<AnothorView>
<CustomView MyCustomAction="MyActionHandler" />
</AnothorView>

このアクションをトリガーするためにメソッドを呼び出す。

CustomView.cs
MyCustomAction.Trigger();
MyCustomAction.Trigger(eventData);//もしなんらかのイベントデータをハンドラに渡したい場合

View Action Handlers

ビューアクションハンドラはアクションがトリガされたときに呼び出されるメソッドだ。
アクションハンドラは呼び出されたときに用いる追加パラメータを持つことができる。

CustomView.cs
public void MyActionHandler(View parent, View source, ActionData actionData)
public void MyActionHandler(View parent, View source, BaseEventData eventData)
public void MyActionHandler(GameObject parent, GameObject source)

parent:
アクションをハンドリングする親のビューもしくはゲームオブジェクト
source:
そのビューアクションをトリガーするビューもしくはゲームオブジェクト
eventData:
ベースイベントデータを継承したイベントシステムのイベントデータ
actionData:
アクションデータクラスを継承したカスタムアクションデータ


ボタンクリックハンドラは以下のようになる

CustomView.cs
public void MyButtonClickHandler(PointerEventData eventData, Button source)

Event-System Actions

以下のような名前に設定したアクションを、ユニティのイベントシステムからトリガーさせることができる

BeginDrag
Cancel
Deselect
Drag
Drop
EndDrag
InitializePotentialDrag
Move
Click
PointerClick
MouseClick
PointerDown
MouseDown
PointerEnter
MouseEnter
PointerExit
MouseExit
PointerUp
MouseUp
Scroll
Select
Submit
UpdateSelected

E.g.(例えば)
あなたがクリックアクションをビューに追加したければ、単にClickとアクションの名前を書くだけで自動的にユーザーがクリックしたときにトリガーされる。

Content Views

もし独自のViewが必要であれば、コンテントとしてContentViewを継承するだけでいい。

MyCustomContentView.cs
public class MyCustomContentView : ContentView

ビューXML上でとビューを指定してそれをコントロールすることができる。

MyCustomContentView.xml
<MyCustomContentView>
<Region>
<ContentContainer IncludeContainerInContent="False" />
</Region>
</MyCustomContentView>

どんなコンテントでもコンテントコンテナをチルドレンとしてビューに追加することができる。
もしコンテナそれ自体を追加分に含めたくない場合は、IncludeContainerInContentフィールドをFalseにセットすればいい。

Embedded XML

気づいたかもしれないが、どのスタンダードなビューもXMLファイルと分かれていない。
XMLがview-modelに組み込まれているのがその理由だ。
これはビューのディストリビュートや別ファイルでビューを持ちたくないときに役立つかもしれない。
(どんな方法でもビューのシェアをしてもらえるとありがたい。)
XMLの埋め込みには単にGetEmbeddedXML()をオーバーライドすればいい。

Frame.cs
public override string GetEmbeddedXml()
{
return @"<Frame ResizeToContent=""True"" ContentMargin=""0"">
<ContentContainer Id=""Content"" Margin=""{ContentMargin}""
ResizeToContent=""{ResizeToContent}"" />
</Frame>";
}

すでにビューのコンポーネント、主要な構成要素については解説した。
アドバンスドビュー作成のための情報はとりあえず十分だろう。

コメント、質問、指摘があったらこちらまでhttps://www.reddit.com/r/markuxdev/