Unity - AR - GPS(简单的 3D 对象出现在 GPS 坐标处)- Android 和 iOS

问题描述 投票:0回答:1

尝试学习 Unity 并创建了一个项目,我希望一个简单的 3D 对象(胶囊预制件)出现在我的 iPhone 相机中的各个 GPS 位置(全部在我办公桌的视野内)。我的场景层次结构中有以下内容:

AR专场 定向光 用户界面 XR Origin(AR 装备) -相机偏移 -- 主摄像头 -- XR 屏幕空间控制器 GPS 管理器

我正在让胶囊出现,它们的大小似乎根据它们与我的距离适当。但它们并不在 GPS 坐标所示的位置。它们似乎就在我附近,并且与罗盘方向相差大约 90 度。

这是我链接到胶囊预制件的 GPS 管理器脚本 -


using System.Collections;
using UnityEngine;
using TMPro;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class GPSLocationManager : MonoBehaviour
{
    public GameObject capsulePrefab; // Assigned in the Inspector
    
    private GameObject xrOrigin; // Used as the AR Session Origin
    private ARAnchorManager anchorManager; // AR Anchor Manager for anchoring objects in AR space - unsure if working

    // Reference GPS location dynamically set to the user's current location
    private Vector2 referenceGPSPos;
    private bool isReferencePointSet = false;

    // List of colors is working
    private Color[] capsuleColors = new Color[]
    {
        Color.red, Color.blue, Color.green, Color.yellow, Color.magenta
    };

    void Awake() 
    {
        xrOrigin = GameObject.Find("XR Origin (AR Rig)"); 
        if (xrOrigin == null)
        {
            Debug.LogError("XR Origin (AR Rig) is not found in the scene.");
            return;
        }
        
        anchorManager = xrOrigin.GetComponent<ARAnchorManager>();
        if (anchorManager == null) 
        {
            Debug.LogError("ARAnchorManager component is not found on the XR Origin (AR Rig).");
        }
    }

    void Start()
    {
        StartCoroutine(StartLocationService());
    }

    private IEnumerator StartLocationService()
    {
        if (!Input.location.isEnabledByUser)
        {
            Debug.Log("[GPSLocationManager] User has not enabled GPS");
            yield break;
        }

        Input.location.Start(5f, 5f); 
        Debug.Log("[GPSLocationManager] Starting location service...");

        while (Input.location.status == LocationServiceStatus.Initializing)
        {
            yield return new WaitForSeconds(1);
        }

        if (Input.location.status == LocationServiceStatus.Failed)
        {
            Debug.Log("[GPSLocationManager] Unable to determine device location");
            yield break;
        }
        else if (!isReferencePointSet && Input.location.status == LocationServiceStatus.Running)
        {
            SetReferenceLocation();
            Debug.Log("[GPSLocationManager] Location service started successfully");
            PlaceCapsuleBasedOnGPS();
            Input.location.Stop();
            Debug.Log("[GPSLocationManager] Stopping location service...");
        }
    }

    void SetReferenceLocation()
    {
        referenceGPSPos = new Vector2(Input.location.lastData.latitude, Input.location.lastData.longitude);
        isReferencePointSet = true;
    }

    void PlaceCapsuleBasedOnGPS()
    {
        float[,] targetLocations = new float[,] {
            {-33.XXXXXXf, 151.XXXXXXf}, 
            {-33.XXXXXXf, 151.XXXXXXf}, 
            {-33.XXXXXXf, 151.XXXXXXf}, 
        };

        Debug.Log("[GPSLocationManager] Placing capsules based on GPS locations...");

        for (int i = 0; i < targetLocations.GetLength(0); i++)
        {
            float lat = targetLocations[i, 0];
            float lon = targetLocations[i, 1];
            if(IsCloseEnough(Input.location.lastData.latitude, Input.location.lastData.longitude, lat, lon))
            {
                PlaceCapsuleAtGPSLocation($"Capsule {i+1}", lat, lon);
            }
            else
            {
                Debug.Log($"[GPSLocationManager] Skipped placing Capsule {i+1} due to distance.");
            }
        }
    }

    void PlaceCapsuleAtGPSLocation(string capsuleName, float lat, float lon)
    {
        Vector3 position = GPSToUnitySpace(lat, lon);
        Pose pose = new Pose(position, Quaternion.identity);
        ARAnchor anchor = anchorManager.AddAnchor(pose);

        if (anchor == null)
        {
            Debug.LogWarning("Failed to create anchor.");
            return;
        }

        GameObject capsule = Instantiate(capsulePrefab, position, Quaternion.identity, anchor.transform);
        capsule.name = capsuleName;
        Renderer renderer = capsule.GetComponent<Renderer>();
        if (renderer != null)
        {
            renderer.material.color = capsuleColors[Random.Range(0, capsuleColors.Length)];
        }

        var textMesh = capsule.GetComponentInChildren<TextMeshProUGUI>();
        if (textMesh != null)
        {
            textMesh.text = capsuleName;
        }
    }

    public Vector3 GPSToUnitySpace(float lat, float lon)
    {
        float latDistance = (lat - referenceGPSPos.x) * 111319.9f; // Meters per degree latitude
        float lonDistance = (lon - referenceGPSPos.y) * Mathf.Cos(Mathf.Deg2Rad * lat) * 111319.9f; // Meters per degree longitude
        Vector3 positionOffset = new Vector3(lonDistance, 0, latDistance);
        return xrOrigin.transform.position + positionOffset; // Now correctly using xrOrigin
    }

    bool IsCloseEnough(float currentLat, float currentLon, float targetLat, float targetLon)
    {
        float distanceLat = Mathf.Abs(currentLat - targetLat);
        float distanceLon = Mathf.Abs(currentLon - targetLon);
        float latDistanceMeters = distanceLat * 111319.9f;
        float lonDistanceMeters = distanceLon * Mathf.Cos(Mathf.Deg2Rad * currentLat) * 111319.9f;
        float distanceMeters = Mathf.Sqrt(latDistanceMeters * latDistanceMeters + lonDistanceMeters * lonDistanceMeters);
        
        // Removed incorrect reference to 'i' here, no loop index needed.
        Debug.Log($"[GPSLocationManager] Distance to target: {distanceMeters} meters");

        return distanceMeters <= 2000.0f; // Use an appropriate distance threshold
    }
}

感谢任何指导。我试图避免使用 google/mapbox 等付费服务。另外,我认为 Mapbox 不再支持他们的 Unity SDK。我还想以一种可以在 Android 和 iOS 中使用的方式构建它。将来我可能会想看看是否可以添加一个对撞机,并且也许可以在正确的距离内打开 Pokemon go 之类的东西并且与对象进行交互。

预先感谢您的帮助。

我添加了一些调试日志。一切似乎都按预期进行。

我将胶囊的比例更改为 4,80,4,这样我的设备的高度就不会成为问题 - 至少我应该看到它们的某些部分伸出地面。

c# unity-game-engine mobile gps augmented-reality
1个回答
0
投票

据我所知,ARAnchorManager 提供基于地理位置的服务,我认为这就是您在这里寻找的服务。

从您的代码中,我看到您正在尝试:

  1. 创建一个游戏对象(在本例中为您的胶囊)。
  2. 将其放置在一个位置,以您设备的 GPS 坐标为参考。
  3. 然后添加了一个 ARAnchor 组件来跟踪它。

所有这些听起来都很合乎逻辑,但是您还必须考虑一些因素:

  1. 它们似乎就在我附近,并且与罗盘方向相差约 90 度。

XROrigin 变换在应用程序启动时为 (0,0,0)。这意味着无论您的应用程序在何处启动,XROrigin 都会假定该位置是北。使用转换后的 GPS 到 Unity 值将对象放置在场景中,可以让应用程序知道您希望将它们放置在特定位置,并假设您的设备在朝北时启动。

根据手机罗盘值校正 XROrigin 可能有点棘手。我建议首先通过朝北启动您的应用程序进行测试。如果方向正确,那么您就找到了问题并从哪里开始搜索。

  1. 我试图避免使用 google/mapbox 等付费服务。另外,我认为 Mapbox 不再支持他们的 Unity SDK。

我不确定您以何种方式表示谷歌是一项付费服务。我一直免费使用他们的 API。至于 Mapbox,这取决于您计划使用他们的 API 或 SDK。如果您只是想使用他们的地图工具包,它是免费的并且并不真正需要积极的支持。

  1. GPS 不准确。

对于许多决定在本地计算数据的基于位置的应用程序来说,这是一个大问题。众所周知,仅 GPS 就有误差。在视野开阔的情况下,您最多可以获得 5 米的准确度;在城市地区,您最多可以获得 10-20 米的准确度。这完全取决于您需要应用程序的灵敏度和准确性。如果您需要超过 5 米的精度,那么您最好使用基于地理位置的服务,例如 Google Geospatial API,它们结合使用 GPS 和视觉定位系统 (VPS) 来提供更精确的位置数据。

© www.soinside.com 2019 - 2024. All rights reserved.