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

テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】AnimationController間でStateMachineBehaviourのインスタンスを共有するSharedBetweenAnimatorsの挙動

SharedBetweenAnimatorsというAttributeがあります。

簡単に言えば「StateMachineBehaviour」のインスタンスを共有するAPIです。少しだけ動作に疑問があったので確認してみました。

インスタンスを共有するSharedBetweenAnimators

StateMachineBehaviourはAnimationControllerのStateに設定する、以下のタイミングで駆動してくれるスクリプトです。

  • ステートに入ったタイミング
  • ステートから抜けたタイミング
  • ステートが動作してる
  • 子ステートが動作してる
  • IK動かしてる
  • RootMotionでキャラクターを動かす

上手く使用すれば、特定のアニメーション(ステート)再生時にのみ処理を行うといった動作が期待出来ます。地面判定とか

f:id:tsubaki_t1:20170522203635j:plain

このStateMachineBehaviourですが、AnimationController毎に異なるインスタンスが生成されます。AnimationControllerを動かすキャラクター毎に状態が違う可能性があるからです。また同様に各ステート毎に異なるインスタンスを持ちます。

要するにAnimationControllerが持つ、各State毎に複数持つ事が出来ます。つまり、

  • キャラクター100体
  • キャラクターの持つAnimationControllerはState10個持つ
  • Stateは各2個のStateMachineBehaviourを持つ

がある場合、以下のStateMachineBehaviourインスタンスが生成されます。

 

 

これが要らないケースもあります。そういった場合SharedBetweenAnimatorsを使用する事で、異なるAnimationControllerの同一Stateのインスタンスを共有します。

つまり、先程と同じ条件の場合、以下のStateMachineBehaviourインスタンスが生成されます。

 

さらにステート間でインスタンスをシェアしたいなら、SubStateMachineを使用して親ステートにStateMachineBehaviourを設定すると良いかもしれません。

パラメータはシェアされる

SharedBetweenAnimatorsでシェアしたインスタンスですが、インスタンスが共有という事は変数等も今日ウユになります。

例えば下のようなAnimationControllerとSceneがあったとします。

  1. SampleというStateMachineBehaviourがあり、Tagというフィールドを持つ
  2. StateはIdleとRunに二種類で、各々異なるTagを持つ
  3. Animator1とAnimator2という二つのAnimatorがあり、同じAnimationControllerを参照している

f:id:tsubaki_t1:20170522211009j:plain

これを実行した結果が、下の内容です。

  • Animator1とAnimator2で同一のState.csを動かしているが、ステートのカウントは常に進む
  • 異なるStateのStateMachineBehaviourでは値がリセットされ1からスタートしている

f:id:tsubaki_t1:20170522211317j:plain

という事で、同じステートの同じStateMachineBehaviourではパラメータが共有されている事が分かります。

このため、例えば同一State間で共通して参照するコンポーネント等がある場合、どれか一つのStateMachineBehaviourに値を設定してやれば、以降は設定する必要が無くなります。

どんな時に使うべきか

使うべきタイミングは、ステートが動的に動かすフィールドを持たないStateMachineBehaviour等でしょうか。

例えばMatch Targetでキャラクターを動かすStateMachineBehaviourの場合、設定すべきパラメータは以下の通りです。

  • MatchTargetの終着点
  • アニメーションに対するMatchTargetの開始と終了タイミング
  • MatchTargetで支点とする部位

終着点は動的かつキャラクター毎に設定する必要がありますが、それ以外はステート単位で共有しても問題無い項目です。

ですので、終着点は都度Animatorから取得しそれ以外の項目はシェアしても良いというアイディアが出ます。

他にも足音を鳴らす、Animator終了待ちイベントを発行する等も、StateMachineBehaviourを共有しても問題無さそうです。

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

StateMachineBehaviour全体でシェアするならStatic、同じステートでシェアするならSharedBetweenAnimatorsとなりそうです。

値がリセットされない

多分バグだとは思いますが、どうもSharedBetweenAnimatorsを使用すると値がリセットされないようです。

例えば上の例で言えば、ゲームの再生を終了して再度ゲームを再生すると、Entry5からスタートします。

f:id:tsubaki_t1:20170522211852j:plain

ので、値が変動する物を使いたい場合、いずれかのAnimatorをAwake辺りのタイミングでリセットしてやる必要がありそうです。*1

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

*1:というか、共有対象をフィールドにしてくれたらシンプルになりそうなんですけどね

【Unity】ステージのギミックをアニメーションで作る際に便利な設定

f:id:tsubaki_t1:20170518221552g:plain

今回は、現在位置からの相対距離で動くアニメーション設定という小ネタです。この使い方に気づかなかったので色々面倒な事をしていましたが、不要やったんやってお話です。

ステージギミックとアニメーション

ステージのギミックをアニメーションで作るのは、意外と便利です。特に最近はAnimationをPhysicsベースで動かす事が出来るようになったので、上手く使えばユニークな動きを見せるステージギミックが作れそうです。

tsubakit1.hateblo.jpkon

初期設定ではAnimationの位置は親オブジェクトからの相対値

アニメーションでステージのギミックを動かそうと思った時、面倒になるのが「Animationの動きや位置は親オブジェクトからの相対値」という点です。
親がいなければRootオブジェクトが対象となります。

 

例えば下のようなコインのアニメーションを用意します。xとz座標は0で、y軸に簡単なアニメーション、ついでに回転を行うアニメーションです。

f:id:tsubaki_t1:20170518230053g:plain

このコインを画面に二つ用意した場合、もしコインに親オブジェクトが無ければ、二つのコインは現在位置を無視して親オブジェクト(Root:x:0,y:0,z:0)からの相対距離でアニメーションを行います。

つまり二つのコインは異なる場所に置いた場合でも「同じ位置で同じようにアニメーション」してしまい、一つに見えるレベルで重なってしまいます。

f:id:tsubaki_t1:20170518230706j:plain

変化値だけでアニメーションを行う設定

そこで何時も面倒な設定を使用していたのですが、よく見たら「アニメーションの相対値」でオブジェクトを動かす設定がありました。

BlendingをAditiveに設定すると、初期値からの移動量でアニメーションを行うみたいです。

f:id:tsubaki_t1:20170518231221j:plain

ここで言う初期値とは、アニメーションの開始時の値です。例えば下の例だとyが2.97の位置から約3.5の位置までジャンプするので、移動量は大体0.5です。
なので、このアニメーションを適応したモデルは何処にあろうとも、0.5mジャンプします。

f:id:tsubaki_t1:20170518231611j:plain

f:id:tsubaki_t1:20170518231737g:plain

設定はレイヤー単位で、Aditive設定にしたいレイヤーのBindingをOverrideからAditiveに設定するだけで、この設定が適応されます。

f:id:tsubaki_t1:20170518231310g:plain

他の方法と問題点

よく行っていたし紹介していたアプローチは、アニメーションに親オブジェクトを設定してやる事でした。これは、この為だけにGameObjectが必要になるので正直面倒です。

f:id:tsubaki_t1:20170518230929j:plain

またApply Root Motionを使用する手もありますが、Apply Root Motoinは現在位置を更新してしまうので、必ず最終的に元の位置に戻ってくるアニメーションにしか使えません。

http://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20150308/20150308013621.gif

tsubakit1.hateblo.jp

最後にTween系やスクリプトの制御ですが、動作が機械的というか…ショボく見える事があります。以前かなりのゲームでNGUIのあるTweenを使っていたせいで特定のTweenを見ると雑に見えるというか何というか。

ただ最終目的地が動的に変わる場合はTweenがベストなアイディアですし、カーブや計算式で緩急を気持ち良くなる感じに付けると良いんじゃないかなと個人的には思います。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

【Unity】モバイル向けシェーダー見え方比較メモ

f:id:tsubaki_t1:20170515224442j:plain

ふと思いついて、モバイル向けシェーダーの見え方を比較します。自分メモ用

使用アセット

今回登場頂いたのは、Adam氏。…のLOD版

本家Adam氏は大量のMaterialを(主に目に)使用しており、変更が面倒なので、Material二つしか使ってないAdam(LOD)氏で代用。

f:id:tsubaki_t1:20170515224912j:plain

https://www.assetstore.unity3d.com/#!/content/74842

ちなみに、このLODですが、一番低い奴は顔が平べったくて笑えます。

f:id:tsubaki_t1:20170515225352j:plain

ちなみに、Graphics APIDirectXを使用しているので、モバイルの見え方とは多分一致しません。

比較

PBRだけあって、ちゃんと表示されています。

PBRベースでスペキュラや影を書き込むと、PS2PS3感のある絵になります。PBRの基本は光は全部シェーダーにやらせる事

f:id:tsubaki_t1:20170515231348j:plain

Highと比較して、少しスペキュラが薄いです。
まだまだソレナリに綺麗。ハイエンドモバイルの設定はこの辺り

f:id:tsubaki_t1:20170515232517j:plain

Mediumと比較してスペキュラが大幅に薄いです。
ローエンドモバイルは特に設定してなければこの設定…まぁ、発熱的にもっと軽いシェーダーを使うべきな気もしなくもないですが

f:id:tsubaki_t1:20170515232529j:plain

なんちゃってスペキュラ。PBRのような材質で光沢を設定するのではなく、光沢を設定する奴です。

設定をちゃんと調整すればStandard Shader(High)に近い表現になります。調整はPBRのような物ではなく、アーティストの直感に依存

f:id:tsubaki_t1:20170515231658j:plain

モバイルでのスペキュラです。綺麗さはアーティストに依存します。色や見え方を調整しなかった時は、もっとテカテカしています。
脳内偉い人が言ってた。とりあえずスペキュラ付けてBloomつけとけばハイエンドっぽく見えます。

f:id:tsubaki_t1:20170515231606j:plain

スペキュラは死ぬ。
スペキュラをテクスチャに書き込めば、まぁ見えなくはないです。

f:id:tsubaki_t1:20170515232504j:plain

凹凸も死ぬ。

低ポリのディティールアップに使用していたノーマルマップが無くなります。何となく優しい顔に。

f:id:tsubaki_t1:20170515232611j:plain

光表現が死ぬ。
凹凸も光表現も全部テクスチャに書き込むべきです。逆に光表現を書き込めば、古き良き女の子が可愛い系ゲームの表現に近くなるかもしれません。

f:id:tsubaki_t1:20170515232837j:plain

【Unity】uGUIの自動レイアウトが分かりにくいと評判なので解説してみる

uGUIには自動レイアウトなシステムが積まれています。ただ、このシステム便利ではあるんですが色々と複雑なので、少し補足です。

UIの大きさを動的にフィットさせる、オートレイアウト

UIを構築する上では、必ずしも画面や要素の大きさが一致とは限りません。画面で言えば16:9の場合もありますし9:16の場合もあります。ソレ以前に4:3の場合もあります。

f:id:tsubaki_t1:20170513215535j:plain

 

また、画面だけでなく子の要素も必ずしも固定とは限りません。分かりやすいのがフキダシのような項目で、文字の量によって大きさが違います。

単純なレイアウトの調整の場合は9スライスやアンカーで十分レイアウトの対応が可能ですが、そこに要素の変更まで加わると、ややこしい事になります。

f:id:tsubaki_t1:20170513211640j:plain

 

このフレキシブルなスケールに対応するのがオートレイアウトなシステムの便利な所で、上手く使えばコードを書かずにうまい感じにスケールを調整してくれます。

※簡単では無いですし、シンプルになる訳でも無い点に注意

UIの大きさはLayoutElementで決まる

オートレイアウトシステムの各要素のサイズは、LayoutElementに依存します。つまり、ほぼRectTransformで動作しません。むしろLayoutElementの値に従いRectTransformの値を書き換えます。pivotは除く
実際Auto Layoutの機能を使用すると、RectTransformで操作できなくなる項目が出現します。具体t系にはPosition・Width/Height・Anchorsは動かない物と思った方が良いです。

f:id:tsubaki_t1:20170514120556j:plain

ここで使用するパラメータは大きく分けて3つで、Min(Width/Heiht)Flexible(Width/Height)Perferred(Width/Height)です。

f:id:tsubaki_t1:20170513222555j:plain

またLayoutElementを使用しない場合も、ILayoutElementを継承しているコンポーネントの場合、サイズを持つ事があります。例えばTextとか、Image

なお基本的にuGUIのImageやTextはminの値を持ちません。LayoutElementを設定するか、LayoutGroupを通すと設定される項目みたいです。

f:id:tsubaki_t1:20170513222536j:plain

Minは最低限サイズ、Preferredは変化するサイズ、Flexibleは余りを割合で

  • Min□
    これ以上小さくならない幅・高さ(サイズ)
    最低限描画される事が保証される範囲
  • Preferred
    描画出来る幅・高さ(サイズ)があれば描画する
  • Flexible
    RectTransformのサイズからMinもしくはPerferredを引いた割合

例えば下のようにUIのサイズを変更すると、白(Min)は最後まで変化が無いですが、青(Flexible)が縮んだり伸びたりします。青が無くなると赤が縮み始めます。

f:id:tsubaki_t1:20170513221138g:plain

ここで混乱しやすいのが、MinとPreferredはサイズなのに対しFlexibleは割合であるという点です。
例えば下の図では、上は左右の端をPreferredで、下をFlexible Widthで設定しています。Perferredでは20ピクセルで描画されているのに対し、Flexibleの場合は14%で描画されています。*1

minとpreferredとflexbleの全てが設定されてる場合、採用されるのは一番大きなサイズみたいです。

f:id:tsubaki_t1:20170513223156j:plain

レイアウトプロパティの確認はInspectorビューで

LayoutPropertyの確認は、Inspectorビューのプレビューから確認出来ます。Valueでどの程度の値か、Sourceでレイアウトのサイズを決めているのが誰かを確認します。

f:id:tsubaki_t1:20170514120223g:plain

UIを並べるだけでなくサイズも変更するLayoutGroup

 各UI要素のサイズが確定したら、LayoutGroupでUIを並べます。これ(自分も含めて)レイアウトを並べる機能として紹介されていますが、実際にはレイアウトのサイズを変更して、並べる機能です。*2

f:id:tsubaki_t1:20170514125751j:plain

ここからがややっこしい内容です。覚悟はいいか?俺はできてる。

LayoutGroupは子のLayoutを親に伝える

LayoutGroupは子のレイアウトサイズを保持し親に伝える役割もあります。
逆を言えば、LayoutGroup(もしくはソレに類するもの)がLayoutEmelentを宣言しない場合、親にLayoutElementの要素は伝わりません。

つまり、オートレイアウトな機能を使用する場合、UIの親は必ず何らかのLayoutGroupを保持している必要があります。でないとレイアウトが破綻します。

f:id:tsubaki_t1:20170514125628j:plain

例えば下に二つの例を用意します。この二つの違いは、選択中のUI(Image)にLayoutが設定されているかどうかです。

上の画像は中央のImageにレイアウトが設定されているため、子のImageサイズを取得して親に伝えているため、最終的にUIが必要としているサイズは128です。

逆に、下の画像の場合、中央にLayoutGroupが無く子のPreferredなサイズを取得していないため、最終的なサイズは0になってしまっています。

f:id:tsubaki_t1:20170514125017j:plain

f:id:tsubaki_t1:20170514124937j:plain

ちな正確には「伝える」ではなく「親が勝手に見る」です

Control Child Sizeは子のLayoutを元に子のRectTransformを変更する

まずControl Child SizeはLayougGroupの子の要素のサイズを変更する機能です。正確には、一つ下の子が提出してきたサイズを元に子のサイズを決める機能を有効にする機能です。

 

最もコレをよく使うのが、テキストの内容を変更したケースで、文字数や文字幅に応じてウィンドウのサイズを動的に変更する事を期待出来ます。例えば下のようなケース。

  1. Textが文字列を元にPreferredの高さと幅を算出
  2. WindowのレイアウトがTextの高さと幅を取得
    Control Child Sizeを元にTextのWidthとHeightを更新
  3. ContentがWindowのpreferred widthとpreferred heightを元にwidthとheightを更新

f:id:tsubaki_t1:20170514153457j:plain

f:id:tsubaki_t1:20170514154553g:plain

Control Child SizeがFalseの場合、RectTransformのサイズをLayoutに使う

またややっこしいのが、control child sizeをfalseに設定していると、RectTransformのサイズをlayout のサイズ(しかもmin)に使用する点です。

例えば下の場合、WindowのControl child sizeは設定されておらず、RectTransformの値が使用されています。つまり下のような流れです。

  1. Windowは子のRectTransformのサイズを元に、Layoutを設定
  2. ContentはWindowのLayoutを元にWindowのWidthとHeightを設定

並べる際、設定したサイズをそのまま使える為、単純なリスト構造を作るならコチラの方が有用かもしれません。

f:id:tsubaki_t1:20170514130511j:plain

f:id:tsubaki_t1:20170514130831g:plain

Control Child Sizeによるリサイズは親より大きくならない

minが設定されている場合は別ですが、基本的にLayoutGroupで駆動するUIの大きさは一番上のLayoutGroupより大きくなりません。折り返しや省略等は親のRectTransformに従って行われます。

f:id:tsubaki_t1:20170514161230g:plain

とは言え、それでは困るケースもあります。例えばScrollView等で子要素を元に親オブジェクトを大きくしたいケースです。

ここでContent Size Fitterが登場します。

設定したオブジェクトのサイズをminもしくはPreferredのサイズに従って変更します。使えば、テキストで広がった分だけRectTrasnformが広がります。

 

逆を言えば、最後に親オブジェクトのサイズを変更したい場合以外では、Content Size FitterではなくLayoutGroupでなんとかしろというお話でもあります。

f:id:tsubaki_t1:20170514161519j:plain

Child Force ExpandはUIを引き伸ばす

Child Force Expandは説明文の通りです。

追加の利用可能なスペースを埋めるため子要素を強制的に拡大するか

レイアウトの幅が余っている場合、埋めるように拡大します。自身と子を

f:id:tsubaki_t1:20170514160317g:plain

この項目は初期値でEnableですが、基本的にDisableにしたほうが良い項目じゃないかなと個人的には思います。この項目Undo効きませんし

Vertical LayoutかHorizontal Layoutのどちらを使うべきか?…好みで

さんざんLayoutGroupと行ってきましたが、Grid Vertical Horizontalの3種類のうちどれを使えば良いかと言うと、お好みです。整形のルールは異なりますが、LayoutGroupの機能として見れば同じものなので、好みでどちらを使うべきか決めても良いかと。

 

LayoutGroupだけの機能があれば健全なんですが…

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

docs.unity3d.com

*1:割合が0.2・1・0.2なので、幅は「1 / ( 1+0.2+0.2 ) * 0.2 * 親の幅」

*2:正直、LayoutGroupだけの機能があればソレが分かりやすかった感