テラシュールブログ

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

【Unity】AR Foundation(ARKitとARCoreのマルチプラットフォームAR環境)の超入門

今回はARCoreとAR Kitの両方に対応したマルチプラットフォームAR環境である「AR Foundation」、その最初の一歩として「とりあえず動くもの」の作り方を紹介します。

AR Foundationとは?

マルチプラットフォームAR環境です。現状はAR CoreとAR Kitの両方ですが、今後は他のものが増えるかもしれません。

blogs.unity3d.com

かなり勘違いしやすいのは、ARCore SDK for UnityAR Kit Pluginをまとめたものではない という点です。

AR Foundationは、ARKit XR Plugin(com.unity.xr.arkit)ARCore XR Plugin (com.unity.xr.arcore)をまとめたものです。
どちらも最終的にはARKit SDKとARCore SDKは使用しているのですが、C#側で使用するAPIが若干異なるので、そのあたりに注意です。また、AR Kit PluginやARCore SDK for Unity等の、それぞれ特化したプラットフォームと比較して機能が若干貧弱です。

平面検知は普通に使えるので、それが欲しいなら割と良いです。

なお、現状「ビルドしないと動作しない」です。他のAR Kit PluginsやARCore SDK for Unityが持っているような、スマホを接続してプレビューする的な機能は使えません。ロードマップによるとUnity 2019.1+です。 そういった点で言えば、Vuforia は楽で良いですね。対応するスマホも不要ですし。

f:id:tsubaki_t1:20181121204720j:plain

使い方

Package ManagerでAR FoundationARCore XR PluginARKit XR Pluginをインポートします。

あとPlayerSettingsiOSRequest ARKitSupportAndroidARCore Supportedを入れる必要があるかもしれません。

f:id:tsubaki_t1:20181121202521j:plain

ARのセットアップ

セットアップは非常に簡単です。

  1. Main Cameraを削除して、代わりにAR Cameraを導入します。
  2. メニューアイテムのGameObject > XR > AR Aession Originと、GameObject > XR > AR Sessionを選択します。

f:id:tsubaki_t1:20181121203130j:plain

現状こんな感じ。

f:id:tsubaki_t1:20181121203335j:plain

平面の検出をグラフィカルにする

下のような感じで、判定した平面の検出をグラフィカルに表示します。

f:id:tsubaki_t1:20181121203542j:plain

  1. メニューアイテムの GameObject > XR > AR Default Planeを選択して、AR Default Planeを作成、それをPrefab化します。
  2. AR Session OriginオブジェクトにAr Plane Managerコンポーネントを追加します。
  3. AR Plane ManagerコンポーネントPlane Prefabに、1で作成したAR Default Planeをセットします。

これでカメラを地面に写した時、黒い枠が表示されるようになります。

f:id:tsubaki_t1:20181121203857g:plain

f:id:tsubaki_t1:20181121213515j:plain

オブジェクトをAR空間に配置する

AR空間に配置するのには、少しだけコードを使用します。

タップした位置を把握するのは origin.Raycast(Input.GetTouch(0).position, hitResultsです。あとは取得した位置にオブジェクトを作るだけです。
下のコードはARSessionOriginコンポーネントと同じオブジェクトに登録します。

gist.github.com

あとはタップで生成したいPrefabをSpawnObjectprefabに登録すれば準備完了です。

f:id:tsubaki_t1:20181121205432j:plain

なおPrefabが大きすぎると最初からめり込んだ状態で生成され、生成されていないように見えるので、注意です。

完成

ここまで調べる時間を除けば、ほとんど数分で達成できるのは中々良いです。リモートが無いので作る手軽さやワークショップ等ではVuforiaの方が正直楽なんですが、まぁかなり良いんじゃないでしょうか。

f:id:tsubaki_t1:20181121210315g:plain

あとは周辺の環境と馴染ませたいなら、https://github.com/Unity-Technologies/arfoundation-samples/blob/master/Assets/Scripts/LightEstimation.csとか使うと、結構良い感じにマッチします。

f:id:tsubaki_t1:20181121210545g:plain

試した環境:2018.3b9

関連

サンプル一覧

AR Foundationのサンプル集です。

github.com


AR Foundationのマニュアルです。撮影したカメラの画像とか、一部で需要があるんじゃないかしら
撮影結果をキューブマップに流し込んだり

About AR Foundation | Package Manager UI website


www.youtube.com

【Unity】Multi-Scene Physicsで、物理演算の世界を分割する

Unity 2018.3の目玉機能(???)の一つである物理演算周りの更新、その一つであるMulti-Scene Physicsについてです。

Multi-Scene Physics

Unityの物理演算では、基本的に一つの空間にColliderやRigidbodyを配置し、それを運用していました。配置したRigidbodyははスリープしない限り動作していましたし、Raycastで特定のオブジェクトを無視するにはLayerによるフィルターが必要でした。

Multi-Scene Physicsの対応により、Unityは単一のゲーム空間内に複数の物理演算の空間を持てるようになりました。

何が出来るのかって? ソレを説明しましょう。

PhysicsScene単位で物理演算の速度を変化させる

まず最初に紹介したいのは、物理演算の再生をPhysicsScene単位で行えるということです。PhysicsScene.Simulate(deltatime)で時間を進められるので、特定のオブジェクトのみをスローにしたり、逆に高速再生して物理演算の結果を予測する等を行うことが出来ます。

tsubakit1.hateblo.jp

その他、屋内の物理演算の進行をドアを開けるまで止める賑やかし用の物理演算の精度を雑にする等、色々と時間に関わるマジックを使用できそうな感じがあります。

なお、接触判定は同じPhysicsScene同士でしか行なえません
例えばスロー再生するためにPhysicsSceneを移動した場合、スロー再生するためのPhysicsSceneにも通常のシーンと同じ当たり判定がないと、壁を貫通します。
さて、そうなると使い所がかなり限定されry

PhysicsScene単位でRaycast

もう一つはRaycastPhysicsScene単位で行えます。

例えば判定用Colliderが重なっている時、特定のPhysicsSceneColliderのチェックのみを行う等ができそうです。 こうった場合、普通はLayerを設定してマスクをかけるのですが、PhysicsScene単位で出来るので色々と便利そうに見えます。

なお使えるのはRaycastのみです。

使い方

このPhysicsSceneはScene単位で制御されています。

SceneManagerからSceneをロードする際、第二引数にnew LoadSceneParameters(LoadSceneMode.Additive, LocalPhysicsMode.Physics3D)をセットしておきます。意味は加算ロードかつ、3D用PhysicsSceneの追加…です。 もし通常の物理演算として登録したい場合は、LocalPhysicsMode.Noneです。

あとはscene.GetPhysicsScene()SceneManagerを取得し、physicsScene.Simulate(deltaTime)physicsScene.Raycast等を実行する感じです。 なおLocalPhysicsMode.None以外で取得したSceneは、Auto Simulationが有効でも物理演算をシミュレーションしません。時間はphysicsScene.Simulate(deltaTime)を使用して進行させます。

基本的にInspectorでシーンを展開してると使えないので(ロードした時のみLocalPhysicsModeを指定出来るっぽい?)この機能を使う場合にはシーンは閉じておく必要があります。

f:id:tsubaki_t1:20181119214405j:plain

gist.github.com

加算ロードではなく任意のシーンを作って使用したい場合は、new CreateSceneParameters(LocalPhysicsMode.Physics3D)をつけてシーンを作成し、あとはSceneManager.MoveGameObjectToScene(moveGameObject, scene)等で作ったシーンにGameObjectを移動させます。
なお新しく作ったシーンにはColliderが無いでしょうから、ColliderのGameObjectを移植等をする必要がありそうです。

gist.github.com

面白い機能だね!どんな時に使えるの?

ううーん…キャラクターのような物理演算とかけ離れた物を別ワールドに持ってっちゃうとか、もしくは軌道計算…? ううーん…?

<文章はここで途切れている…>

関連

blogs.unity3d.com

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

【Unity】AssetBundleからモデルをロードする際の最適化ポイント

ゲームでキャラクターをロードしたりする際、画面がカクつくというのは割とよく見る光景です。今回はその辺りの対処法についてメモしておきます。

Async Upload Pipeline(非同期アップロードパイプライン)の活用

ロード時にカクつく要因の一つは、GPUへのアップロードです。これは一度に全てをアップロードするのではなく、分割でアップロード(タイムスライス)することで回避することが出来ます。使用出来るのは以下の設定項目です。

  • 読み込み/書き込み有効無効なテクスチャ(及びミップマップ)
  • 読み込み/書き込み有効無効かつ、メッシュ圧縮オフなモデル
    (※Unity 2018.3以降)

つまりアセットでの設定をしておけば、あとは裏で勝手によしなにしてくれます。 ただしインポート直後のモデル等は大抵ReadWriteがEnableなので、その辺り注意です。ユニティちゃんのモデルもReadWriteが付いてます。

f:id:tsubaki_t1:20181117221858j:plain

f:id:tsubaki_t1:20181117222124j:plain

また使用する上でのパフォーマンスについても、ある程度の調整が効きます。

  • 非同期アップロードタイムスライスの値
    小さいとロードが伸びるがスムーズ
    大きいとロードが短縮されるが処理落ちを起こす
  • 非同期アップロードバッファサイズの値
    一回のロードで扱えるサイズ。小さいと一度に処理できる量が減る

Unity 2018.3以前は、使用するデータの小ささもあったのか「分割数多め」の設定でしたが、Unity 2018.3から非同期アップロードバッファサイズを大きめに設定しています。この設定によって、ロードが倍くらい早くなるという話もあるそうです。

これらの設定は 編集 > 設定 > Quality で確認することが出来る他、スクリプトからも設定することが出来ます。

f:id:tsubaki_t1:20181117215157j:plain

Async Upload Pipelineについては下の記事が正しいです。

blogs.unity3d.com

シェーダーのウォーミングアップ

モデルを表示する際に毎回ガッツリ止めてくれる要因の一つに、シェーダーのパースがあります。処理で言えば、Shader.ParseShader.CreateGPUProgramといったものがそうです。 特にモバイルだとガツガツと止めてきます。

これを回避する為のアイディアは2つです。

  • ドライバにシェーダーを事前にロードしておく
  • ロードしたシェーダーを使い回す

シェーダーを事前にロードする

まずは事前にシェーダーをロードします。
ShaderVariantCollectionに事前ロードしておきたいシェーダー一覧を登録しておき、ShaderVariantCollection .WarmUpで実際にロードするだけです。
指定されたシェーダーは、カクついても問題ないタイミングで読んでおけば、以降カクつく事はありません。

f:id:tsubaki_t1:20181117231406j:plain

全てのシェーダーを登録するとパースに恐ろしい時間がかかるので、必要なものだけを登録しておきます。

ロードしたシェーダーを使い回す

もう一つ、ロードしたシェーダーを使い回します。

ShaderVariantCollectionが事前ロードするシェーダーを、全てShaderVariantCollectionと同じAssetBundleに格納するだけです。

f:id:tsubaki_t1:20181117235229g:plain

というのも、AssetBundleに暗黙的に格納された(AddressやAssetBundle Nameを指定されていない)アセット群は、基本的にAssetBundle毎に独立した存在とみなされるためです。
AssetBundle Browserで確認すると、同じアセットを参照しているものは警告が出ます。

f:id:tsubaki_t1:20181117235012j:plain

同じアセットを別のAssetBundleが暗黙的に格納している場合、コンテンツの中身は完全に一致する”だけ”の、別のアセットとして扱われます。
そのため、今回のケースではShaderVariantCollectionWarmUp()しても、ロードされるのは同じAssetBundle内のシェーダーのみです。他のAssetBundleに格納されているシェーダーは別途ロードされることになります。

f:id:tsubaki_t1:20181117233337j:plain

つまり、今回の理屈は以下のような事です。

  • シェーダーをAssetBundleに明示的に指定することで、他のAssetBundleは明示的に格納したシェーダーを使うようにする。
  • ShaderVariantCollectionとソレが参照するシェーダーを同じAssetBundleに明示的に格納しておくことで、WarmUp()出来るようにしておく

なおShaderVariantCollectionを格納しているAssetBundleを積極的にUnloadすると、AssetBundle間の参照関係やアセット運用周りがおじゃんになるので、そこんとこ注意です。

関連

その他、最適化ポイント云々

tsubakit1.hateblo.jp

アセット設定の一括変更とかは、コレが楽で好きです。

tsubakit1.hateblo.jp

色々試す前に「施工前と施工後」を確認できるようにする。話はそれからだ
Stay alert! Trust no one! Keep profiler!

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

【Unity】CinemachineのClear Shotという機能

自分はCinemachineの機能は結構好きで、色々と遊んでみたりしているのです。それでもなお首を撚る系の機能「Clear Shot」についてです。

Clear Shot

Clear ShotはCinemachineに含まれるカメラ挙動の一つで、主な用途は 「キャラクターがカメラ外に行くのを防ぐ」 というものです。他のFollowLookAt系のようなカメラを追随するのではなくアクティブなVirtual Cameraそれ自体を切り替えるという点が特徴といえます。

主な切り替えるための判定は2つです

  • カメラにキャラクターが一定以上近づいた
  • コライダーによってキャラクターが隠れた

キャラクターが隠れた時に、見える位置にカメラを動かす

まずキャラクターが隠れた場合です。

f:id:tsubaki_t1:20181115204335g:plain

例えば下の図では、通常時は青の位置にカメラがありますが、そのままだとオレンジの壁に遮られキャラクター(カプセル君)が見えなくなります。コレを回避するため、高めの位置にVirtual Cameraを配置しておき、それと切り替えることでキャラクターが完全に壁の裏に隠れるのを防ぎます。

f:id:tsubaki_t1:20181115204537j:plain

これは通常のCinemachineColliderと比較して、カメラの切替え先が明確に異なる設定を持つことが可能な点が良いです。 例えば通常は神視点カメラですが、洞窟に入っている間はサードパーソン的な視点とする…といった事も可能です。

f:id:tsubaki_t1:20181115210725g:plain

手順

  1. Cinemachine > ClearShotを選択
  2. CM ClearShot1.CinemachineClearShotLookAtに追跡する対象のGameObjectをセット
  3. CM vcam1を切り替えたいカメラの数だけ増やす。
    その際、常にメインとなるカメラのプライオリティは高めに設定しておく

重要なことは、親(CM ClearShot1)もしくは子の全て(CM vcam1~)のどちらかにCinemachineColliderが設定されていることです。この設定を利用して遮蔽の判定を行っています。遮蔽の判定を行う必要がない場合は、CinemachineColliderAvoid Obstanclesのチェックを外します

f:id:tsubaki_t1:20181115210903j:plain

f:id:tsubaki_t1:20181115211150j:plain

f:id:tsubaki_t1:20181115211200j:plain

なおCinemachineColliderが障害物を検知してめり込みを回避するため、カメラの切替えがCut以外だとカメラが急激に動きます。コレを回避したい場合はCinemachineColliderDumpingWhenOccludedを少し高めに設定しておきます。

f:id:tsubaki_t1:20181115211407j:plain

f:id:tsubaki_t1:20181115211713g:plain

キャラクターが近づいてきた時にカメラを切り替える

次にキャラクターが近づいた場合です。

f:id:tsubaki_t1:20181115212807g:plain

キャラクターがカメラに対して一定以上近くに寄ってきた際に、優先度が切り替わる設定です。実際には遮蔽物で遮られていない、かつ一定範囲内のカメラという条件で取得する際に使う設定のように思います。 下の図でいうところの赤い枠に入るとカメラの切替えが始まります。

f:id:tsubaki_t1:20181115214027j:plain

手順

Optional Target Distanceを設定します。遮蔽物で遮られる予定が無ければAvoid Obstaclesのチェックは外してもOKです。遮蔽物があり複数のカメラが切り替わる場合は、ONにしておきます。

f:id:tsubaki_t1:20181115214327j:plain

使いすぎに注意なのか?

今回紹介したClear Shotですが、使い所はかなり限定されると踏んでいます。

例えば「遮蔽されたときにカメラが切り替わる」場合。今回はカメラの位置を少し上にしただけで、プレイに大した影響は出ないでしょうが、これがカメラの位置がイマジナリーラインを超えてガッツリ変わる場合、ゲームプレイが凄い混乱する気がします。 バイオハザード2やディノクライシスのように、操作性も含めてソレを許容するならば兎も角、こういったカメラ切り替えは最近は余り見ない印象です。いや、特定の位置に近づいたら…等はありそうですが、遮蔽されたら(範囲外に出たら)切り替えるタイプは少なくともパッと思いつきません。

また「近づいたらカメラが切り替わる」、コレはRPGやサードパーソン3Dアクション等でありそうですが、この「近づくと」というのは結構曲者で、カメラの中心点が実際のカメラの位置と異なる場所に調整出来ないと使い勝手が滅法悪いです。 これならばTriggerColliderを用意し、OnTriggerEnter等で反応したカメラの切替えとする方が理にかなっています。また、様々な条件でガチャガチャカメラが切り替わるのは、非常によろしくありません。条件は明確にしておきたい所です。

そして最後に、酔います。今回の記事を書いててカメラをガチャガチャ入れ替えてたんですが、きもちわるい・・・ううっぷ

カメラの操作は、出来るだけプレイヤーが予測できる動きに抑えたい所です。まぁカットシーンやフィルム的なアプローチに利用するのであれば、割とありな気もしますが、その場合はカメラの動きも含めて作っちゃいそうな予感も。

関連

Cinemachine Clear Shotの各項目の設定が日本語で記述されています。

gametukurikata.com

公式チュートリアル動画です。

www.youtube.com