【Unity(C#)】ARFoundationにおける平面検知シーケンスの実装

## はじめに
GIFのような処理を実装することが多く、再参照コストがもったいないのでメモします。
![PlaneDetection.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/9dbddc40-c5bf-ad96-112e-08df9f5f8f6d.gif)


## バージョン情報

|  諸々名前 | バージョン |
|:-:|:-:|
| Unity  |  2020.3.4f1 |
|  ARFoundation |  4.1.7 |
|  ARCore XR Plugin | 4.1.7  |
| ARKit XR Plugin  | 4.1.7  |
|  XR Plugin Management | 4.0.1  |

## コード

```CS
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class PlaneDetectionDemo : MonoBehaviour
{
    [SerializeField] private GameObject arObject;
    [SerializeField] private GameObject scanGuide;
    [SerializeField] private GameObject detectGuide;
    [SerializeField] private ARRaycastManager arRaycastManager;
    [SerializeField] private ARPlaneManager arPlaneManager;

    private readonly List<ARRaycastHit> hits = new List<ARRaycastHit>();
    private bool isDeteced;
    private Vector2 screenCenter;

    private void Start()
    {
        screenCenter = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f);
    }

    private void Update()
    {
        if (isDeteced)
        {
            return;
        }

        var isHit = arRaycastManager.Raycast(screenCenter, hits, TrackableType.PlaneWithinPolygon);
        if (isHit)
        {
            //RayとARPlaneが衝突したところのPose
            var hitPose = hits[0].pose;
            detectGuide.transform.SetPositionAndRotation(hitPose.position, hitPose.rotation);
            detectGuide.SetActive(true);
            scanGuide.SetActive(false);
        }
        else
        {
            detectGuide.SetActive(false);
            scanGuide.SetActive(true);
        }
        
        if (Input.touchCount > 0)
        {
            var touch = Input.GetTouch(0);

            if (touch.phase == TouchPhase.Began)
            {
                if (isHit)
                {
                    //RayとARPlaneが衝突したところのPose
                    var hitPose = hits[0].pose; 
                    //オブジェクトの配置
                    arObject.transform.position = hitPose.position;
                    arObject.SetActive(true);
                    var cameraPos = Camera.main.transform.position;
                    cameraPos.y = arObject.transform.position.y;
                    arObject.transform.LookAt(cameraPos);

                    //平面認識の機能をオフ
                    arPlaneManager.requestedDetectionMode = PlaneDetectionMode.None;
                    foreach (ARPlane plane in arPlaneManager.trackables)
                    {
                        plane.gameObject.SetActive(false);
                    }

                    detectGuide.SetActive(false);
                    scanGuide.SetActive(false);
                    isDeteced = true;
                }
            }
        }
    }
}

```

処理の流れとして、まずはスキャンを促すUIを表示します。
次に画面の中央からRayを出し、検出した平面と衝突した場合は画面タップを促すUIを表示します。
タップしたら画面の中央から出したRayと平面との交点にオブジェクトを表示するという流れです。

オブジェクトの青い部分はそのオブジェクトの正面方向となっており、
体験者に正対するようにしています。

スキャンを促すUIについては、ARFoundationのデモに含まれているのでお借りしました。

【参考リンク】：[Unity-Technologies/arfoundation-demos](https://github.com/Unity-Technologies/arfoundation-demos/tree/master/Assets/UX/Videos)

## 参考リンク
[【Unity(C#)】ARFoundationで認識した平面をオンオフ、平面認識機能そのものをオンオフする方法](https://qiita.com/OKsaiyowa/items/4dc8f3c52a7fd70ebdd0)