【Unity】ARFoundationを使ってCubeを配置、削除する

# やりたいこと

ARFoundationを使ってオブジェクトを配置したり削除したりしたい。そのために以下の手順でやってみる。

- 平面を検出
- Cubeを配置
- 配置済みのCubeを削除


# 環境
Mac
Unity 2019.3.9f1
AR Foundation 3.1.3
Xcode Version 11.3.1

# 平面を検出

### PackageManagerでインストール
ARFoundationはプロジェクトにデフォルトで組み込まれている訳では無いのでPackageManagerからインストールする。インストールしたのは以下。全て最新版(この時点では3.1.3)をインストールした。

- AR Foundation
- AR Subsystems
- ARKit XR Plugin

### ARSessionOrigin, ARSessionを追加

ヒエラルキー上で右クリックすると```XR```とうい項目が追加されているのでその中から、```ARSessionOrigin```と```ARSession```をシーンに追加する。

<img width="423" alt="スクリーンショット 2020-05-11 12.34.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85509/f4945057-ad4f-3953-0a56-9f56c5bc6762.png">

### MainCameraを削除

先ほど追加した```ARSessionOrigin```は子に```ARCamera```を持っている。シーンに元々配置されている```MainCamera```は不要となるので削除する。

### ARPlaneManager

さらに、右クリックして```ARDefaultPlane```を作成する。これをプレハブ化してシーンからは削除する。```ARSessionOrigin```に```ARPlaneManager```を追加する。```PlanePrefab```とうい項目があるので、ここに先ほどプレハブ化した```ARDefaultPlane```をアタッチする。
<img width="651" alt="スクリーンショット 2020-05-11 12.40.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85509/c514b12d-7695-9b58-9cbf-b5dc5bde0227.png">

## ビルドしてみる

PlayerSetting→OtherSettingで

- Camera Usage Descriptionに"Camera is required for AR."とでも書いておく。
- ArchitectureをARM64にする
- Target Minimum iOS Versionを11.0にする

この時点で実機ビルドして確認すると平面が検出されることが確認できる。

# Cubeを配置する

### ARRaycastManager

平面のどこがタップされたか知る必要があるので、3Dゲームでやるようにrayを照射する必要がある。ARは必ずしも物理モジュールを必要としないので、物理モジュールが存在しない場合にもrayを使用できるように、独自の```ARRaycastManager```が準備されている。```ARSessionOrigin```に```ARRaycastManager```を追加する。

さらに、配置を管理するためのクラス```ARPlacementManager```を新規作成して、同じく```ARSessionOrigin```に追加する。

<img width="509" alt="スクリーンショット 2020-05-11 16.34.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85509/81dd53ad-8fb8-2d8c-6e28-673747bf1898.png">

###### 注意点

配置するオブジェクトのスケールに注意する必要がある。ここではCubeを配置するようにしているが、デフォルトでCubeの一辺は1mなので、配置した際に大きすぎて、画面上で確認できないことがある。Scaleを変更して、現実世界の大きさに合わせておく。

ARPlacementManagerは以下のように実装。とりあえずTouchPhase.BeganでCubeを配置するようにしてある。


```ARPlacementManager.cs
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class ARPlacementManager : MonoBehaviour
{
    [SerializeField] private GameObject placementPrefab;
    [SerializeField] private Camera arCamera;
    [SerializeField] private ARRaycastManager raycastManager;
    private List<ARRaycastHit> raycastHits = new List<ARRaycastHit>();

    private void Update()
    {
        if (Input.touchCount <= 0) return;

        var touch = Input.GetTouch(0);
        if (touch.phase == TouchPhase.Began)
        {
            // rayを照射
            var ray = arCamera.ScreenPointToRay(touch.position);
            if (raycastManager.Raycast(ray, raycastHits, TrackableType.Planes))
            {
                // 複数のPlaneがあった場合、最も近いPlaneが0番目に入っている
                Pose pose = raycastHits[0].pose;
                // 配置すべき座標
                Vector3 placePosition = pose.position;
                Instantiate(placementPrefab, placePosition, Quaternion.identity);
            }
        }
    }
}
```

# Cubeを削除する

上記でCubeを配置する際に```raycastManager.Raycast```を用いてrayを照射したが、これでは物理オブジェクトを取得することが出来ない。なので、Cubeの検出にはお馴染みの```Physics.Raycast```を使用する。

```ARPlacementManager.cs
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class ARPlacementManager : MonoBehaviour
{
    [SerializeField] private GameObject placementPrefab;
    [SerializeField] private Camera arCamera;
    [SerializeField] private ARRaycastManager raycastManager;    
    private List<ARRaycastHit> raycastHits = new List<ARRaycastHit>();

    private void Update()
    {
        if (Input.touchCount <= 0) return;
        
        var touch = Input.GetTouch(0);
        if (touch.phase == TouchPhase.Began)
        {
            // arCameraカメラからrayを照射
            var ray = arCamera.ScreenPointToRay(touch.position);
            
            // Cubeをタップした場合は削除する
            RaycastHit hit;
            bool hasHit = Physics.Raycast(ray, out hit);
            if (hasHit)
            {
                var target = hit.collider.gameObject;
                if (target.name.Contains("Cube"))
                {
                    Destroy(target);
                    return;
                }
            }
            
            // Cubeを配置
            var isHitPlane = raycastManager.Raycast(ray, raycastHits, TrackableType.PlaneWithinPolygon);
            if (isHitPlane)
            {
                // 複数のPlaneがあった場合、最も近いPlaneが0番目に入っている
                Pose pose = raycastHits[0].pose;
                // 配置すべき座標
                Vector3 placePosition = pose.position;
                Instantiate(placementPrefab, placePosition, Quaternion.identity);
            }
        }
    }
}
```

# 結果

![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85509/07edbc67-1977-9ef2-16ae-83efc9e94759.gif)


# ドキュメント
https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@3.1/manual/index.html