当前位置:网站首页>ARFoundation入门教程10-平面检测和放置
ARFoundation入门教程10-平面检测和放置
2022-07-29 05:07:00 【suelee_hm】
示例源代码:
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
边栏推荐
- Force deduction ----- sort odd and even subscripts respectively
- JS (in ES6) sync & await understanding
- Torch.nn.crossentropyloss() details
- 搭建手机APP需要用到什么服务器
- 浅谈AspectJ框架
- TMUX essays
- 1 sentence of code, get asp Net core binds multiple sources to the same class
- Pivot table of odoo development tutorial
- 网安学习-内网安全1
- 【文件下载】Easyexcel快速上手
猜你喜欢

Torch.nn.crossentropyloss() details

浅谈AspectJ框架

A little knowledge about management

Jackson解析JSON详细教程

How to make the characters in the photos laugh? HMS core video editing service one click smile function makes people smile more naturally

excel怎么设置行高和列宽?excel设置行高和列宽的方法
Let you understand several common traffic exposure schemes in kubernetes cluster

MySQL regularly calls preset functions to complete data update

Mapper agent development

Office提示系统配置无法运行怎么办?
随机推荐
MySQL regularly calls preset functions to complete data update
自贸经济中架起的“隐形桥梁”:国货精品与中国AI力量
Conv2d of torch
开区网站打开自动播放音乐的添加跟修改教程
缓存穿透、缓存击穿、缓存雪崩以及解决方法
MySQL sorts the queried result set according to the specified sequence
Word如何查看文档修改痕迹?Word查看文档修改痕迹的方法
[wechat applet -- solve the alignment problem of the last line of display:flex. (discontinuous arrangement will be divided into two sides)]
JS daily question (12)
How to solve the problem of configuring the progress every time Office2010 is opened?
Legend how to configure multiple versions of wechat updates on one server
Deadlock to be resolved
带你搞懂 Kubernetes 集群中几种常见的流量暴露方案
office2010每次打开都要配置进度怎么解决?
How does WPS use smart fill to quickly fill data? WPS method of quickly filling data
DataSourceClosedException: dataSource already closed at Mon Oct 25 16:55:48 CST 2021
【config】配置数组参数
Let you understand several common traffic exposure schemes in kubernetes cluster
输入的查询SQL语句,是如何执行的?
Stack and queue and priority queue (large heap and small heap) simulation implementation and explanation of imitation function