りゃおの学びログ

ARを中心に学びを記録

【ARCore】タップした平面のDetectedPlaneTypeを識別して配置するオブジェクトの出し分けをする

1.4.0で追加された、自分の中では一番気になる変更点であるDetectedPlaneTypeについて調べてみました。

変更の内容

新規に"DetectedPlaneType.cs"が追加され、水平面の上向きと下向き、垂直面を区別してenumで管理するようになったようです。
github.com

それに併せ、"DetectedPlane.cs"に"PlaneType"が追加され、DetectedPlaneTypeを取得できるようになっています。
github.com

やったこと

  • HelloARサンプルを修正し、タップした平面の水平面上向き、水平面下向き、垂直面のどれなのかを識別
  • 平面のタイプに合わせて配置するARオブジェクトを変える
    • 水平面上向き:みさきちゃん
    • 水平面下向き:ゆうこちゃん
    • 垂直面:こはくちゃん

(点にはアンディが配置されます)

開発環境

  • Windows10
  • Unity 2018.1.7f1
  • ARCore SDK for Unity v1.3.0
  • Galaxy S9

HelloARサンプルのコードを修正

今回は3種類(+1)のオブジェクトが必要になるので、先にpublicで宣言しておきます。
そしてUpdate()内の、画面がタップされた場所に対してヒットテストを行いオブジェクトを配置する処理の部分を修正します。

// Raycast against the location the player touched to search for planes.
TrackableHit hit;
TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinPolygon |
    TrackableHitFlags.FeaturePointWithSurfaceNormal;

if (Frame.Raycast(touch.position.x, touch.position.y, raycastFilter, out hit))
{
    // Use hit pose and camera pose to check if hittest is from the
    // back of the plane, if it is, no need to create the anchor.
    if ((hit.Trackable is DetectedPlane) &&
        Vector3.Dot(FirstPersonCamera.transform.position - hit.Pose.position,
            hit.Pose.rotation * Vector3.up) < 0) {
        Debug.Log("Hit at back of the current DetectedPlane");
    } else {
        // Choose the Andy model for the Trackable that got hit.
        GameObject prefab;
        if (hit.Trackable is FeaturePoint) {
            prefab = AndyPrefab;
        } else if (hit.Trackable is DetectedPlane) {
            // ---------------↓ここから---------------
            // hit.TracableをDetectedPlaneにキャスト
            DetectedPlane plane = hit.Trackable as DetectedPlane;
            // 平面の種類ごとにモデルの出し分け
            switch (plane.PlaneType) {
                case DetectedPlaneType.HorizontalUpwardFacing:
                    // 水平面上向き
                    prefab = MisakiPrefab;
                    break;
                case DetectedPlaneType.HorizontalDownwardFacing:
                    // 水平面下向き
                    prefab = YukoPrefab;
                    break;
                default:
                    // 垂直面
                    prefab = KohakuPrefab;
                    break;
            }
            // ---------------↑ここまで---------------
        } else {
            prefab = KohakuPrefab;
        }

        // Instantiate Andy model at the hit pose.
        var arObject = Instantiate(prefab, hit.Pose.position, hit.Pose.rotation);

        // Compensate for the hitPose rotation facing away from the raycast (i.e. camera).
        arObject.transform.Rotate(0, k_ModelRotation, 0, Space.Self);

        // Create an anchor to allow ARCore to track the hitpoint as understanding of the physical
        // world evolves.
        var anchor = hit.Trackable.CreateAnchor(hit.Pose);

        // Make Andy model a child of the anchor.
        arObject.transform.parent = anchor.transform;
    }
}

ヒットテストにより取得した"Trackable"が"DetectedPlane"だった場合の処理を修正しています。
"hit.Trackable"を"DetectedPlane"型にキャストし、"PlaneType"により"DetectedPlaneType"を取得します。
そして、平面のタイプごとにAR空間に配置するオブジェクトの出し分けを行っています。
※Instantiateするオブジェクトの変数名を変えていますが(andyObject→arObject)処理には影響しません。

結果

このように、平面のタイプによって出現するオブジェクトを変えることをができました。


つまづいたこと

はじめ、タップした平面の情報を取得する方法が分からず、PlaneTypeを呼び出せずにいました。
ソースコードを眺めていてようやくDetectedPlaneがTrackableを継承しているものだということに気づき、Raycastで取得したhit.Trackableをキャストするという方法に至りました。

終わりに

タップした平面をDetectedPlane型にする方法が学べたので、これでDetectedPlaneType以外の情報を取得できそうです。

もっと他にいい方法があるよ、という場合はどうか教えてください。