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 提供程序目前仅支持 PlaneEstimatedPlaneWithinBoundsPlaneWithinPolygonFeaturePointImage 和 Depth

三、android打包运行

如未配置,参看《ARFoundation从零开始3-arfoundation项目》。

1.安装运行

四、常见问题

五、参考资料

1. Unity api:

Class ARRaycastManager | AR Foundation | 4.2.3

2.ARFoundation示例:

GitHub - Unity-Technologies/arfoundation-samples: Example content for Unity projects based on AR Foundation

3.ARCore文档:

在 Unity (AR Foundation) 应用中执行光线投射  |  ARCore  |  Google Developers

4.本项目示例源代码:

https://github.com/sueleeyu/ar-plane

  • 1
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ARFoundation是一个基于Unity的增强现实(AR)开发框架,用于在移动设备上创建AR应用程序。在ARFoundation中,平面检测可以看作是AR应用程序中的一种基本功能。但并不是所有的AR应用都必须先进行平面检测平面检测是指ARFoundation使用传感器数据和计算算法来识别设备摄像头视野中的平面,并将这些平面作为虚拟物体放置和交互的基础。通过平面检测,应用程序可以识别水平的平面,例如地面、桌面等,用户可以在这些平面放置虚拟物体,使其看起来像是与真实世界的物体进行交互。 在许多AR应用中,平面检测是一个重要的步骤,它为用户提供了与虚拟内容进行互动的机会,并将其与真实世界环境相结合。例如,一个AR游戏可以使用平面检测来将虚拟关卡放置在用户当前所处的地面上。然后用户可以在这个平面上行走,并与游戏中的虚拟角色进行互动。 然而,并不是所有的AR应用都需要平面检测。一些AR应用可能更关注于物体跟踪或人脸识别等其他功能。例如,一个AR镜框应用可能更关注如何准确地将虚拟眼镜在用户的脸上,而不是在平面上。在这种情况下,平面检测可能并不是首要的任务。 总的来说,ARFoundation提供了平面检测的功能,可以在AR应用程序中使用。然而,具体的AR应用功能需求决定了是否需要平面检测

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值