やりたいこと
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
をシーンに追加する。
MainCameraを削除
先ほど追加したARSessionOrigin
は子にARCamera
を持っている。シーンに元々配置されているMainCamera
は不要となるので削除する。
ARPlaneManager
さらに、右クリックしてARDefaultPlane
を作成する。これをプレハブ化してシーンからは削除する。ARSessionOrigin
にARPlaneManager
を追加する。PlanePrefab
とうい項目があるので、ここに先ほどプレハブ化したARDefaultPlane
をアタッチする。
ビルドしてみる
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
に追加する。
注意点
配置するオブジェクトのスケールに注意する必要がある。ここではCubeを配置するようにしているが、デフォルトでCubeの一辺は1mなので、配置した際に大きすぎて、画面上で確認できないことがある。Scaleを変更して、現実世界の大きさに合わせておく。
ARPlacementManagerは以下のように実装。とりあえずTouchPhase.BeganでCubeを配置するようにしてある。
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
を使用する。
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);
}
}
}
}