2015年10月23日金曜日

Unityで値リンクするものを作った の解説

値リンク 概説

まず値リンクとは何か説明しよう。
値リンク、値バインドともいう(というかそちらのほうがメジャーな言い方だ)が、つまりこれは、「特定の値」が変更された際、ほかの値に変更を及ぼすコードをラップしたライブラリのこと、である。
値リンクが最も威力を発揮するGUIアプリケーションの場合、上記の特定の値とは、つまるところモデルデータであることが多い。
UnityのようなゲームにおいてはMVCが単純には適用できない(この話はまた今度したい)ためにその限りではないが、この特定の値はおおよそほかのオブジェクトに影響を与えたい根幹オブジェクトの値そのものであったり、あるいはモデルデータであったりする。
リンクすると言っても特に特別なことをしているわけではない。基本的な構造は以下のとおりである。
  1. 影響を及ぼしたい特定の値をラップしたクラスを作成する
  2. 特定の値のラップクラスにGetter,Setterを設定する
  3. ラップクラスにイベントリスナを設定する、この登録されたイベントリスナがほかの値に影響を及ぼし、値を伝播させる
  4. Setterに値が渡された際にイベントリスナを、変更された値(ライブラリによっては変更前の値も渡すことがある)と共にコールする
と、ざっと簡単だ。Setterがイベントリスナをコールし、そのイベントリスナの実装がほかの関連する値に、変更された値を伝播させる。
実際のコード上ではこういうことが起きており、これにより値は変更時に瞬時に他の関連する値に伝播する(なお、JavaFX等高度なライブラリではより効率的に値を伝播させるために値の伝播のコールを伝播先の値が評価されるまで遅延させたりする場合もある)。

値リンクの使いどころ

この値リンクはどこで使われているか。最も身近な例としては、色選択のスライダである。

このようなUIがあるとしよう。R,G,Bのそれぞれのスライダを動かすと、プレビューの色は瞬時に反映され変更された色が表示される必要がある。
これらをシンプルな値リンクを用いて解決すると以下のようになる
  1. R,G,Bのカラー値をそれぞれモデルとしてとり、スライダの移動に合わせて変更する
  2. モデルは変更に合わせてプレビューの色のデータを連動させる
なお、より強くUI系にも結び付いた値リンクシステムであれば、スライダの移動データ自体がモデルになっており、そのモデルとカラーモデル、さらにプレビューの色のモデルをそれぞれにリンクさせるようなことをする場合もある。
もちろんシステムと十分に強くモデル的に結びついた値リンクシステムは、Unityで構成するようなゲームにはあまり向いていないので、より柔らかく必要なときにのみ必要な利用ができるようなシステムのほうが良いが、GUIアプリケーション系ではそういうシステムのライブラリもある。

Unityでどんな時に値リンクがあると便利か

基本的にはユーザインタフェースコードの煩雑さを防ぐために用いるのが最も良い適用例である。
なんにでもこれを適用しよう、というのはあまり良い手法ではない。
特にゲームにおいて、ゲームのロジック記述は「コードによる文で語られるべき」箇所もあり、かといって「値のリンクによるデータで記されるべき」個所もある。
このあたりの見極めは大事で、ともすればいったい何をしたいコードなのかが霧散しているような状態を作り出してしまう。
上に、ユーザインタフェースコードの煩雑さを防ぐために~と書いたが、より一般化して言えばこういうことである
同じ内容のデータの表示を多様な手法で行いたいが、データは一つであり、それは与えられたり変更されたりする
こういう状況が起きているとき、値リンクは最も威力を発揮するだろう。
簡単に例示するとこういう状況だ。

ここではAliceがクリックなどのアクションで選択されており、このAliceを選択することは「選択中のキャラクター」というモデルに変更を加えることになる。およそこのモデルはenum等で表される値だろう。これで、キャラクターのUIに対してモデルより伝播させた値により、選択中のキャラクターをハイライトすることができる。
さらに可能なアクションが引き出される。これは、Aliceを選択した際に、Aliceのオブジェクトにあるアクションリストを「利用可能なアクション」のモデルに適用したことによって反映されるだろう。
さらにここで会話を選んだとする。この会話アクションが必要なデータとしてキャラクターの名前があるとしたら、それは「選択中のキャラクター」モデルより引き出すことができるだろう。
あるいはさらに、この会話アクションのクラスデータに対して「選択中のキャラクター」が変更された際に「キャラクターの名前」のデータを伝播させることもできる。これによってクラス依存関係の逆転を起こせば、よりすっきりしたクラスの参照関係を作れるかもしれない。
この状況ではAliceというキャラクターがいくつかのデータを持ち、そのデータが色々な表現方法で表現されている。まさに先に挙げた状況にマッチするだろう。

最後に多少別の話題を - 相互リンクはどのように行われるか?

たとえば、先のRGBスライダの話で、プレビューをクリックするとランダムにプレビューの色が変わり、それがカラーモデルに反映され、さらにそのモデルよりRGBのスライダを動かしたい、という状況を想定する。
これは、カラーモデルとRGBスライダの相互リンクに他ならない。JavaFX等では双方向値バインド bidirectional value bind と呼んだりする。
ところで、値の変更時にはイベントリスナがコールされる。片側が変更されたときにもう片側を変更し、それがさらに伝播すると無限にイベントリスナがコールされ、メソッドのコールスタック領域を食いつぶしてしまう。
これを防ぐため、特殊なイベントリスナを用いて、片側からもう方側へ変更が伝播したらそこで止めるようにする。特殊な、と言っても単純だ。以下に示す。
  1. イベントリスナの実装クラスが値Aよりコールされる
  2. 値Bへ値を伝える前に、あらかじめフラグを立てておき、値Bへ値を伝播する
  3. 値Bでも同じイベントリスナがコールされるが、あらかじめ立てておいたフラグにより値Aへの伝播は抑制され、またフラグは回収されて下ろされる
これだけである。値BからAでも同じことが起きる。
このイベントリスナは、一度にきちんと2つのモデルにまたがって登録されなければいけないため、大抵の場合staticなメソッドによって、隠蔽されたコンストラクタが呼び出され、そこで生成されたオブジェクトが2つのモデルに登録される、という形式を踏むのが一般的なようだ。

0 件のコメント:

コメントを投稿