---
# System prepended metadata

title: AR Foundation 偵測平面與置放物件
tags: [AR Foundation]

---

---
tags: AR Foundation
---

# AR Foundation 偵測平面與置放物件

參考影片
{%youtube lelX8GGh_S8 %}

Github
https://github.com/allinreality/PlaneTracking-ARFoundationUnity3D

1. 開啟3D專案
2. 使用 Package Manager，需要安裝 AR Foundation 套件，看你是使用什麼平台，選擇 ARCore XR Plugin 或 ARKit XR Plugin 套件至少一項
4. 刪除既有場景的攝影機物件
5. 新增物件 XR -> AR Session Origin
6. 新增物件 XR -> AR Session
7. 在 AR Session Origin 中增加以下組件：
* AR Plane Manager
* AR Point Cloud
* AR Point Cloud Manager
* AR Raycast Manager
* AR Anchor Manager
8. 新增物件 XR -> AR Default Point Cloud，並且做成預置物件，原場景物件刪除，預置物件放到 AR Point Cloud Manager
9. 新增物件 XR -> AR Default Plane，並且做成預置物件，原場景物件刪除，預置物件放到 AR Plane Manager
10. 設計要置放的物件，並且做成預置物件
11. 將 PlaceOnPlane 程式碼匯入專案，並且置放到 AR Session Origin 中
12. 匯入指標圖樣動畫物件「VisualObjectAR.unitypackage」
13. PlaceOnPlane 的 Placed Prefab 欄位放要置放的物件、Visual Object 放置指標圖樣
14. 專案匯出設定為 Android，Player 設定如下：
* Graphics API 把 Vulkan 移下或者移除
* Minimun API Level 至少要有 API Level 24
* Scripting Beckend 設定為 IL2CPP
15. Project Setting 的 XR Plug-in Management 設定，選擇 Android 的 ARCore 與 ARKit (iOS)
16. 安裝到手機測試

## PlaceOnPlane
```csharp=
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

/// <summary>
/// Listens for touch events and performs an AR raycast from the screen touch point.
/// AR raycasts will only hit detected trackables like feature points and planes.
///
/// If a raycast hits a trackable, the <see cref="placedPrefab"/> is instantiated
/// and moved to the hit position.
/// </summary>
[RequireComponent(typeof(ARRaycastManager))]
public class PlaceOnPlane : MonoBehaviour
{
    [SerializeField]
    [Tooltip("Instantiates this prefab on a plane at the touch location.")]
    GameObject m_PlacedPrefab;

    UnityEvent placementUpdate;

    [SerializeField]
    GameObject visualObject;

    /// <summary>
    /// The prefab to instantiate on touch.
    /// </summary>
    public GameObject placedPrefab
    {
        get { return m_PlacedPrefab; }
        set { m_PlacedPrefab = value; }
    }

    /// <summary>
    /// The object instantiated as a result of a successful raycast intersection with a plane.
    /// </summary>
    public GameObject spawnedObject { get; private set; }

    void Awake()
    {
        m_RaycastManager = GetComponent<ARRaycastManager>();

        if (placementUpdate == null)
            placementUpdate = new UnityEvent();

        placementUpdate.AddListener(DiableVisual);
    }

    bool TryGetTouchPosition(out Vector2 touchPosition)
    {
        if (Input.touchCount > 0)
        {
            touchPosition = Input.GetTouch(0).position;
            return true;
        }

        touchPosition = default;
        return false;
    }

    void Update()
    {
        if (!TryGetTouchPosition(out Vector2 touchPosition))
            return;

        if (m_RaycastManager.Raycast(touchPosition, s_Hits, TrackableType.PlaneWithinPolygon))
        {
            // Raycast hits are sorted by distance, so the first one
            // will be the closest hit.
            var hitPose = s_Hits[0].pose;

            if (spawnedObject == null)
            {
                spawnedObject = Instantiate(m_PlacedPrefab, hitPose.position, hitPose.rotation);

            }
            else
            {
                //repositioning of the object 
                spawnedObject.transform.position = hitPose.position;
            }
            placementUpdate.Invoke();
        }
    }

    public void DiableVisual()
    {
        visualObject.SetActive(false);
    }

    static List<ARRaycastHit> s_Hits = new List<ARRaycastHit>();

    ARRaycastManager m_RaycastManager;
}
```
