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つのモデルに登録される、という形式を踏むのが一般的なようだ。

2015年9月23日水曜日

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

作った。こちら。

Github - TakafumiFukuyo/UniDLink

Unity 上で値をリンクするライブラリである。

JavaFX の value-bind 機構に近い、といえばわかりやすいだろうか?(むしろ余計にわかりづらいだろうか?)

この下にいろいろと解説を書いたが、今は脳みそが回らなくて微妙な文章が生成されてきたので消した。また次辺りのpostで解説するかもしれない。

2015年5月25日月曜日

特定の実行環境上かつ Unity 5.0.2f1 上で Scroll Rect と Mask コンポーネントを用いた際に起きるバグについて

Unity 5.0.2f1(記憶の限りでは1f1でも同様だったように思える)上で、Scroll Rect Component と Mask Component を用いて、そのMaskに用いた Rect をはみ出るような構成をした上で Scroll すると、はみ出た部分がきちんと Mask されずにちらついて見えるバグを発見した。

これは私の持っているデスクトップPC(AMD Radeon HD 7850を用いている)で起きた。Intel チップの載ったラップトップPCや Android(少なくともNexus 7) では起きなかった。

ドライバの構成は以下のとおりである。

iPhone や nVidia チップに関しては試験環境がなく不明であるが、少なくとも環境依存するバグのようである。

--以下個人的日記--

近くバグ報告をしたい。が、英語力がない。ほんやくチーム…いやチームまでは要らないか…ほんやくマンが必要だ。

2015年4月16日木曜日

Unity uGUIのMoveイベントはポインタがMoveしたときのイベントではない

--要約を本文より抜粋--

そう、イベント「Move」は確かにカーソルが動いたときに発動する…発動するがカーソルがマウスカーソルだともタッチのポインタカーソルだとも言っていない…つまりUnityがそう指定すればカーソルは現在選択中のGUIカーソル、つまりTabやArrow Keyで移動できるカーソルが「Move」したときに発動させることも可能だと言うこと…ッッ!!!

--以下が本文だが日記を含むので不要な場合は読む必要はない--

最近uGUIで仕事をしている。

ポインターの動きを追って、それによってこちらで動きを知りたかった。

ドキュメントやVisual Studioのコード補完でMoveというものがあったので使う。

うんともすんとも言わない。

そう、Moveは確かにカーソルが動いたときに発動する…発動するがカーソルがマウスカーソルだともタッチのポインタカーソルだとも言っていない…つまりUnityがそう指定すればカーソルは現在選択中のGUIカーソル、つまりTabやArrow Keyで移動できるカーソルが「Move」したときに発動させることも可能だと言うこと…ッッ!!!

つまるところそういうことだ。

マウスが動くだけならOnEnter等、タッチしてどうこう、であればOnDrag等を用いるべきだと言うことがわかった。

余談だが源典を読んだことがなくても様々な創作物のネタを理解することができるのはインターネット時代以降に限ることなのだろうか。それはそれとして、寝る。

2015年3月26日木曜日

Arduino LeonardoでApple Extended Keyboard IIをUSBで繋ぐ

Apple Extended Keyboard IIというものを、少し前に入手した。引っ越しの少し前だったので今USB化するのも運ぶ手間もあると思ってしなかったのだが、引っ越しが終わってひと段落した。東京である。

というわけでApple Extended Keyboard IIのUSB化である。日本語で出る情報で適当にググるとDa Vinciというボードを用いたものに当たる。だが今回はArduino Leonardoを用いることにした。理由は特にない。しいていえばたまたま入手できたからである、

こことかここを見ながら配線とtmk_keyboard内のadb_usbのmake、avrdudeによる書き込みを行う。

なお、WinAVR20100110版をWindows 8.1に普通に入れて普通にmakeするとmakeがerrorを吐いて止まる。エラーコード(戻り値?)は -1073741502 だった。

適当にググるとこんなサイトに当たる。

このファイルをWinAVRのutils/binにぶち込め、と言ってmsys-1.0.dllを添付している人がいるが、いくらなんでも怖すぎるので自分でmsysを落としてくる。msysの落とし方は適宜ググってほしい。そこまで解説するのはつらい。

mingw-getでmsys-baseあたりを適当にインストールすると、msys/1.0/binにmsys-1.0.dllがたぶん公式から落ちてきているであろうと思われる雰囲気で存在している。少なくともさっきのより怖くない。

これをさっき言われた通りutils/binに入れるとmakeが走るようになる。

そこから先はだいたい先に挙げた日本語の情報に従う。DINコネクタの配線には注意する。

さて、makeも、配線も、avrdudeによる書き込みも終わったと思う。ところでこのDINコネクタのADBピンは、いったいArduino Leonardoのどのピンにつなげれば良いのだろうか?

結論から言うと、Arduino Leonardoのシルク印刷にSCLと記述されているピンにつなげばよい。

この情報は、先の日本語情報や、tmk_keyboardのgithubにあるPD0ピンがデフォルトだよ、という情報と、Arduino公式の32u4とArduinoボードのピンアサインマッピングのページを見ると良い。PD0はSCLにつながっている、との記述があるので、ADBピンをSCLに、Vccを5Vに、GNDをGNDにそれぞれつなげる。

これで完成だ。

2015年2月3日火曜日

Ruby on Rails の ActiveRecord でドハマリした話 - リフレクションとかsendメソッドとか

簡潔に

Rubyにはsendメソッドという(Java等で言う)リフレクション用メソッドが存在する

sendメソッドはObjectから継承されるのですべてのオブジェクトはsendメソッドを持つ

ActiveRecordでModelを作るときに送り先の意味でsendカラムを指定した場合、 上記のsendメソッドによってModel作成時のアクセスがおかしくなってしぬ

なにをしようとしたか?

ActiveRecordのModelを作る際にカラム名に「send」を指定した。

何が起きたか?

ArgumentError (wrong number of arguments (2 for 0))

がモデルのnewやcreate等で起き、ログに記録される。

rails cで同様にnew等をすると意味の分からない箇所でエラーが起きているように見える(具体的なログは失われてしまった…)。

対処法は?

sendという名前のカラムを作らない。

sendという名前のカラムがある場合は変える。

すでにあって変えられない場合はどうしたらいいのか私はわからない。

きっとカラム名とModelのシンボルを変換してくれる指定をする何かがあると思う。

どうでもいい話

あとsendってのは動詞なので、カラム名に使っていいものではない。この命名をしたときの私はとても疲労していたが、だからといって動詞をカラム名につけてはいけない。

カラム名には名詞を使おう。少なくとも動詞はやめよう。英語力を付けよう。そう思った。あと疲れているときに無理にコードを書くのはやめよう。いや、無理をしなくてはいけないことも、あるけれど。

2015年1月29日木曜日

Unity Free版を骨の髄までしゃぶりつくすハック - .NET Socketなんて怖くない!

私は貧乏である。少なくともリッチマンではない。

よってUnity Free版を使っているのだが、Free版でAndroid/iOS向けにコンパイルしようとすると、思わぬ罠が待っていることがある。

それは .NET Socket は、Android/iOS向けにはPro Onlyな機能であることだ。

さらにまずいことに、Unityにおいては、プロジェクトをモバイル向けにコンパイルする際に.NET Socketへの参照を持つdllやコードが「プロジェクト内」に1つでも存在すると、Pro Onlyであるという旨…は特に出ず、コンパイルエラーだとだけ表示する。

これだけであれば、モバイルオンリーで制作中の場合特段の問題にはならない。.NET Socketへの参照を拾わないように、注意深く作っていけば良い。

問題は次のような例にある。


  • モバイルとPC両対応のゲームを1つのPJ内で作っていて、PCで.NET Socketへの参照がある
  • モバイル向けにはPCとは違う画面構成だが、ゲームとして共通であるので1つのPJで作っていて、PJ内のPC向けに.NET Socketへの参照がある
  • モバイルでのネットワークゲームを作っていて(UnityのNetwork ViewはFreeでもモバイル向けにコンパイル可能だ)ネットワークコードの都合上(RPC等を使うと、どうしても1PJにサーバとクライアントの同居状態がほしくなる)でサーバプログラムも同じPJで作っており、サーバプログラム側で、既存プラットフォームとの接続等で.NET Socketへの参照がある
つまり、モバイル版には.NET Socketを注意深く使わないようにしたが、非モバイルを対象に諸般の都合により.NET Socketあるいはそれへの参照を持つライブラリが必要になってしまった場合、である。

このようなとき、Unityはどうするかといえば、私の試した限りでは、モバイル向けのシーンにアタッチされてるスクリプトの一切に.NET Socketへの参照あるいは.NET Socketへの参照を持つライブラリへの参照が含まれていなくても、もっと言えば、PJに.NET Socketへの参照を持つdllが混じった時点で、モバイル向けにコンパイルが不能となった。

つまり、この時点でプロジェクトはお手上げ、大幅な修正をする必要になる。あるいはPro版を購入する必要がある。

…だがちょっと待ってほしい。本当にそうだろうか?本当に大幅な修正がいるのだろうか?

私たちはモバイル向けに.NET Socketを使おうとしてない。だというのに、Pro版が要るのだろうか?

そんなことはない。私は巧妙なハックにより、これを解決する策を見出した。

ただし、これがUnityライセンス契約的に正当かどうかについて、私は保証できない。実際にプロジェクトでこの手法を取る際には注意してほしい。

以下のようにする。

.NET Socketへの参照や.NET Socketへの参照を持つライブラリへの参照をするusingを、次のように囲う。

#if !(UNITY_ANDROID || UNITY_IPHONE)
//usingたち
#endif

同様に、.NET Socketへの参照や、参照をもつライブラリへの参照をするコードの、クラス本体の内部を、丸ごと囲う。

public class NetworkCode : MonoBehaviour {
#if !(UNITY_ANDROID || UNITY_IPHONE)
//コードの本体たち
#endif
}

このようにする。

さらに、その.NET Socket参照をするコードを、参照するコード、つまりかかわりのあるコードの部分を、注意深く同様に囲う。

これをしないと、既存のメソッドコードがないことになるので、まずプロジェクト自体のコンパイルができず、問題となる。

そして、Unity上で実行する際に、上記のコードによってサーバシーンやPC向けシーンが不具合を起こさないように、Build SettingsからSwitch PlatformでAndroid/iOS以外にしておく。

もしモバイル向けシーンをUnity上で実行するなら、逆にSwitch PlatformをAndroid/iOSに設定しておくと良い。

この操作をすることで、.NET Socketへの参照や、その参照を持つライブラリ(dll)を削除できる。もしAndroid向けにビルドする時は、アセットフォルダからこれを一時的にどこかに移動しよう。何度も移動するのは面倒だから、.batなり.shなりを作ると良いかもしれない。

その後、Android/iOS向けにコンパイルすれば、Unityはエラーを出さないはずだ。PC/サーバシーンをコンパイルしたいときは、先ほど一時的に移動したものをアセットフォルダに戻してコンパイルすればいい。

途中から変更したり、何度もするなら面倒かもしれないが、初めからこれを意図して設計したり、バッチやシェルスクリプトを書いたりすれば、そう問題はないはずだ。そもそもAndroid向けにコンパイルなんて1日に100回も200回もやったりはしないはずだ。

以上、Unity Free版を骨の髄までしゃぶりつくすハック、第一弾だった。第二段があるかはわからないが、もしネタが出たら書きたいと思う。