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

チリペヂィア

リンクフリー。サンプルコードなどは関連記事内でライセンスについて明示されない限り商用利用なども自由に行って構いませんが、自己責任でお願いします。またこれら日記内容の著作権自体は放棄していません。引用部分については引用元の権利に従ってください。

Enterするまで入力を反映しないテキストボックス再考

PropertyDrawerって結局バグってEditor継承するしかないよね(白目)

さておき。

掲題のネタが必要になって以前書いた記事を見直したんですが、あまりにもブン投げ記事だったので書き直し。

今回はアプライ遅延コントロールを複数並べる事も考慮して挑戦です*1。ついでにフォーカスロストのタイミングを検知&反映する方法も。

こういうのがあったとして。

普通にエディタを書いたらこんな感じだと思います。


まず第一に今、気の利いた処理が必要ではないなら、これから続く、ややこしい記事を読むのは今すぐにやめてPropertyField使ってしまうのが一番楽だと思います。でもそういうわけにもいかず、例えば次のようなケース…

  • GameObject.OnValidate()じゃなくエディタ側でValidationしたい
    • 無効な入力でも無駄にUndo.RecordObject()撃たず帳消しにしたい
    • 入力データを反映する時、ユーザビリティ等の観点から配列メンバを逐一ソートしておきたい
  • 配列エディタの配列数入力フィールドの自作で入力遅延しないと、文字を消した瞬間に配列長ゼロでリストがクリアされちゃう事故とか

こんなケースでは、入力途中でも逐一現在の入力値を返すGUIコントロールをそのまま使う事は出来ません。スクリプトで制御して、GameObjectへの反映を遅延させる必要があります。

まずUnityのスクリプトリファレンスに書いてあるのは

  1. GUI.SetNextControlName()で入力フィールドに任意の名前を与え
  2. GUI.GetNameOfFocusedControl()で実際にフォーカスされているか確認

ただ、それだけだとニッチーモサッチーモなので、次のような記録変数を仕込みました。

[CustomEditor(typeof(EnteringApply))]
public class EnteringApplyEditor : Editor
{
    // 現在編集中のコントロール名
    string editControlName;
    // 現在編集中のコントロールに表示している値
    object editControlValue;

端的に言えば、反映を遅延する=入力エイリアスの一時的なバッファ管理。入力フィールドに一意な名前を与える条件で、かつ、カーソルは1か所だけ。だったら入力状態をobject型にキャストしてしまえば、もうこれ以上のバッファリングも要らないかな多分。

実際に先ほどの2つのフィールドを同時に扱ってみます。

うん…ほんのちょっとだけ長いね(震え声)

そんなわけでクラス化はした方が良いと思いますが*2あんまりキレイにまとまらなかったので非公開…。型キャストに絡んでジェネリック、さらに比較処理カスタマイズにデリゲートとか渡し始めるとなかなかシンプルにはまとまらんですよ…


f:id:tiri_tomato:20150628081634p:plain
スクショとったけど見た目にまったく変化の無い話であった。

*1:これで配列カスタマイズも安心

*2:こういったフラグ地獄になっている個所では、クラス化は見た目とか精神衛生面の問題にとどまらない話。いちいちブレークするよりもToString()のオーバーライドで逐一ログに出す方が状態の変化チェックがラクなんですよね…