【Unity】ARCoreでUnityちゃんを召喚する

f:id:tm8r:20170906102209j:plain
ARCore出ましたね!TangoさんRIP。

というわけで早速サンプルをいじってUnityちゃんを召喚してみます。

動作環境について

現状はドキュメントに書いてある通り、動作を確認する場合PixelかGalaxyS8が必要です。
一応すり抜ける術もいくつかあるようなので一応貼ってきます。
ARCoreをPixelやS8以外の端末で動かす - Qiita

GitHub - tomthecarrot/arcore-for-all: Google ARCore for "unsupported" Android devices
後者が有志のサポート外の端末でもARCore動かそうぜというプロジェクトです。
現在の対応状況は以下で確認できます。
arcore-for-all/Device-Research.md at master · tomthecarrot/arcore-for-all · GitHub
ちなみに個人的に持ってるGalaxyS6はどちらも動作対象外。かなしみ。

準備

以下の指示に従って準備します。
Getting Started with Unity

まずはSDKAndroid端末に入れるARCoreのapkのダウンロード。
SDK Preview for Unity
ARCore Service apk

そして手持ちの対応端末をUSBで接続して以下を実行。

adb install -r -d arcore-preview.apk

次にUnityのプロジェクトの準備。
上のドキュメントにはUnity2017.2b9以降ならおっけーと書いてありますが、b10だと動かなかったので現時点ではb9を使うのがよさそう。
(エラー見る限りメソッド名かパッケージあたりが変わった?)

2017.09.13追記:現在はUnity 2017.2 Beta 11を使ってねと書いてあり、実際にこのバージョンで動作したのでこちらをオススメします。

任意の名前で3Dのプロジェクトを作成し、ダウンロードしたSDKを入れます。
次に以下の作業を行います。

  • File>Build SettingsからAndroidにSwitch Platform
  • 同画面(もしくはEdit > Project Settings)からPlayer Settingsを開く
    • Other Settings > Multithreaded Renderingのチェックを外す
    • Other Settings > Package Nameを一意な名前に変更(com.example.helloARなど)
    • Other Settings > Minimum API LevelをAndroid 7.0以上に
    • Other Settings > Target API LevelをAndroid 7.0または7.1に
    • XR Settings > ARCore Supportedにチェック

f:id:tm8r:20170906161557p:plain
f:id:tm8r:20171004115741p:plain
こんな感じ。

また、GalaxyS8などを使う場合、Resolution and PresentationのAspect Ratio ModeをLegacy Wide Screen(1.86)にしておかないとカメラが縦に引き伸ばされる感じになります。
f:id:tm8r:20170906161603p:plain

早速プレハブを差し替える

GoogleARCore > HelloARExample > ScenesのHelloARを適当な場所にコピーして開きます。(そのまま編集してもよいです)
次にコピーしたHelloARをBuild SettingsのScenes In Buildにぶっこみます。
f:id:tm8r:20170906161551p:plain

次に、このシーンにあるExampleControllerに追加されているHelloARControllerを選択してInspectorを開きます。
すると、AndyAndroidPrefabというフィールドがあるので、こちらに設定されているAndyを差し替えたいプレハブに変更。
最後にプレハブにPlaneAttachmentコンポーネントを追加すれば完了です。かんたん!
f:id:tm8r:20170906161600p:plain

完成

今回はSDのUnityちゃんに差し替えてみました。
f:id:tm8r:20170906165223j:plain
ちなみにこの画像では上の作業に加えて無理矢理影を出したりしてます。

f:id:tm8r:20170906165208j:plain
もちろん回り込むことも出来ます。やったぜ。

次はライトもよしなにしてもうちょっといる感を出したい。
影の解説はまた今度…!

f:id:tm8r:20170906162925p:plain
この作品はユニティちゃんライセンス条項の元に提供されています

【Unity】指定したTypeを持つオブジェクトをHierarchyで選択する

あんまり使うシーンないとは思いつつ。

指定したTypeを持つオブジェクトをHierarchyで選択するエディタ拡張です。
ソースコードは以下。

gist.github.com

Selection.activeGameObjectに選択したいGameObjectを突っ込むことで、Hierarchyでこのオブジェクトをクリックしたのと同じ結果を得ることが出来ます。

インスペクタはロックされてない限りはHierarchyで選択されたオブジェクトの内容が表示されるので、インスペクタから変更することを想定したプロパティを持つTypeを指定しておけば、頑張ってHierarchyから探すことなく簡単にアクセスできて楽ちんですね。(むしろそれ以外の使い道は思い浮かばない)

エンジニアならまあ多少面倒でも目的のものを探し出せると思うんですけど、非エンジニアに使ってもらうことを考えると、この名前のオブジェクトをシーンから探し出してーとか言うのも偲びないので、分かりやすいメニューを作るなり、ショートカットを作るなり、ウィンドウを作るなりして目的のものにたどり着きやすくする、という意味ではこういうツールの需要があるプロジェクトもあるんじゃないかな(あった)と思います。

今回の実装ではTools>SelectTypeを選択、またはShift+Tで起動します。
キーボードショートカットに関しては以下を参照。
docs.unity3d.com

ざっくり言うと「MenuItem」Attributeに通常通りメニューに表示するラベルを指定し、このラベルのあとにスペースを入れて以下の規定のフォーマットでショートカットに使用するキーを指定してやればよい、という感じです。

指定 キー
%t Ctrl+t(Win) Cmd+t(Mac)
#t Shift+t
&t Alt+t
_t t

尚、今回はFindObjectOfTypeを使ってるので、同Typeが複数あった場合は先頭のものが選択されます。
同じTypeが複数存在しうるものもAlt+Tabでアプリケーションを切り替えるノリで順番に見ておきたいとかであれば、FindObjectOfTypeをFindObjectsOfTypeに書き換え、staticで結果の数とかカウンタとかを保持して順番に見ていけたりもするかもですね。そんな需要があるかはさておき。

【Unity】エディタ拡張でパフォーマンスを落とさずにScrollViewに大量の要素を表示させる

f:id:tm8r:20160713192115p:plain
特定の条件に合致するアセットのリストをEditorWindowに表示させてごにょごにょする系の拡張を作ることがちらほらあるんですけど、アセットはまあ運用すればするほど増えていくわけで、その数が数千数万になっていくと単純に表示するだけだとUnityが悲鳴を上げます。

実際、まさか一気にそこまで増えると思ってなかったとあるツールで表示はできてこそいるものの、スクロールがまるで動かんみたいな状況に陥ったので、自前でカリングする方法を考えてみます。

ようは表示領域にいない要素は描画しなければいいわけです。

まずは表示領域の高さを取得したいところですが、GUILayoutのBeginHorizontal(BeginVerticalも同様)は戻り値がvoidなので、Rectが返ってくるEditorGUILayoutの同名メソッドを使います。
次にその中に表示されるリストの各要素の高さを定義し、その時点のスクロール量に応じたリストの先頭と末尾のindexを算出できれば、あとは表示領域の高さが変わらないように、要素を描画しない領域に本来描画されるはずの高さを確保してあげれば要件が満たせそうです。

f:id:tm8r:20160713193745p:plain
実際はこんな表示になっちゃだめですが、表示領域の上下の見えない部分にこんな感じで表示領域の高さを保つためのスペースがあるイメージです。

というわけで実装した結果が以下のような感じ。

これで表示領域だけに要素が描画される形になったので、リストの要素が増えても大きくパフォーマンスが損なわれることはなくなりました。
めでたしめでたし。

【Unity】Sceneビューやカメラのレイヤー表示を切り替える

普通にGUI上からいじれますが、どうしてもスクリプトでやりたいでござる…!というときのために。

以下のようにTools.visibleLayersを編集することで実現できます。

// UIレイヤーを非表示
Tools.visibleLayers &= ~(1 << LayerMask.NameToLayer ("UI"));

// UIレイヤーを非表示にして他は表示
Tools.visibleLayers = ~(1 << LayerMask.NameToLayer ("UI"));

// UIレイヤーを表示
Tools.visibleLayers |= (1 << LayerMask.NameToLayer ("UI"));

// UIレイヤーのみ表示
Tools.visibleLayers = (1 << LayerMask.NameToLayer ("UI"));

GameViewのカメラのcullingMaskも同じように操作可能なので、その場合はTools.visibleLayersをCamera.main.cullingMaskなどに読み替えてください。

また、上のコードではLayerMask.NameToLayerを用いてレイヤーのindexを取得してますが、勿論自分でindexを指定してもよいので、その場合はレイヤーのInspectorを開けば操作対象のindexが分かります。
(「UI」でいうと「Builtin Layer 5」になっているので5を指定する形)

Everything(全レイヤーを表示)に戻したい場合は-1を代入してやればよいです。

Tools.visibleLayers = -1;

【Unity】SceneViewのカメラを特定のオブジェクトに向ける

Hierarchyでオブジェクトをダブルクリックしたときと同じ挙動をScriptから再現するやつ。

public class FocusSceneViewCamera : MonoBehaviour
{
    [SerializeField]
    GameObject focusTarget;

    void Awake ()
    {
        SceneView.onSceneGUIDelegate += InitializeSceneCamera;
    }

    void InitializeSceneCamera (SceneView sceneView)
    {
        SceneView.onSceneGUIDelegate -= InitializeSceneCamera;
        if (focusTarget != null) {
            Selection.activeGameObject = focusTarget;
            sceneView.FrameSelected ();
        }
    }
}

前述の挙動の実装が、SceneViewのFrameSelectedを叩いてるっぽいんですけど、その中でSelectionからその対象を引っ張ってきているので、SelectionのactiveGameObjectを任意のGameObjectで書き換えた上で同メソッドを叩くことで実現しています。
こちらの例はゲームプレイのタイミングで一回だけ任意のGameObjectにフォーカスしてますが、Awakeで行ってる処理を行うメソッドを用意すれば好きなタイミングで呼び出せるかと思います。

正直あんまり使いどころはないですが、SceneViewのカメラとGameViewのカメラを同期したいケースでは使えなくもなかったりします。
同期は以下のようなメソッドを上と同じようにSceneViewのonSceneGUIDelegateに登録してあげれば実現できます。

void SyncGameViewCamera (SceneView sceneView)
{
    Camera.main.transform.position = sceneView.camera.transform.position;
    Camera.main.transform.rotation = sceneView.camera.transform.rotation;
}

スポンサーリンク