RE:ADV

UnityでADVを作るブログ

MarkLightの使い方を再学習するときに参照するまとめ

MarkLightとは?

UnityのUIをXUMLと呼ばれるマークアップLで記述できるアセット。
20161218現在 20$
www.marklightforunity.com


去年はMarkUXという名前でした。
UniRxとの繋ぎこみの話。
ykimisaki.hatenablog.jp


Documentation->Tutorialsオーバービュー

Examples and How To'sをちらっと
パラメータをエディタから探したいので、インテリセンスほしくなりますよね。

How to Get Intellisense in XUML

VisualStudioのスキーマフォルダにプロジェクト内にある.xsdファイルのアドレスを書き込む。
管理者権限でテキストエディタを開いて編集。

C:\Program Files (x86)\Microsoft Visual Studio 14.0\Xml\Schemas

<Schema targetNamespace="MarkLight" href="C://Assets/MarkLight/Views/Schemas/MarkLight.xsd"/>
を書き込む。
つくったViewのxmlのルートを
<YourView xmlns="MarkLight">
こんな感じで


Getting Started

UIを作成する流れ
Sceneに空のGameObjectを作成->View Presenterをアタッチ
Viewファイル作成->using MarkLight.Views.UI して UIViewを継承
ViewModel(Viewと同名.cs)作成->
ViewのルートになるViewを作成->
ViewModel作成->using MarkLight して Viewを継承
View Presenter のインスペクターから ルートのViewを参照
スタイルをあてる

以下見出し的に紹介
View Action : イベントを指定したタグと結びつけられた名前のメソッドを呼び出す機能
Layout : タグで切り出されたコンテンツを並べてレイアウトする機能
ViewSwitcher : タグで切り出されたコンテンツを入れ替える機能
データバインディング : フィールドの値との TwoWay バインディングサポート
Animations : View上で名前で紐づけるアニメーション
SetValue() : UIの値を指定して変化させる
DependencyFields : SetValue()のエイリアス記法


View Models - Bringing Views to Life

クラス名<-->タグ名
フィールド名<-->パラメータ で紐づけ

SetValueメソッドで値を変更
型名の前に_をつけて宣言->ディペンダンシーフィールド
.Value で内部の値にアクセス(オブザーバーが効いてノティファイする)
.DirectValue で内部の値にアクセス(オブザーバーがスルーしてノティファイしない)

UIの子要素を宣言<-->タグ名
子要素UIの名前<-->Idパラメータ で紐づけ

もしくは <タグ名:クラス名 UI.フィールド="値"> の記法でダイレクト参照可

ViewAction型 UnityEvent(or カスタムメソッド名)
<クラス名 ViewActionの名前="値"> で紐づけ
メソッド名.Trigger()
メソッド名.Trigger(data) でインボーク

Textコンポーネントに紐づいた内部stringをディペンダンシーフィールドで表現
[Mapto("Textコンポーネントの名前.Text")]
public _string アクセス用の名前;
こう宣言するとUI.Text.Textにアクセス用の名前.Valueもしくはアクセス用の名前="値"でアクセスできる。

ChangeHandlerアトリビュートでフィールドが変更されたら呼び出すメソッドを定義できる。
MapToアトリビュートの第二パラメータで設定してもよい。
値のイニシャライズ専用の記法・メソッドがあるよ


データバインディング

記法は
ターゲットフィールド="{ソースフィールド}"
ソースフィールドをSetValueメソッド等で入れ替えると、自動的にターゲットフィールドが書き換わる。

ソースフィールドをディペンダンシーフィールドで定義すると(_stringのように型に_をつけて宣言)フィールド.Value=値 のように代入でSetValueと同じことができる
ネストしたパスに対して . でアクセスできる
フォーマットストリングバインディングやマルチフィールドバインディングをサポート
"{=ソースフィールド}"でワンウェイバインディング
"{#ソースフィールド}"でローカルバインディング

ListItemバインディング

<BindingExample>
  <List Items="{HighScores}">
    <ListItem IsTemplate="True" Text="{#Item.Score}" />
  </List>
</BindingExample>

この例でListは、 ローカルバインディングされたアイテムのScoreフィールドを持つ ListItem を複数個保持する HighScoresオブジェクトをバインドしている。

ブーリアンのフィールドとバインディングした場合
IsChecked="{!ShieldActive}"
と値を反転して保持することができる

Resourceバインディング
ディクショナリーに対して、
"{@ディクショナリ名.キー}"もしくは"{@キー}"でバインディングできる

最後にバインディングチートシートがついてます。
こんな例も

<Label Text="{!=#IsActive}" />

Negated One-way Local!


テーマとスタイルの作成

Themaタグ内で名前を指定してつくる
Thema内でstyleやidを指定しておくと使うときに適用された状態で呼び出せる
BasedOn=""でスタイルの上書きができる
見た目だけじゃなくふるまいを設定することもできる
テーマをひとつのシーン・ビューにいくつも読み込むことができる
Style="*"と書くと親のスタイルをインヘリト(継承)する


Working with List Data

List,DataGrid,ComboBox,TabPanelはリストに関係している。ひとまず基礎的なリストについて
Staticなリストはタグで簡単に作れる
Dynamicなリストは...
まず以下のモデルクラスを考えてみる
Highscore.cs

public class Highscore
{
    public string Player;
    public int Score;
}

自動変更通知リストとしてObservableListを使う

上のモデルを含んだリストを見てみよう
DynamicListExample.cs

public class DynamicListExample : UIView
{
	public ObservableList<Highscore> Highscores;

	public override void Initialize()
	{
		Highscores = new ObservableList<Highscore>();
		Highscores.Add(new Highscore { Player = "PlayerA", Score = 100 });
		Highscores.Add(new Highscore { Player = "PlayerB", Score = 750 });
		Highscores.Add(new Highscore { Player = "PlayerC", Score = 9000 });
		Highscores.Add(new Highscore { Player = "PlayerD", Score = 9000 });
	}
}

Viewにバインドする
DynamicListExample.xml

<DynamicListExample>
	<List Items="{Highscores}" Width="200">
		<ListItem IsTemplate="True" Text="{#Item.Player} {#Item.Score}" />
	</List>
</DynamicListExample>

三つの重要なポイントがある

  • <List Items="{Highscores}" /> オブザーバブルリストとバインドしたリストを宣言
  • <ListItem IsTemplate="True" ... /> テンプレートを使うことを宣言
  • <ListItem IsTemplate="True" Text="{#Item.Player} {#Item.Score}" />  最後にitem template とItemDataをバインドする

見た目を整える
DynamicListExample.xml

<DynamicListExample>
	<List Items="{Highscores}" width="250">
		<ListItem IsTemplate="True">
			<Region Margin="10">
				<Label Text="{#Item.Player}" Alignment="Left" FontColor="Black" FontSize="22" AdjustToText="Width"></Label>
				<Label Text="{#Item.Score}" Alignment="Right" FontColor="Black" AdjustToText="Widht"></Label>
			</Region>
		</ListItem>
	</List>
</DynamicListExample>

xmlを書くときにEMMETを使ったので備忘
上のxmlをつくるときはCustom attribute の記法を使う

td[rowspan=2 colspan=3 title]
<td rowspan="2" colspan="3" title=""></td>
DynamicListExample>List[Items={Highscores} width=250]>ListItem[IsTemplate=True]>Region[Margin=10]>Label[Text={#Item.Player} Alignment=Left FontColor=Black FontSize=22 AdjustToText=Width]+Label[Text={#Item.Score} Alignment=Right FontColor=Black AdjustToText=Widht]

以上を打ち込んで Ctrl + E(VScodeの場合はTAB)

itemの追加と削除

//インデックスによる削除
Highscores.RemoveAt(0);
//リファレンスによる削除
var item = Highscores.Find(x => x.Player == "PlayerB");
Highscores.Remove(item);
//itemの追加
Highscores.Add(new Highscore { Player = "Player", Score = 0 });
//インデックスによる追加
Highscores.Insert(0, new Highscore { Player = "Player", Score = 0 });

itemのアップデート(変更通知)
例えば、最高得点が更新されました、といったシナリオ

//Update Items
public void UpdateScores()
{
	//scenario#1 - アイテムによってプレイヤー名を更新する
	var item = Highscores[0];
	item.Player = "New Name";
	Highscores.ItemModified(item, "Player");

	//senario#4 - すべてのリストアイテムのすべてのフィールドを更新
	Highscores.ItemModified();
}

ひとまず備忘録として残します

【Unity】RectTransformの外枠に沿って線を引く!【uGUI】

Vectrosityという線を引くアセットを購入しました。
magicbullet.hatenablog.jp
befool.co.jp

シルバー事件リマスター版が出たのを機にプレイ動画を一気見したせいで、フィルムウィンドウと呼ばれていたあのシステムが気になりだしたのです。
このゲームで<ウィンドウ>と呼ばれるものは線で演出されています。動きや、トランジションも線で視線を誘導します。
今見返してみると同期・非同期が計算されて配置されていて、作劇法・ノベルゲーのシステムとして興味深いのはもちろん、プログラムの題材としてもおもしろいと感じました。

さて、線を引けるようになったのはいいのですが、しばらくいじっているとuGUIと連携できれば様々な問題が解決することに気づきます。
LineとMonoBehaviourとの連携です。これは言い換えるとGameObjectとの連携ということになります。
UnityはGameObject-MonoBehaviourを基点にAPIを組み上げている側面があり、これと連携することで画面構築上(ググラビリティ上?)相当な恩恵を得ることができます。(気づくのにけっこう時間がかかった...)

考えていると、Panelの外枠に線を引ければそれだけでもいろいろできるようになることに気づきました。
(っていうかPanelがUI>>Imageの絵なしバージョンってことさえこのとき知ったのですが)
オブジェクトのコピーが簡単にできるので、同じ動きをするオブジェクトがあると役割を変えて重ね掛けができるんですね。
ここで立ちはだかったのが、RectTransformです。
UnityのUIを考える人全員の前に立ちはだかる壁になるのではないか。そう思うくらい強かったです。
そもそもインスペクターウィンドウに表示されているものの意味がわからないじゃないですか。
いや、感覚的にはアンカーもピボットもなんとなくはわかりますよ。
でもそれらのエディタUIが隠している計算がさっぱりわからない。
Script的に公開されているプロパティも意味がわからない...。
ググるしかありません。
tsubakit1.hateblo.jp

www.slideshare.net

とくに今回は二つ目のスライドに助けられました。(読んでも意味がわからなくて、出てくるプロパティを片っ端からDebug.Log()したのは内緒だ)
結論を述べると、親のCanvas下に作られたPanelのRectTransform、この矩形の角の四つの頂点をスクリプトから指定するには(このスクリプトコンポーネントとしてPanel自身にアタッチするとして)、以下のように書きます。

using Vectrosity;
using UnityEngine;
using System.Collections.Generic;

public class VRectTest : MonoBehaviour
{
    private RectTransform rect;

    private Vector2 TopRight;
    private Vector2 TopLeft;
    private Vector2 BottomLeft;
    private Vector2 BottomRight;

void Start()
{
    //自分自身がアタッチしたGameObjectのコンポーネントを取得する場合はgameObject
    //自分がアタッチしているかわからないシーン内のゲームオブジェクトを探して
    //キャッシュするにはGameObject.Find("ゲームオブジェクトの名前")
    rect = gameObject.GetComponent<RectTransform>();

    var offsetMax = rect.offsetMax;//アンカーからのオフセット(右上)
    var offsetMin = rect.offsetMin;//アンカーからのオフセット(左下)

    //アンカーがCanvasの四隅を指定している場合
    TopRight = new Vector2(Screen.width + offsetMax.x, Screen.height + offsetMax.y);
    TopLeft = new Vector2(offsetMin.x, Screen.height + offsetMax.y);
    BottomLeft = new Vector2(offsetMin.x, offsetMin.y);
    BottomRight = new Vector2(Screen.width + offsetMax.x, offsetMin.y);
}}

f:id:aktaat:20161030085023p:plain
これが...
f:id:aktaat:20161030085027p:plain
f:id:aktaat:20161030085031p:plain
こう!

どこかの一辺だけに線を引くとかも選択的にできるようになりました。

やっぱり入力補完<インテリセンス>って最高なのでは

ティラノスクリプト(tyranoscript)の入力補完についてです。
vscodeでできたらいいなぁと思って調べていたのですが、Sublime Text3 でできたので(情報量が多い…vscodeはまだできたてなので)そっちで作りこんでいこうと思います。

入力補完のない世界って、本当は名前のない世界で暫定的な名前を*覚えられる*能力者にちょっと有利だと思うんです。
規約で縛るのも、本当は名前なんてないものを「とりあえずこう呼ぼうよ」って決めたことにして*本当は名前のないものたち*を名付けているんだけど、そういうのが苦手(記憶力がない)な人に厳しい世界です。

VisualStudio2013を触って衝撃的だったのは言語世界のツリーを別に人間が覚えなくてもよくない?っていう提案がすごくあって、それで世界に入れたのが大きかったと思います。
一度入ってしまうとキャラ立ちして文字列も覚えられますしね。
つまり学習の補助をコンピュータがしているっていう当たり前なんだけど、まだ受け入れられてない世界観があります。

当時のことがおぼろげですが、吉里吉里っていうゲームエンジンにはすばらしいエディタが開発されていて、人間の入力を助けていました。
今、tyranoriderというデバッグ・入力補助ツールが開発されていますが、少し似ています。
DSLの扱い方って難しくて、webの方ではだいぶヘイトを稼いでいるようです。(次々にいろいろ出てくるからね…)
まあ、そこらへん乗り切るのに、入力補完がほしいと思ったのでちょっとやってみたっていう記事です。

とりあえず調べていくと、textmateっていうxmlのファイルがあるとカラライズ(色付け)ができるってわかってきます。
拡張子は.tmLanguageです。
vscodeの場合、そのファイルをYeomenなるもののコマンドを叩くと組み込まれるらしいです。
code.visualstudio.com
Extensionsのラングイッチサポートっていうそれらしいやつがそれだろうなと思いました。
つまりtmLanguageファイルをつくらなきゃダメらしい。
というわけでそっち方向にググりなおすと、この二つの記事にそのための知識がまとまっているとわかりました。

qiita.com
qiita.com

さっぱりわかりませんでした。
で、もう少し入門的な記事を探すとこんな記事が。

qiita.com

オレオレ記法をtextファイルに適用して、色づけしてみよう!っていう素晴らしいチュートリアルです。
これを写経して、やっと意味が分かりました。
まず、tmLanguageファイルを編集するには、SublimeTextがよさそう、ということ。
そして何はともあれパッケージコントロールでAAAPackageDevパッケージをインストールしなきゃいけないってことです。
※AAAPackageDevパッケージはPackageDevパッケージに改名していました。
慣れてない人間にxml形式のファイルを直書きするのはつらすぎます。pythonさんに(裏で)働いてもらいましょう。

あと、上の解説ではjsonで書いていますが、jsonのエスケープをいきなり書くとたいてい動きません(僕は)
なのでYAMLで書く下の例がサイコーということになります。
どっちで書いてもPackageDevがtmLanguage形式に変換してくれます(F7キーで)。

書き方は
Syntax Definitions — Sublime Text Unofficial Documentation
こういう英語のサイトの例も見ながらやります。
特に引数を書きたいってときにネストするので、その書き方を見る必要があります。

patterns:
- name: support.constant
  comment: 【link】タグの構文定義
  begin: \[(link)
  end: \]
  captures:
    '1': {name: keyword.other}
  patterns:
  - name: support.variable
    match: (storage)\s*(=)\s*"(.+)"
    captures:
      '1': {name: support.variable}
      '2': {name: constant.other}
      '3': {name: storage.modifier}

  - name: support.variable
    match: (target)\s*(=)\s*\*([^\].]+)

基本は name: のところで色づけ規則を指定して、match: のところでどんな文字を色づけするか決めるって感じです。
上では()でキャプチャして、captures:でその各々の色づけを指定、begin: と end: の文字で囲うってことをしています。一個目のpatterns: で大枠を決めて、引数は二個目のpatterns: で色付けしています。

正規表現でマッチしたところが色づくとわかり、いったん白目になりますが、1日くらい勉強してなんとなくわかったら書きます。
正規表現は三つの基本演算がある、ということを意識したら少し入れました(入門的な)。

  • 連接
  • 選択
  • 繰り返し

こいつらです。
詳しくは「正規表現技術入門」っていう本の1章にまとまってます。1章でキャプチャまで網羅してるのでとりあえず1章だけ読みます。

SublimeTextのPreferences>Browse Packages...>でエクスプローラが開くので、その中のUserフォルダに作ったファイル.YAML-tmlanguageと生成した.tmLanguageファイルがあると思います。
うまくいっていれば新規ファイルを作成、言語をUser><拡張子の前に指定した名前>にすると色付けができているはずです!

...でもこれってインテリセンスじゃないよね?

そこで気づくのが.sublime-completionsなるものです。もとより機械的な解釈をしていないので、インテリセンスという名前じゃなかったのかもしれませんが、疑似的にそういう風に動くようにしたい。

そこでks.sublime-completionsを作ってさっきのUserフォルダに保存。
先人の例をなんとなく改変していきます。
まあ、タグ一文字なら登録したら終わりですが、パラメータがあるタグをどうやって登録するか。
こんな感じになりました。

// Tags with paramaters
{ "trigger": "link_parm_full\t", "contents": "link storage=${1:val} target=*${2:val}" },

↑これもエスケープまわりですぐ動かなくなります。でもセーブするだけで反映されるので、トライアンドエラーが素晴らしく速くて、なんとか正解っぽくなりました。
番号で指定している文字列(ここではパラメータval)にTabキーで一発で飛ぶので、動作的には完璧感しかありません。
やった!狙った補完と色付けができたー!と思いました。
が。
コードヒントが出ない!!
入力を途中までやっていると「あなたが探しているのはこれじゃない?」ってエディタがサジェストしてくれるあれです!
あれがなかったら、こんなものなんの意味もありません。
エディタは初めから狙った文字列を覚えている能力者のための道具でしかなく、プログラムの世界は僕のようにすべてを曖昧にしか記憶していない人間にはとうてい踏み入れられない砂漠でしかなかったのです。

コードヒントでググれどもみんなメジャーな言語のコードヒントの情報しかググっていないので、独自のコードヒント出力法なんてさっぱりヒットしません。
というわけでふて寝してからもう一度基本に返って見直していると、最初にどっちでもいいよって感じで書いてあった設定回りのことが気になりました。

# [PackageDev] target_format: plist, ext: tmLanguage
---
name: Tyrano_Ks
scopeName: text.ks
fileTypes: [ks]

このスコープネイムscopeNameのところです。
マークアップならtext、プログラムソースならsourceって指定してね、って言われてた気がします。
そこでsource.ksに変更して、もう一度コンバート(トランスパイル?)をかけます。
すると...?
サジェストが出たぁー!!
f:id:aktaat:20160505194333p:plain
選択すると?
f:id:aktaat:20160505194447p:plain
ちゃんと入力されています!

パラメータも初めに入れておいたものが書式と一緒に出るのでなんとなく何をしたらいいかわかります。
登録時に名前を変えておいて、自分用に最適化されたパラメータのサジェストとかも作ることができますね。※そういうのをスニペットというらしいです。
(ちなみに上の例だと一つ目の引数storage=valは構文的にvalが""でくくられてないといけません。なので=のカラライズがうまくいっていません。それも意図通りの挙動です)
砂漠ではとりあえず自分の通る道だけ覚えるという戦術で挑みたいと思います。そのための下地についての記事でした。

TyranoScriptの変数はJavaScriptオブジェクトだったのでオブジェクトリテラルや配列リテラルで設定できるという理解#再

(一回書いたと思ったお題が跡形もなく消え去っていた…)

TyranoScriptの変数についてはこちらのページをとりあえず参照していくtyrano.wiki.fc2.com

minifyされたjavascriptはSublimeTextのBeautifyプラグインで整形して読もう
整形前
f:id:aktaat:20151031113448p:plain
整形後
f:id:aktaat:20151031113454p:plain


こちらのテキストをfirst.ksに張り付けると動作します。

;コメントアウト
*start

Tyranoscriptの変数では、オブジェクトリテラルと配列リテラルを使うことができます![l][r]

以下をiscript/endscriptタグを用いて入力します。[l][r]
([]は半角で読み替えてね。←タグをエスケープする方法がわかんなかった)[l][r]
f.ppp={
	placeA:{name:"sibuya",reiki:100,item:"room-card",[r]
	    _line:[metro-ginza","metro-hanzomon"]},[l][r]
	placeB:{name:"akihabara",reiki:120,item:"radio",[r]
	    _line:[musashino-line","yamanote-line"]},[l][r]
	placeC:[{name:"nihonbashi"},{reiki:90},[r]
	{item:"master-key"}]}[l][r]


;実際にオブジェクトを設定しているところ
[iscript]

f.ppp={
	placeA:{name:"shibuya",reiki:100,item:"room-card",
	line:["metro-ginza","metro-hanzomon"]},
	placeB:{name:"akihabara",reiki:120,item:"radio",
	line:["musashino-line","yamanote-line"]},
	placeC:[{name:"nihonbashi"},{reiki:90},{item:"master-key"}]
}

[endscript]

[cm]
では設定したオブジェクトにアクセスしてみます。[l][r]
placeA.name:[emb exp="f.ppp.placeA.name"][r]
ドットでつないでオブジェクトのプロパティを得るパターン[r]
placeB.item:[emb exp="f.ppp['placeB']['item']"][r]
['']でオブジェクトを指定していくパターン[r]
placeA.line:[emb exp="f.ppp.placeA.line[0]"][r]
配列リテラルへのインデックスでのアクセス[r]
placeC.reiki:[emb exp="f.ppp.placeC[1].reiki"][r]
オブジェクトの配列にアクセスすることもできる。[r]
(そのあとオブジェクトの名前を指定することでプロパティを得ています)。[r]
[l]
[cm]

表示はこんな感じになります。[r]
placeA.name:[emb exp="f.ppp.placeA.name"][r]
placeB.item:[emb exp="f.ppp['placeB']['item']"][r]
placeA.line:[emb exp="f.ppp.placeA.line[0]"][r]
placeC.reiki:[emb exp="f.ppp.placeC[1].reiki"][r]

実際に動かした画面
f:id:aktaat:20151121095315p:plainf:id:aktaat:20151121095333p:plainf:id:aktaat:20151121095338p:plain

オブジェクトの設定と
([iscript][endscript]タグで囲う)

f.ppp={
placeA:{name:"shibuya",reiki:100,item:"room-card",
line:["metro-ginza","metro-hanzomon"]},
placeB:{name:"akihabara",reiki:120,item:"radio",
line:["musashino-line","yamanote-line"]},
placeC:[{name:"nihonbashi"},{reiki:90},{item:"master-key"}]
}

オブジェクトやプロパティ(値)の取得
placeA.name:[emb exp="f.ppp.placeA.name"][r]
placeB.item:[emb exp="f.ppp['placeB']['item']"][r]
placeA.line:[emb exp="f.ppp.placeA.line[0]"][r]
placeC.reiki:[emb exp="f.ppp.placeC[1].reiki"][r]


この両方をペアで覚えておくと便利です。

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/

TyranoScriptのjQuery拡張部分のメソッドを書き出してみた

v342です。
javascriptよくわかってないマンがさらっと読んだだけなので、間違ってる可能性
ファイル名はlib.js

(function($){

});

プラグインは囲うんだって

$.setBaseURL

パスからURLを取得

$.isHTTP

最初の四文字を調べてHTTPかどうかを返す

$.play_audio

オーディオ再生中かどうか

$.getViewPort

表示画面の高さと幅を返す

$.escapeHTML=function(val,replace_str){}

valで指定したdivタグを作成する。もしそれが""(文字列なし、boolean:false)ならreplace_strにする。

$.br=function(txtVal){}

文中の\r\nをbrタグに入れ替え

$.getNowDate

現在の日をyyyy/mm/ddで返す

$.getNowTime

現在の時刻をh/m/sで返す(ちゃんと形式調べてない)

$.convertSecToString=function(val){}

valを_日_時間_分_秒形式で返す

$.setToMinute=function(val){}

val(秒)を_分_秒で返す

$.trim

文字列の両サイドの空白を消して返す(グローバルマッチ以下同)

$.rmspace=function(str){}

半角全角スペース、改行を文中から消す

$.replaceAll=function(text,searchString,replacement){}

text中のsearchStringをreplacementで差し替えて返す

$.setExt=function(name,ext_str){}

nameに拡張子がなければ.ext_str拡張子をつける

$.cloneObject=function(source){}

sourceをクローンする
(内部→return $.extend(true,{},source);)
再帰的な上書き=true,新規オブジェクト={}にsourceを上書きして返す=クローンする

$.convertOpacity=function(val){}

0-255の範囲でオパシティを返す(範囲外のnumberを丸める)

$.convertStorage=function(path){}

未実装?(コメント://パスにfgimage bgimage imageが含まれていた場合、それを適応する)

$.convertColor=function(val){}

0xが含まれていたら#に変換して返す

$.convertBold=function(flag){}

flagがtrueなら"bold"を、そうでないなら""を返す

$.send=function(url,obj,call_back){}
//game.current_story_file=story_file;

ajaxする。jsonオブジェクトを引数にしてコールバック実行

$.loadText=function(file_path,callback){}

読み解けない...

$.getCookie=function(key){}

クッキーを取得

$.isNull=function(str){}

nullを返す

$.dstop

コンソールに"dstop"と出力

$.userenv

ユーザー環境を取得(iPhone or iPad or Android or pc)

$.lang=function(key){}

tyrano_lang["word"][key]で設定されたプロパティを返す

$.novel=function(key){}

tyrano_lang["novel"][key]で設定されたプロパティを返す

$.getBrowser

ユーザのブラウザ情報を取得

$.swfName=function(str){}

Windowsの場合、ウィンドウを返す(?)

$.trans=function(method,j_obj,time,mode,callback){}

トランジション

$.minifyObject=function(obj){}

要素から空白のオブジェクトを削除して返却する

$.setStorage=function(key,val){}

localStorageにkeyと難読化したオブジェクトvalのセットを作成する

$.getStorage=function(key){}

keyのvalを取得する

$.countObj=function(obj){}

オブジェクトの個数を返す

$.setName=function(jobj,str){}

渡されたjqueryオブジェクトにクラスをセット

$.isFlashInstalled

フラッシュのインストール判定

クリックをタッチイベントに読み替える処理

$.error_message=function(str){}

エラー時にalert(str)を呼び出す

$.setCookie=function(key,val){}

クッキーをセット

innerHTMLをouterHTMLに読み替える処理

easing各種の設定


以上です。読み終わったので、次は標準のプラグイン(kag)を読んでみたいと思います。

コンソールアプリケーションで学ぶReactiveExtensionの仕組み(チュートリアル作ったよ)

なんとなくRxよさそうだな、もっと勉強したい!という欲が高まってきたのですが、Unityをいきなり絡めると決まりごとが増えるので(MonoBehaviourのUpdate()の仕組みとかCoroutioneとか)、単純さを前面に押し出した勉強法を探していました。

単純!基本!といえばコンソールアプリケーションです。

僕も2014年からC#勉強し始め組として、VisualStudioを使ったコンソールアプリケーションでの勉強法はプログラム習得にとても有効だったと思っています。

ReactiveExtension(Rx)を学ぶのにも、大量のコンソールアプリケーションのサンプルがあればいいなぁと思いました。それで探してみると、ちょうどよさそうなのがありました。
Rx Wiki - Reactive Framework (Rx) Wiki
です。

このサイトのサンプルをもとにチュートリアルを作ってみたいと思います。

まず、最初のページの以下のリンク 101 Rx Samples をクリックします。飛んだページのTable of Contentsから、サンプルコードを選んでいきます。

今回は一番下の

Use Subject<T> as backend for IObservable<T>

(訳:IObservable<T>のバックエンドとしてのSubject<T>)
をやってみたいと思います。

このSubject<T>がすごく便利で、今後頻出することが予想されます。
(Subject<T>は一言で言うと、IObserver<T>とIObservable<T>どちらも実装したクラス)

元のコードはこれ↓
f:id:aktaat:20141227171505p:plain

orderクラスはプライベートなフィールド_paidDateを持ちます。(DateTime?はヌル許容型)
次にプライベートなストリーム、order型のサブジェクト_paidSubjをnew演算子で作ります。
かつその_paidSubjをAsObservable()して返すゲッターを設定。(これで他のクラスに公開されます)

メソッドMarkPaid()は_paidDateを引数から設定し、_paidSubjストリームに自分自身(order)をOnNext()しています。
Subject<T>ストリームはIObserver<T>を実装しているのでOnNext()が可能なんですね。OnNext()することでストリームに値が流れます。ここでのポイントは流れている値の型が<order>ということです。これについては後ほどいじってみたいと思います。

VisualStudioを立ち上げ、C#の新しいプロジェクトからコンソールアプリケーションを作成します。
f:id:aktaat:20141227180024p:plain

ReactiveExtension(Rx)を参照するライブラリに追加しなければなりません、ので、NuGetを使います。
f:id:aktaat:20141227180550p:plain
ソリューション・エクスプローラーの[ConsoleApprlication57]を右クリック。
[Manage NuGet Packages...]を選択します。
すると以下のような画面が出るので、
f:id:aktaat:20141227180843p:plain
検索窓で reactive と入れてやると一番上に目的のライブラリが出てきますので、インストールします。途中で何か聞かれたらacceptしてずんずん進んでください。

終わったら、using 名前空間に追加することでReactiveExtensionが使えるようになりました。
f:id:aktaat:20141227181240p:plain

サンプルコードの通り、打ち込んでいきます。
f:id:aktaat:20141227181426p:plain
これを実行すると…
f:id:aktaat:20141227181525p:plain
無事動きました。

Main関数の中で、新しいorderオブジェクトを作成し、Paidストリームが値を受け取ったときの挙動をSubscribe()メソッド(内のラムダ式)で定義しています。
サンプルでは、Paidというストリング型の値を表示していますね。
最後にMarkPaid()メソッドを実行して値を流しています。

ここでふと思うのは、<order>型のオブジェクトを流しているのに、出力は関係ないストリングっていうのはあまりおもしろくないな、ということです。
MarkPaid()メソッドはDateTimeを引数に持っているので、その値を出力できないか?
というわけでやってみました。
f:id:aktaat:20141227182339p:plain
実行。
f:id:aktaat:20141227182531p:plain
うん。意図したとおりに動いています。

追加・変更したのは2点。自身のメンバフィールド_paidDateにゲットのプロパティをつけて…

public DateTime? PaidDate{ get { return _paidDate; } }

サブスクライブの内容を、プロパティからの値の取得に書き換えます。

order.Paid.Subscribe(_ => Console.WriteLine("{0}",order.PaidDate));//Subscribe

オブジェクトをストリームに流して、意図したメンバの値を取得するという、このサンプルプログラムのストーリーはかなり使えるのではないかなーと思います。
ぜひ試してみてください。