ARFoundation入门教程10-平面检测和放置
Posted suelee_hm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARFoundation入门教程10-平面检测和放置相关的知识,希望对你有一定的参考价值。
示例源代码:
https://github.com/sueleeyu/ar-plane
从《ARFoundation从零开始3-arfoundation项目》复制项目,继续:
一、添加组件
1.添加AR RayCaset Manager 和AR Plane Manager:
选择左侧Hierarchy-AR Session Origin,Inspector下点击Add Component,依次输入并添加AR RayCaset Manager 和AR Plane Manager
2.创建平面prefabs:Hierarchy-‘+’-XR-AR Default Plane,Assets下新建Prefabs目录,将创建的对象拖动到Prefabs目录,删除Hieraychy下的对象。
3.将创建的plane预制件拖动到Plane Manager组件:
4.创建Button组件,命名BtnPlane,用于显示/隐藏平面:
二、编写代码
1.编写cs代码PlaceManager.cs,用于放置预制件。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
namespace FrameworkDesign.Example
public class PlaceManager : MonoBehaviour
[Header("AR Foundation")]
/// <summary>
/// The active ARRaycastManager used in the example.
/// </summary>
public ARRaycastManager m_RaycastManager;
[Header("UI")]
[SerializeField]
[Tooltip("Instantiates this prefab on a plane at the touch location.")]
GameObject m_PlacedPrefab;//要放置的预制件
/// <summary>
/// The prefab to instantiate on touch.
/// </summary>
public GameObject placedPrefab
get return m_PlacedPrefab;
set m_PlacedPrefab = value;
[HideInInspector]
static List<ARRaycastHit> s_Hits = new List<ARRaycastHit>();//存放检测到的碰撞点
/// <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>();//也可以通过GetComponent获取到ARRaycastManager
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;
var touch = Input.GetTouch(0);
const TrackableType trackableTypes =
TrackableType.FeaturePoint |
TrackableType.PlaneWithinPolygon;
if (Input.touchCount == 1 && touch.phase == TouchPhase.Moved)//移动已放置的对象
if (m_RaycastManager.Raycast(touchPosition, s_Hits, trackableTypes))
// 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.transform.position = hitPose.position;
if (Input.touchCount == 1 && touch.phase == TouchPhase.Began)//检测touch begin,在touch begin中做射线碰撞检测
//---判断是否touch到UI组件----
//#if IPHONE || android
if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
//#else
// if (EventSystem.current.IsPointerOverGameObject())
//#endif
//Debug.Log("当前触摸在UI上");
Logger.Log($"当前触摸在UI上"+ touch.phase);
return;
else
//Debug.Log("当前没有触摸在UI上");
Logger.Log($"当前没有触摸在UI上"+ touch.phase);
if (m_RaycastManager.Raycast(touchPosition, s_Hits, trackableTypes))
// 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
spawnedObject.transform.position = hitPose.position;//更新对象状态
2.Hierarchy下Game下Create Empty,命名GameScene,将PlaneManager.cs挂载到其下:
选择ARScene,将AR Session Origin 和做好的预制件拖放到PlaceManager.cs的参数列:
3.编写ARManager.cs,用于显示/隐藏平面信息:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
public class ARManager : MonoBehaviour
[Header("AR Foundation")]
/// <summary>
/// The active ARRaycastManager used in the example.
/// </summary>
public ARPlaneManager m_ARPlaneManager;
[HideInInspector]
/// <summary>
/// 当前识别出的平面
/// </summary>
List<ARPlane> detectPlanes = new List<ARPlane>();
/// <summary>
/// 当前是否要显示平面
/// </summary>
bool isShowPlane = true;
#region MonoBehaviour CallBacks
private void Awake()
m_ARPlaneManager = FindObjectOfType<ARPlaneManager>();
void Start()
CheckDevice();
m_ARPlaneManager.planesChanged += OnPlaneChanged;
private void Update()
SaveElePolicy();
void OnDisable()
m_ARPlaneManager.planesChanged -= OnPlaneChanged;
#endregion
// 启用与禁用平面检测
// 程序默认启用,启用时一直不停地检测平面。关闭时则不会再检测新平面了。
public void DetectionPlane(bool value)
m_ARPlaneManager.enabled = value;
if (m_ARPlaneManager.enabled)
print("已启用平面检测");
else
print("已禁用平面检测");
// 显示与隐藏检测到的平面
public void SwitchPlane()
isShowPlane = !isShowPlane;
for (int i = detectPlanes.Count - 1; i >= 0; i--)
if (detectPlanes[i] == null || detectPlanes[i].gameObject == null)
detectPlanes.Remove(detectPlanes[i]);
else
detectPlanes[i].gameObject.SetActive(isShowPlane);
/// <summary>
/// 得到当前AR会话是否正在运行,并被跟踪(即,该设备能够确定其在世界上的位置和方向)。
/// </summary>
public bool Skode_IsTracking()
bool isTracking = false;
if (ARSession.state == ARSessionState.SessionTracking)
isTracking = true;
return isTracking;
//在ARFoundation新发现平面时,将平面添加进列表里,便于我们控制这些平面
void OnPlaneChanged(ARPlanesChangedEventArgs arg)
for (int i = 0; i < arg.added.Count; i++)
detectPlanes.Add(arg.added[i]);
arg.added[i].gameObject.SetActive(isShowPlane);
//检查设备运行环境
void CheckDevice()
if (ARSession.state == ARSessionState.NeedsInstall)
ShowAndroidToastMessage("AR is supported, but requires an additional install. .");
Invoke("Quit", 1);
else if (ARSession.state == ARSessionState.Ready)
Debug.Log("AR is supported and ready.");
else if (ARSession.state == ARSessionState.Unsupported)
ShowAndroidToastMessage("AR is not supported on the current device.");
Invoke("Quit", 1);
void ShowAndroidToastMessage(string message)
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
if (unityActivity != null)
AndroidJavaClass toastClass = new AndroidJavaClass("android.widget.Toast");
unityActivity.Call("runOnUiThread", new AndroidJavaRunnable(() =>
AndroidJavaObject toastObject = toastClass.CallStatic<AndroidJavaObject>("makeText", unityActivity, message, 0);
toastObject.Call("show");
));
void Quit()
Application.Quit();
/// <summary>
/// 一种省电设置,当设备没找到识别目标,允许屏幕在最后激活一段时间后变暗
/// </summary>
void SaveElePolicy()
if (ARSession.state != ARSessionState.SessionTracking)
const int lostTrackingSleepTimeout = 15;
Screen.sleepTimeout = lostTrackingSleepTimeout;
else
Screen.sleepTimeout = SleepTimeout.NeverSleep;
4.添加ARManager.cs到Hierarchy-Game下,挂载PlaneManager组件:
5.添加Button的click事件。选择BtnPlane组件,添加Onclick事件,拖动Game,选择Function函数:
二、unity知识点
1.射线检测ARRaycastManager:
ARRaycastManager.Raycast(Vector2, List<ARRaycastHit>, TrackableType) 。
API:Class ARRaycastManager | AR Foundation | 4.2.3
注意:并非所有 TrackableType
都受 ARCore 和 ARKit 提供程序支持。ARCore 提供程序目前仅支持 PlaneEstimated
、PlaneWithinBounds
、PlaneWithinPolygon
、FeaturePoint
、Image
和 Depth
。
三、android打包运行
如未配置,参看《ARFoundation从零开始3-arfoundation项目》。
1.安装运行
四、常见问题
五、参考资料
1. Unity api:
Class ARRaycastManager | AR Foundation | 4.2.3
2.ARFoundation示例:
3.ARCore文档:
在 Unity (AR Foundation) 应用中执行光线投射 | ARCore | Google Developers
4.本项目示例源代码:
https://github.com/sueleeyu/ar-plane
ARFoundation入门——平面检测(Android)
开发环境及一些说明
Unity:2021.1.14flc1
导入Package:
AR Foundation:4.1.7
ARCore XR Plugin:4.1.7
(在进行配置前需要先将Unity中的Android环境配置好)
对ARFoundation的使用学习可以参考unity官网的文档:
http://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/manual/index.html
也可以到ARCore的官网:https://developers.google.cn/ar对这部分SDK的功能和环境配置有个更好的了解。
两个网站的中文翻译我觉得都挺令人头疼的,看不懂的部分地方建议可以用英文文档理解一下。
步骤
- AR 平面管理器 AR Plane Manager
在"AR Session Orign"中添加"AR Plane Manager"控件
平面管理器可以为环境中检测到的每个平面创建游戏对象
Plane Prefab 放置的是平面预制件,也就是检测到平面后生成的游戏对象
Detection Mode 中设置检测平面类别,检测的平面类型包括水平屏幕和垂直平面 - 平面预制件
在Hierarchy面板中创建"XR" -> “AR Default Plane”,
将它拖入Assets中保存为预制件,
然后在Hierarchy面板中将其删除,
再将预制件中的"AR Default Plane"拖入到"AR Session Origin"中的"AR Plane Manager"控件下的"Plane Prefab"。
(不懂预制件的可以补一下unity的一些基本知识,但这里先不知道也没关系)
- 导出apk到测试机进行效果测试
淡黄色的范围即为平面检测结果生成的平面
以上是关于ARFoundation入门教程10-平面检测和放置的主要内容,如果未能解决你的问题,请参考以下文章
ARFoundation系列讲解 - 70 HumanBodyTracking3D
ARFoundation系列讲解 - 70 HumanBodyTracking3D
ARFoundation系列讲解 - 69 HumanBodyTracking2D