Trying to learn Unity and have created a project where I want just a simple 3D Object (a capsule prefab) to appear in my iPhones camera at various GPS locations (all within the field of view of my work desk). I have the following in my Scene Hierarchy:
AR Session Directional Light UI XR Origin (AR Rig) -Camera Offset -- Main Camera -- XR Screen Space Controller GPS Manager
I am getting the capsules to appear and they seem to be sized appropriately based on their distance from me. but they are not where the GPS coordinates say they should be. they seem to be right near me and about 90 degrees off in terms of compass direction.
here is the GPS Manager Script that I linked to the capsule prefabs -
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
}
}
Appreciate any guidance. I am trying to avoid using paid services like google/mapbox. Additionally I don't think Mapbox supports their Unity SDK any more. Also I'd like to build it in a way that can be used in Android and iOS. In the future I will likely want to see if I can add a collider and maybe open something like Pokemon go when within the correct proximity and the object is interacted with.
Thanks in advance for any help.
I added a number of Debug logs. and everything seems to be working as expected.
I changed the scale for the capsule to 4,80,4 so that the altitude of my device would not be an issue - at least I should see some portion of them sticking out of the ground.