【Unity】画面外のターゲットを追跡するカーソル
今回は画面外に移動したプレイヤーを追跡するカーソルを作ってみます。

画面外と画面内の判定
まず、キャラクターが画面外に移動した際の判定を行います。
やり方は色々とありますが、面倒くさいのでプレイヤーのViewPortがRect(0, 0, 1, 1)内に居るかどうかで判断してみます。
Viewport座標はスクリーン座標を0~1で正規化するので、とりあえず0以下・1以上の場合は画面外と判断出来ます。

アイコンにキャラクターを追跡させる
次にアイコンにキャラクターを追跡させます。
配置すべきUIの位置はRect.NormalizedToPointで画面のサイズを取得して割り出します。その際、画面外に移動したViewport座標は、Clampで0-1の範囲に抑えます。

但しそのままだとUIが半分画面外に出てしまうので、UIのサイズだけRectのサイズを小さくして、UIがはみ出ないようにします。

追跡するアイコンにターゲットを映す
アイコンに追跡する対象を映してみます。
やり方は単純で、
まずは追跡する対象の子オブジェクトとしてカメラを持たせます。AudioSourceやGUILayer等の不要なコンポーネントは外しておくと良さそうです。また、ターゲットが映る位置に調整しておきます。
次にターゲットを表示するRenderTextureのアセットを作成しカメラに設定します。
これで、RenderTextureに常にターゲットが描画されます。
あとはアイコン側の設定です。
アイコンをImageで描画していたらRawImageに変更(一旦RemoveしてAdd Component)します。後はRawImageコンポーネントのTextureに先ほど設定したRenderTextureを設定すればOKです。
丸でくりぬきたい場合は、Maskも設定します。
なお、RenderTextureを設定したカメラのBackgroundにアルファが設定されていると透けます。RenderTextureを使用する場合は注意です。

後は対象に向けてUIを回したりする等、色々と面白い事ができるかもしれません。
画面外にあるターゲットを追跡するアイコン pic.twitter.com/RSQRfVt4fV
— 椿 (@tsubaki_t1) 2016年4月25日
関連
【Unity】AssetBundleのVariantsでロードするアセットを差し替える
今回はAssetBundleのVariantsで、リソースを差し替えてみます。
シーン/プレハブが参照するアセットを差し替えるVariants
AssetBundleのVariantsは、「AssetBundleから取得するアセットを差し替える」機能です。
通常Unityの依存関係は各アセット毎に割り振られるGUIDを使用して管理しますが、Variantsを指定した場合は、同AssetBundle名で同アセット名なアセットに参照先が差し替えられます。

例えば、ゲームのUIを構成しているスプライトやキャラクターのイメージを、ゲームをプレイする国や言語毎に差し替えたいケースがあります。
通常はスクリプトにて実行時にイメージを差し替える…といったフローが必要になるのですが、Variantsを利用する事で、リソースを読み込むAssetBundleを切り替えるだけでリソースの差し替えが完了します。
用途的には「ローカライズによるリソース差し替え」や「SD/HDの切替」、「ScriptableObject切替による難易度の調整(スクリプトでやった方が幸せになるかも)」といった物に使えそうです。
ちなみに、今回はプレハブを使用する例ですが、SceneをAssetBundleに格納した場合でも使用することができます。SD/HDの切替とかをScene単位で行うのは、結構理に適ってる感じがします。
Variantsを使用してみる
Variantを使用するには、以下の設定を行います。
- AssetBundleでVariantを設定する
- Variant内で同名のオブジェクトを持つ
- AssetBundle読み込み時に、AssetBundle名にVariantを設定する
実際にVariantsを使用してみる
実際にVariantsを使用してみます。
今回はAssetBundleからプレハブを1つ取得するサンプルを作成します。但し、取り出すプレハブにはスプライトを設定して起き、Variantsの値によって読み込むスプライトを切り替えます。

プレハブの作成
まずはプレハブを作成します。名前は適当にPlayerPrefabとかしておきます。
プレハブはPlayer.pngのイメージに対して参照を持っているだけです。
作ったプレハブは「prefab」というAssetBundle名を設定しておきます。

まだ、このままプレハブをAssetBundleから読み込むと、今まで通りプレハブに設定したスプライト画像で表示されます。
Variantの設定
次に画像のVariantを作成します。この設定で「同名のスプライトはVariant設定で差し替える」ようになります。
まずプレハブから参照しているスプライトと同名のファイルを用意します。但し殆どのOSの都合上、同じフォルダに同名のファイルは配置出来ないので、異なるフォルダに配置します。

次にリソースにAssetBundle名とVariantを設定します。Variantを設定する場合は同名のAssetBundle名じゃないと動かないです。
なお、AssetBundle名とVariant名は問答無用で小文字に変換される点にご注意を。
今回はプレハブから参照している通常の服を着たバージョンに「type-a」、青い模様を付けたバージョンに「type-b」というVariant名を設定します。

なお、ファイル毎にAssetBundle名やVariant名を設定するのは正直面倒なので、AssetBundle名やVariant名をフォルダに設定し、ファイルをフォルダに配置する方をお勧めします。
あとはAssetBundleを出力すると、character.type-aとcharacter.type-bというAssetBundleが作成されます。

「prefab」のAssetBundleにはプレハブ、「character.type-a」と「character.type-b」には、それぞれキャラクター用のスプライトが格納されています。
またprefabはcharacterに対しての依存関係を持っています。
AssetBundleを読み込む
後はAssetBundleを読み込みます。
基本的には依存先のAssetBundleと一緒にプレハブのAssetBundleを取得するだけです。character.type-aを読んでいればtype-aに格納したスプライトが、type-bを読んでいればtype-bに格納したスプライトが読み込まれます。


注意点
割と便利なVariantsですが、少しややこしい制約として「Variantsにプレハブを設定することができません」。どうもプレハブはAssetBundleをビルドした地点で参照先のプレハブも含めて結合されているらしく、こういった分割は出来ないみたいです。
バグだったみたいです。
なので、こういった事も可能になりました。
AssetBundleManager
ちなみに、AssetBundleManagerを使用する場合は、AssetBundleManager.ActiveVariantsに使用したいVariantsを設定しておけばOKです。

これでVariantsが設定されているAssetBundleを読む場合は、自動的に選択したAssetBundleを読み込んでくれるっぽいです。
なお、シミュレーターモードではVariantsは動作しないみたいです。
関連
© UTJ/UCL
【Unity】同じモデルなのに片方だけベイクした影が汚い場合の対処法

【問題】上の画像には赤い2本の棒がある。これらは同じメッシュを使用している。
これらの棒に影をベイクした際、左の赤い棒にかかる影はハッキリしているが右の棒にかかる影はボケている。このこの違いが発生する理由を記入欄にry。
【答】左の棒はスケールを変更していない物を使用し、右の棒は引き延ばしたブロックを使用しているため。

オブジェクトを引き延ばした時、Lightmapも伸びる
これを引き起こしている要因は、オブジェクトのスケールを引き延ばした時でも、ライトマップのUVは変わらないという事です。
どういう事かといえば、ScenのBakedで比較すればわかります。左(青)が左の赤い棒、右(赤)が奥の赤い棒のライトマップ解像度です。
左のライトマップは正方形ですが、右のほうは引き延ばされているのが分かります。

右のライトマップはどのようにUV展開されるのかといえば、こんな感じで展開されます。これを見てわかる通り、UV的には6面正方形を展開しようとしています。
つまり、6面正方形として展開する予定のUVを引き延ばして展開しているため、影が引き延ばされた、汚い感じで表現されているわけです。

「拡大縮小」では問題は発生しない
ちなみに、この現象は「特定軸の引き延ばし」を行ったモデルを「Baked GIでベイクした」場合に発生します。
なので、リアルタイムな影を使用した場合は発生しませんし、単純にモデルを拡大・縮小した場合、拡大したモデルの影が荒くなる…といった事も無いみたいです。

引き延ばしたLightmapを何とか良い感じにする
引き延びてしまったライトマップですが、一応は何とかする方法があります。それは、スケールを変更したLightmapを一旦メッシュとして出力し、Generate Lightmap UVsを設定する事です。

オブジェクトをメッシュとして出力する場合は、OBJ Exporterがとりあえず便利です。
これがうまく行けば、ライトマップ用のUVが新しく生成され、特定のメッシュだけライトマップが伸びるような事無く焼けます。
まぁ、メッシュを2倍持つ事になりますが、そのあたりは色々な所と相談で。


という事で、スピードレベルデザイン的な物をプリミティブな(Unityに標準で付いている)オブジェクトでやろうとするとオブジェクトの拡縮が多発する訳ですが、場合によってはライトマップが悪くなるので注意というお話でした。
めでたしめでたし。
関連
【Unity】5.3からのマルチシーン編集を前提として使用する上で注意すべき7つの項目
今日はMulti Scene Editingを利用して設計する上で注意すべき項目についてまとめてみます。他にも「こんな事ある」的な事があれば、教えてもらえれば超嬉しいです。
- 読み込んだシーンへの参照は1フレーム後に行う
- 「シーンの相互参照」は出来ない
- Scene.UnloadにはUnloadUnusedAssetsは含まれない
- オブジェクトはActiveなシーンに生成される
- Activeなシーンの切替は最小限に留める
- SceneManagerはStart以降に使用する
- Sceneは毎回取得する
- 注意書き
- 関連
読み込んだシーンへの参照は1フレーム後に行う
シーンはResourcesやAssetBundleからPrefabを取得する場合と異なり、即座にロードが完了する訳ではありません。これは、非同期(Async)ではないSceneManager.LoadSceneを使用した場合も同様です。具体的には、シーンのロードはフレームの最後辺りに実行されるみたいです。
なので、Multi Scene Editingでシーンについて編集する場合、LoadSceneを実行したシーンからロードしたシーンへ参照を行う場合、1フレーム後に行う必要があります。
これはSceneManager.sceneLoaded(5.4で追加されたシーン呼出時のコールバック)を使用した場合も同様です。
なお、もう少しイケメン的に行いたい場合は、Sceneクラスを「LoadScene後に」取得し、IsLoadedを監視すると良さそうです。
「シーンの相互参照」は出来ない
クロスシーンリファレンス(シーンの相互参照)は出来ないみたいです。Unity 5.4 b13ではGUIレベルで出来ないようになっていました。
このため、他のシーンと連携する場合、スクリプトを用いて実行時に参照を解決する必要が出てきます。

出てくるのですが、上の「シーンが読込完了するのは1フレーム後」の制約のお陰で、この相互関係も非常にややこしい話になっています。
今のところオススメとしてはサブシーンのオブジェクト群が親シーンに対してシングルトンなりFind経由で参照を行うタイプみたいです。
Scene.UnloadにはUnloadUnusedAssetsは含まれない
必要ならば自分で呼ぶ必要があります。
オブジェクトはActiveなシーンに生成される
オブジェクトはActiveなシーンへ生成されます。これは一つの点において少し厄介です。
例えばControllerオブジェクトがGameObjectを生成した場合、アクティブなシーンによって結果は異なります。

これが起こす問題の一つは「Multi Scene Editingを念頭にSceneをUnloadしてもUnloadしたシーンが生成したオブジェクトが破棄されない」点です。
例えば、ボタンを押したらシーンをアンロードするコードを作成した場合、アクティブなシーンによってはGameObject(Clone)は破棄されますし、逆に破棄されず残ります。
これの解決する方法は二種類あります。
一つはオブジェクトを指定したシーンへ移動する方法です。SceneManager.MoveGameObjectToSceneを使用すれば、指定のオブジェクトを特定のシーンへ移行させる事が出来ます。
もう一つの方法は、生成したオブジェクトへの参照を親が保持し続ける方法です。そしてUnloadでオブジェクトを破棄する際に生成したオブジェクトも破棄します。
生成したオブジェクトは大抵生成しっぱなしではなく何らかのコントロールを行う事が多いので、概ねこちらの方が良さそうな気がします。
もう一つ、Activeなシーンをどんどん入れ替える事も考えられますが、これは余りお勧めしません。
Activeなシーンの切替は最小限に留める
「Activeなシーン」は、単に太字のシーンというだけでなく特別な意味があります。それは上記で説明したような「オブジェクトを生成するシーン」というだけでなく、LighmtapやNavmesh等、シーンに紐づく設定が選択される要因でもあります。
またLightmapは「Activeなシーンを切り替えた際、切り替える以前に呼んでいたライトマップが破綻する」現象がモバイル限定で発生するらしいので、その辺りも含めてActiveの切替はあまり行わない方が良さそうに見えます。
SceneManagerはStart以降に使用する
これはバグなのか仕様なのか判断出来ていませんが、SceneManagerのAPI…例えばSceneManager.GetSceneByName等で呼び出したSceneオブジェクトは、Awakeのタイミングでは正常に動かないっぽいです。
例えばSceneManager.GetSceneByNameをAwakeで呼び出した時、エディタでMulti Scene Editingを行っている時でもロードされてない状態として扱われます。
そのため、例えば起動時にロードしていないシーンをロードする…的なコードをAwakeで実行すると、シーンが二重にロードされたりします。
Sceneは毎回取得する
Sceneですが、Load前とLoad中〜Load後で別物になっている事があります。
なので、下のようにロード前とロード後に取得した場合で挙動が異なります。

ちなみにSceneはクラスではないです(k_yanase さん指摘ありがとうございます!)
ただシーンをUnityエンジン側に問い合わせる際に使用するハンドルはロード後は同一のものを使うっぽいので、アクセス時に取得するといった事はしなくても良さそうです。
(シーンをロード(1)→アンロード→ロード(2)した場合、(1)で取得したSceneと(2)で取得したSceneは別物な点に注意。
注意書き
この項目は追記予定です。
また、情報が古くなる可能性があります。