テラシュールブログ

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

【Unity】アプリ内課金やAssetBundle、ランキングの例を含むサンプルゲーム、Endless Runner Sample Game

f:id:tsubaki_t1:20170620224446j:plain

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

少し前に、Unity公式の完成プロジェクト「Endless Runner Sample Game」が公開されてました。

Endless Runner Sample Gameといふゲーム

Endless Runner Sample Gameは、モバイルで良く見たエンドレスラン系のサンプルみたいです。

ゲームを開始すると、割と良い感じのアニメーション(猫がねずみを追い回す)が始まり、キャラクターセレクトっぽい画面、そしてエンドレスランのゲーム画面に遷移します。

f:id:tsubaki_t1:20170620225402j:plain

割と作り込んでいて、IAPやランキングの機能が含まれてるっぽいです。

 

 

ダウンロードはこちら:https://www.assetstore.unity3d.com/#!/content/87901

AssetBundleでキャラクター・ステージの切替

このゲーム、キャラクターの読込はAssetBundleで行われており、多分自分で追加したりする事も出来るんじゃないかなと。

f:id:tsubaki_t1:20170620230450j:plain

ただAssetBundleの分割は割と適当で、重複アセットありまくりです。

まぁ複数のキャラクターを同時に読む事は無いので問題は無いのですが、キャラクター切替等にアセット再読込が走るので、割とロードは間抜けです。

(黄色い▲が付いてるアセットが他のAssetBundleと重複して読み込まれる可能性のあるアセット)

f:id:tsubaki_t1:20170620231647j:plain

ちなみにAssetBundleにキャラクターが格納されているので、AssetBundle ManagerのSimulater ModeでAssetBundleの振る舞いをシュミレーションするか、Local AssetBundle Serverでローカルサーバー立ててAssetBundleをダウンロードする形にしないと、動作しません。

f:id:tsubaki_t1:20170620232002j:plain

多分ステージもAssetBundleです。

昼と夜のデータを持つと思われるScriptableObjectがAssetBundleに格納されています。中にはオブジェクトの生成パターンや、空・フォグの色等が格納されていました。

f:id:tsubaki_t1:20170620235031j:plain

Shaderで色々な画面効果

このプロジェクト、結構シェーダーを使いまくっています。

例えばオブジェクトの回転は頂点シェーダーでぶん回していますし、

f:id:tsubaki_t1:20170620230809g:plain

遠方を表示しない(地球の表面のように歪んで見える)もシェーダーでやっています。湾曲率はShader.SetGlobalFloatで一括制御。クール

f:id:tsubaki_t1:20170620231028j:plain

また幾つかの箇所は頂点カラーで色を塗っているっぽいです。

ちゃんと見てないですが、下の壁とかは「頂点カラーでレンガの色を塗る」「レンガの模様はテクスチャを繰り返して実現」といった感じでしょう。多分

f:id:tsubaki_t1:20170620231315j:plain

Animatorは割とシンプル

Animatorは割とシンプルです。

基本的に右側のジャンプ・スライディング・ランはBOOLで管理しています。これはボタンを連打した時もトリガーがつけっぱなしにならない為でしょう。

HITが呼ばれたらANY STATEからHITが呼ばれて、HPが無ければDEATHに移行する等。

f:id:tsubaki_t1:20170620232740j:plain

基本的に RUN LOOPで走りますが、ジャンプすると着地後 run startから再スタートする点、スライディングは速やかにrun loopに戻る点は中々面白いです。

ちなみにRun LoopがBlend Treeで作られてる辺りに、努力の跡を感じます。

f:id:tsubaki_t1:20170620233026j:plain

その他諸々

オーディオは普通に音を鳴らしたり、UIから直接呼び出したりしてました。

f:id:tsubaki_t1:20170620233707j:plain

UIはソコソコちゃんとレイアウトシステム使って組んでます。

f:id:tsubaki_t1:20170620233957j:plain

感想

割と中々悪くないんじゃないでしょうか。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

【Unity】ScriptableObjectを使用した、コンポーネント・シーン間でのデータ共有について

今回はScriptableObjectを使用したComponentやScene間でのデータ共有についてです。なお、この記事は30回近く書き直した*1という曰く付きの記事で、場合によっては消えるかもしれません。

ScriptableObject

ScriptableObjectはScene・Prefab以外のUnityエディターで値を調整する事が可能なオブジェクトです。Unityエディター上で値を調整する場合、ScriptableObjectやScene・Prefabを使用すると割と簡単に調整、値の利用が可能です。

ソレ以外の場合は、自前のシリアライザを用意する必要があります。

ScriptableObjectはアセットでありインスタンスである

ScriptableObjectのアセットは「アセット」ですが同時に「インスタンス」でもあります。この特徴は少し面白く、例えば複数のPrefabやSceneから参照された場合でも、勝手に複製されず単一のインスタンスとして保持されます。

 

例えば、Scene AとScene Bという二つのシーンがあり、それぞれOBJ 1とOBJ 2というオブジェクトを持っているとします。
ここでOBJ 1とOBJ2が同じScriptableObjectを参照していた場合、OBJ1が参照しているScriptableObjectとOBJ2が参照しているScriptableObjectは同一のものです。

f:id:tsubaki_t1:20170619214942j:plain

この仕組みを利用して別SceneのAudio SourceとButtonのEventTriggerを接続したのが、以前紹介した「ボタンを押して音を鳴らす方法」でした。
このアプローチでは、FindやStatic等は一切使用しておらず、動的にコンポーネントコンポーネントを紐付けています。

https://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20170614/20170614000458.jpg

tsubakit1.hateblo.jp

 

二つのオブジェクトでデータを共有する

実際にデータを共有してみます。

f:id:tsubaki_t1:20170619220901j:plain

まずは赤と青の二つのText、そして白のButtonを用意しました。動作としては

  • 赤と青のTextはScriptableObject内のカウントを監視し、テキストに表示
  • 「ADD」Buttonを押すと、ScriptableObjectのカウントが増える

という単純な物です。うまくいくと、ADDボタンを押すたびに赤と青の両方のカウントが1ずつ増えます。

 

なお今回紹介する内容は、その気になればstaticやsingletonで代用出来ると思います。強いて違いを上げるとするならば

  • Findやstaticを使用せずオブジェクトを参照出来る
  • エディターで値が調整出来る
  • インスタンスの破棄が出来る
  • UnityEventベースで色々出来る

辺りでしょうか。

実際にScriptableObjectにデータを格納して共有する

まずはScriptableObjectと、ScriptableObjectを監視してTextを更新するコンポーネントを作ります。

gist.github.com

作成したコードから、ScriptableObjectのアセットを作成しておきます。

f:id:tsubaki_t1:20170619221649j:plain

 

後はClickCountUpdaterのコードに作成したClickCountを設定、設定した物を赤と青のTextオブジェクトに貼り付けます。

先にコードに登録しておくと、コンポーネントを登録するときに自動的にScriptableObjectの参照が完了するので、少し楽です。

f:id:tsubaki_t1:20170619222128j:plain

 

tsubakit1.hateblo.jp

 

後はClickCountのAdd()メソッドをButtonのEvent等から呼び出してやれば、Buttonから赤と青のTextの数字を更新出来ます。

f:id:tsubaki_t1:20170619222358j:plain

f:id:tsubaki_t1:20170619220617g:plain

 

このアプローチでは、オブジェクトは生成した瞬間にScriptableObjectを通して参照を構築します。なので、別々にインスタンス化しても問題なく動作します。

下の図では、青いTextとButton、赤いTextの順番に追加しています。特に参照を行うコードは無いですが、問題なく動作しています。

f:id:tsubaki_t1:20170620001246g:plain

ResourcesからLoadしたインスタンスは一つ、ただAssetBundleは注意

今回のサンプルでは依存関係をエディターで設定しましたが、Resourcesから取り出した場合も同様に単一のインスタンスとして扱われます。

例えば下の青と赤のテキストは個別にResources.LoadでScriptableObjectを取得しますが、値が同じです。Resourcesから取り出したインスタンスは、破棄されない限り同一だと分かります。

gist.github.com

f:id:tsubaki_t1:20170620002743g:plain

AssetBundleの場合も基本的には同じですが、暗黙的参照でインスタンスを参照していた場合、異なるアセットとして扱われるかもしれません。複数のAssetBundleをまたぎそうな場合は、別AssetBundleに明示的に格納しておくことが良さそうです。

Find Reference In SceneでScriptableObjectを参照するオブジェクトを見つけ出す

シーンでScriptableObjectへの参照を持つアセットを探したい場合、Find Reference In Sceneで見つけられます。

ただPrefab化したオブジェクトが参照していた場合、Prefabに含まれる全てのオブジェクトがヒットしてしまうので使い所が難しいです。

f:id:tsubaki_t1:20170620001933g:plain

エディターでアセットのインスタンスに書き込むと、ファイルも更新される問題

さて、アセットのインスタンスを用いてデータを共有しましたが、実はこの方法は一つ問題があります。アセットの値を更新するという事は、ファイルも更新されるという点です。言い換えれば、Sceneの再生を終了しても数値は元に戻りません。

 

下の例では、シーンを一度停止しているにも関わらず、再度再生すると途中から数値の加算が行われています。プレイヤーでは問題はないのですが、エディターだと問題になります。

f:id:tsubaki_t1:20170619222818g:plain

案1:Inspectorで使用する初期値と実際の値を分ける

この問題を回避する方法で一番まともなのは、ScriptableObjectにInspector表示用の値と実際にゲームで使用する値の二種類を用意し、OnEnableのタイミングで値を流し込む事です。

gist.github.com

またOnValidateを定義しておくことで、再生中にもInspectorから値を書き換えられるようになっています。

案2:再生停止時にScriptableObjectをUnloadする

案2はScriptableObjectがアセットでありインスタンスである事を利用します。ゲームの再生停止時にScriptableObjectをUnloadしてしまえば、再生中に変更した値は無かったことになり、ScriptableObjectの値がファイルに書き込まれる事は無いという事です。

 

まずはResettableScriptableObjectをプロジェクトに配置します。これはシーンの再生停止時に自身のインスタンスをアンロードする…という挙動を持ったScriptableObjectの派生クラスです。

これを継承する事で、シーン再生時にScriptableObject値が最後にProject Saveした際の値まで巻き戻ります。

gist.github.com

要するに、もし値を確定させたい場合は、Project Saveを実行しないと、巻戻ります。これはResettableScriptableObjectコメントアウトしてる部分を戻せば回避出来るのですが、どうもUnityエディターを巻き込んでクラッシュする事がある(条件は不明)ので、Project Save推奨です。

f:id:tsubaki_t1:20170619224839g:plain

ScriptableObjectの保持範囲は、アセットが開放されるまで

少し気になるのが、ScriptableObjectを保持する範囲です。

これは単純に「ScriptableObjectのアセットが開放されるまで」となります。

例えば、シーンをロードした際に、次のシーンでも同一インスタンスのScriptableObjectを参照していた場合、ScriptableObjectの値は保持されます。

またUnloadUnusedAssetsを呼ばれた際に、どこからかScriptableObjectへの参照がある場合、同様にScriptableObjectの値は保持されます。

f:id:tsubaki_t1:20170619230259j:plain

なお、エディターだとシーンを跨いだ場合でもScriptableObjectが開放されない事があります

アセットを勝手に開放させない方法

シーンを跨いだり、どこからも参照されない状態でUnloadUnusedAssetsを呼ばれるとアセットが開放される事があります。

アセットを開放させない方法は、思いつく限り3つあります。

  • 何処からか常に参照する。例えばstaticにでも登録してやれば開放されない。
  • PreLoadAssetsに登録する。挙動的にどうも開放されないらしい
  • HideFlags.DontUnloadUnusedAssetを設定す

積極的なアセットの開放は、Resources.Unload。しかしC#インスタンスが開放される訳ではない

もし値を積極的にリセットしたい場合、シーンをロードする直前でResources.Unloadでインスタンスを開放してやると、次のシーンでマッサラな(アセットからデシリアライズしたての)ScriptableObjectを受け取れます。

 

例えば下の図では、二つのシーンを行き交っています。この際、連続してScriptableObjectを参照しているため、二つのシーン間で値を保持していますが、Unloadを押すとScriptableObjectが開放され、次のシーンでは値は0に巻き戻っています。

f:id:tsubaki_t1:20170619231515g:plain

この時、C#のScriptableObjectインスタンスが開放される訳では無い点には注意が必要です。ScriptableObjectは一旦破棄されますが、ScriptableObjectがデシリアライズして生成されたC#インスタンスはそのまま保持されます。

つまり、EventやC#的に直接参照している物は、Unload後もアクセス出来る事を意味します。

下の図では、左がButtonからイベント呼出、右はスクリプトC#)がScriptableObjectへの参照をキャッシュして呼び出しています。左はアンロード時に押せなくなりますが、右はアンロード後も押せます。f:id:tsubaki_t1:20170619233052g:plain

追記:ScriptableObjectをCreateInstanceすべきか、クラスを使うべきか

今回ScriptableObjectについて書きましたが、こうなると動的なクラス生成の運用でも、単純なクラスの使用は止めてScriptableObjectを使用すべきか考えるかもしれません。

個人的な考えでは、

  • エディターでパラメータを修正するようなケース
  • ホットリロードを行うようなケース

以外では、動的なインスタンスは通常のクラスを使用する方が良いのではないかなと思います。

逆に、エディターで既にインスタンスを作成しており、複数シーンやオブジェクトで共有するようなケースの場合、ScriptableObjectはかなり良いです。

感想

ScriptableObjectを用いたデータの共有でした。

アンロード(アセットの開放)周りが少しややこしくなるので、面倒なら避けるのは正しい選択かもしれません。

 

強いていうならば、ボタンを押したら音が出る…を、出来る限りコードを書かずに実現する で紹介したような、オブジェクト間の接続では割と有益かもしれません。このアプローチでは「送信先・送信元」のどちらも存在する場合に使われるので、

  • 意図的にUnloadしない限りScriptableObjectはアンロードされない
  • どちらも無い場合はアンロードされる
  • 再ロード時も参照関係しか保持しないのでパラメータが失われる等の問題は無い

と、悪くないです。

 

もしくはデータはシーン内のGameObjectが持ち、メッセージのやり取りのみを行わせる等でしょうか。

関連

www.slideshare.net

tsubakit1.hateblo.jp

今回の話を考えたきっかけ

www.urablog.xyz

 
 

*1:(同タイトルの下書き記事も4つある)

【Unity】Timeline × Cinemachine でカットシーンのカメラワークを作る

f:id:tsubaki_t1:20170615220517g:plain

今回はCinemachine を使用してカメラワークを設定してみます。

Cinemachine

Cinemachineは「指定したオブジェクトがカメラの何処に映ってほしいのか」と「複数カメラ座標・設定のブレンド」機能を持ったカメラリグです。

tsubakit1.hateblo.jp

例えば下の図だと、黄色いドットが被写体の位置、青で指定してる範囲が被写体が映る位置です。このカメラ設定だと、被写体がどのように動いても右下に映るように設定されています。

f:id:tsubaki_t1:20170615221152j:plain

またカメラの位置や設定(Vertual Camera)を設定・ブレンドする事で、複数のカメラを切り替える事が出来ます。

下の図の場合、1カメラから2カメラへ、2カメラから3カメラへ…といった形でカメラを切り替えています。また、切り替える際、二つのカメラをブレンドする形で切り替える事も出来ます。

f:id:tsubaki_t1:20170615221410j:plain

 

TimelineとCinemachineを使ってみる

Timelineと組み合わせてみます。

Cinemacine TrackCinemachine Brainを追加してCameraの設定

まずはCinemachine.Timeline > Cinemacine Trackでトラックを追加します。Trackが要求する物はCinemachine Brain(Cinemachineのカメラ制御するコンポーネント)なので、無ければ追加します。

f:id:tsubaki_t1:20170615221616j:plain

f:id:tsubaki_t1:20170615221805j:plain

Cinemachine vertual cameraはカメラの座標と設定

Cinemachine vertual camera(カメラの座標や向き、FOVやLookat等の設定)を使って、実際のカメラの位置を調整します。

この機能を使用するとScene上で設定したカメラ位置は無視される点に注意して下さい。

 

 Cinemacine Trackのシーケンス調整する所を右クリックして、Add Cinemachine Shot Clipを追加します。

追加されたらVirtual Cameraを設定しますが、無ければCreateで作成します。Virtual Cameraはショット(カメラワーク)の数だけ作る感じです。

f:id:tsubaki_t1:20170615222222j:plain

f:id:tsubaki_t1:20170615222248j:plain

位置には「CM」が表示されるので、分かりやすいです。

f:id:tsubaki_t1:20170615222541j:plain

カメラのブレンドはTimelineで

カメラをブレンドします。

二つのシーケンスを混ぜればOKです。ドラッグ&ドロップで重なるように移動すれば、ブレンドする設定になります。

重なる幅が広ければ広いほどゆるやかに、狭ければ狭いほど急激に変化します。全くブレンドしたくない場合は、重ならないようにすれば一瞬で切り替わります。

f:id:tsubaki_t1:20170615222722j:plain

f:id:tsubaki_t1:20170615223537g:plain

カメラが見る場所はLookAtとAim

カメラが見るべき場所を指定します。Look Atに見たいオブジェクトを指定、AimをComposerに設定します。

f:id:tsubaki_t1:20170615223739j:plain

後はカメラが映すべき場所に範囲を指定します。枠はGameViewをドラッグすれば、大きくしたり小さくしたり出来ます。

また映すべき対象の中央をドラッグすれば、位置をズラす事が出来るので、画面左端や右端に被写体を写したい場合は、これで動かします。

f:id:tsubaki_t1:20170615223659g:plain

注意すべきは、この操作を行うためにはTimelineで指定のカメラを使用しているかつTimelineでシーケンスを選択している必要があります。

つまり、下のような状態です。上はP4を選択中かつTimelineでP4を再生中なので、編集が可能です。下はP4を選択していますがTimelineがP4を再生する位置に居ないので、編集出来ません。

f:id:tsubaki_t1:20170615224235j:plain

Cinemachineの入手

Cinemachineの入手ですが、現在はフォーラムで公開されています。

将来的にはAssetStoreに移動したり、Unity Engineに統合されるかもしれません。

https://forum.unity3d.com/threads/cinemachine-v2-0-final-release-candidate-loads-of-new-features.476510/

 

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

【Unity】逆引き自動レイアウトのトレーニング。UIをLayoutGroupで並べる

f:id:tsubaki_t1:20170615020246j:plain

今回は自動レイアウトの動作を把握するために、色々とトレーニングしてみました。

自動レイアウトでUIを並べる

UIの要素を並べる場合、HorizontalLayout GroupやVertical Layout Groupといったレイアウト調整機能を使用するのが便利です。

ただ、この動作は割とわかりにくいと評判(主観)です。なので、下の記事である程度の解説を行いましたが、今回は実際に色々と並べてみようと思います。

tsubakit1.hateblo.jp

なお、今回のレイアウトシステムは全てHorizontalLayoutを使用しますが、動作的にはVertical Layoutも似たようなものです。

UIのサイズが固定、もしくは親に合わせて子のサイズが変動する場合は、簡単

UIのサイズが固定だったり、親のサイズに合わせて子のサイズが変動するような場合、並べるのは割と楽です。

Imageを並べる(左から詰める)

単純にSpriteを並べる場合です。並べる際には、左から順番に詰めるように並べます。

f:id:tsubaki_t1:20170614233730g:plain

  1. 適当にCanvasを作る。
  2. CanvasにHorizontalLayoutGroupを設定する。
    その際、Control Child SizeやChild Force Expandは外す
    Child AlignmentはMiddle Left(左の中央)を設定
  3. Image付きSpriteを作る(×沢山)

f:id:tsubaki_t1:20170614234007j:plain

これで、左からUIの大きさに合わせて詰めて並べる挙動になります。生成するImageの大きさ調整を行う設定になっていないので、レイアウトによっては画面外にUIが飛び出したり、逆に余白が生まれるといった事になるかもしれません。

なお、この設定ではRectTransformのサイズを見ます。つまり、UIの大きさによって詰め具合が変わります。

f:id:tsubaki_t1:20170614234810g:plain

また各Imageにスペースが欲しい場合は、Spacingを少し増やします。

 

Imageを並べる(等間隔)

f:id:tsubaki_t1:20170614235312g:plain

UIを等間隔に生成します。

  1. 適当にCanvasを作る。
  2. CanvasにHorizontalLayoutGroupを設定する。
    その際、Child Force Expandにチェックを入れる
    Child AlignmentはMiddle Center(中央)を設定
  3. Image付きSpriteを作る(×沢山)

Child Force Expandにチェックが入っている(初期設定)ので、等間隔に並びます。入り切らない場合、枠の外にはみ出ます。Control Child Sizeにチェックが入っていないので、Imageの大きさは変化しません。

f:id:tsubaki_t1:20170614235645j:plain

高さにフィットしたUIを並べる

f:id:tsubaki_t1:20170615000315g:plain

レイアウトは必ずしも固定とは限りません。
例えばAndroid端末のアスペクト比はカオスですし、iPadはアプリを分割して起動出来ますし、Androidもアプリをウィンドウモードで起動できます。

ので、UIの高さもある程度フレキシブルな事が望ましいといえば望ましいです。

  1. 適当にCanvasを作る。
  2. CanvasにHorizontalLayoutGroupを設定する。
    その際、Child Force ExpandとControl Child SizeのY軸にチェックを入れる
    Child AlignmentはMiddle Left(中央左)を設定
  3. Image付きSpriteを作る(×沢山)

f:id:tsubaki_t1:20170615000759j:plain

Y軸はUIの大きさと一致するレベルで広がりますが、X軸のサイズはRectTransformのサイズで動きます。

 

なお、上下に余白を付けたい場合はPaddingを設定します。

f:id:tsubaki_t1:20170615000941j:plain

 

大きさが固定なUIを並べる

大きさが固定なUI(幾つかのImageを組み合わせたもの)を並べます。

f:id:tsubaki_t1:20170615001522g:plain

まず下のようなUIを作成します。割とごく普通の、よくある構成のUIです。RectTransformの下にImageが3つ。

f:id:tsubaki_t1:20170615001819j:plain

後は「Imageを並べる(左から詰める)」と同じ要領です。

 

  • 適当にCanvasを作る。
  • CanvasにHorizontalLayoutGroupを設定する。
    その際、Control Child SizeやChild Force Expandは外す
    Child AlignmentはMiddle Left(左の中央)を設定
  • UIを作る(×沢山)

上記の設定ではRectTransformの設定でオブジェクトが並べられるので、レイアウトはシンプルに並びます。

勿論、大きさを変更することも可能です。

f:id:tsubaki_t1:20170615212141g:plain

子の大きさを元にUIのサイズが変動する場合は、面倒くさい

逆に子の大きさが固定ではなく、子の大きさに従ってUIのサイズが決まる場合、割と面倒な事になります。具体的には、UIの大きさを自動レイアウトで調整する必要性が出てきます。

子のUIの大きさが変動するUIを並べる(子のmin=Perferred)

「子のUIの大きさが変動するUI」を並べてみます。まずはmin = Perferred…つまり大きさの基準がImageの場合です。

f:id:tsubaki_t1:20170615005624g:plain

この動作では、UIの大きさを変更してしまえば「大きさが固定なUIを並べる」と同じように設定出来るのですが、それが難しい場合はLayout Groupに大きさを調整してもらいます。

  1. 適当にCanvasを作る。
  2. CanvasにHorizontalLayoutGroupを設定する。
    その際、Control Child Sizeにチェックを入れ、Child Force Expandのチェックは外す
    Child AlignmentはMiddle Left(左の中央)を設定
  3. UIを作り、UIのトップはHorizontalLayoutGroupを設定。Control Child Sizeにチェックを入れる
  4. UIの次の階層にHorizontalLayoutGroupを設定。Control Child Sizeにチェックを入れない。4の子にImageを作成。適当な大きさにする

f:id:tsubaki_t1:20170615010723g:plain

Control Child Sizeが入っていると、子のPerferredのサイズを元にRectTransformを変更します。

子から親へPerferredの値にPaddingを足してPerferredのサイズを調整、親から子にPerferredの値を元にRectTransformを調整しています。なので、最後(大きさの変わるUI)の親にはControl Child Sizeにチェックが入っていません。

子のUIの大きさが変動するUIを並べる(子のmin≠Perferred)

次にlayout elementのminとperferredが一致しない場合です。

f:id:tsubaki_t1:20170615004934g:plain

具体的に言えばTextです。Textはperferredは文字が全て入るサイズになりますが、minの大きさは0です。またRectTransformとperferredのサイズが一致しない点もあります。

 

  1. 適当にCanvasを作る。
  2. Canvasの子を作成
  3. 2のオブジェクトにHorizontalLayoutGroupを設定する。
    その際、Control Child Sizeにチェックを入れ、Child Force Expandのチェックは外す。
    Child AlignmentはMiddle Left(左の中央)を設定
  4. UIを作り、UIのトップはHorizontalLayoutGroupを設定。Control Child Sizeにチェックを入れる
  5. UIの次の階層にHorizontalLayoutGroupを設定。Control Child Sizeにチェックを入れる。
  6. 5の子にTextを作成

f:id:tsubaki_t1:20170615012051g:plain

「子のUIの大きさが変動するUIを並べる(子のmin=Perferred)」と異なり、UIの大きさはTextが生成するPerferredの値を元にレイアウトを調整します。なので、下から二番目のLayoutGroupもControl Child Sizeにチェックが入っています。

 

ただ忘れてはいけないのがminのサイズは0という事です。なので、範囲内にUIが入り切らなかった場合、UIの大きさが変動してしまいます。

f:id:tsubaki_t1:20170615013256g:plain

これを回避するアイディアで思いつくのは二つ。

一つはContent Size FitterでPerferredと同じ大きさに一番親のオブジェクト(今回で言うならCanvasの下のRoot)のサイズを変更してしまう事。これで「入り切らなくてUIが縮まる」事は無くなります。なにせ、足りなくなったら親が大きくなる訳ですから。

もう一つが、minのサイズをPerferredのサイズにしてしまう事。これでminが0では無いので圧縮されることはなくなります。

gist.github.com

f:id:tsubaki_t1:20170615014251j:plain

これでオブジェクトが隅っこに到達しても、UIが潰れる事は無くなりました。

f:id:tsubaki_t1:20170615014759g:plain

 

感想

自動レイアウトの復習でした。

とりあえず「任意のサイズのUIを並べる」だけなら簡単なんですが、Textのように子のサイズを元にレイアウトを決める場合、面倒なことになります。

印象としては下な感じです。

  • 子から親にサイズ情報を渡し、それを元に親レイアウトは自身のサイズを決める
  • 親レイアウトグループが子のRectTransformを調整する

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp