332 lines
10 KiB
C#
332 lines
10 KiB
C#
using UnityEngine;
|
||
using System.Collections;
|
||
using UnityEngine.SceneManagement;
|
||
using TMPro; // 添加场景管理命名空间
|
||
|
||
/// <summary>
|
||
/// GPS坐标转换测试器(实时设备GPS版)
|
||
/// 功能:
|
||
/// 1. 实时获取设备GPS作为摄像机原点
|
||
/// 2. 将模型放置在指定GPS坐标
|
||
/// 3. 可视化相对位置
|
||
/// </summary>
|
||
public class GPSTestRunner : MonoBehaviour
|
||
{
|
||
[Header("AR设置")]
|
||
public Transform targetObject; // 目标物体
|
||
public Camera arCamera; // AR主摄像机
|
||
|
||
public Transform otherObject;
|
||
|
||
[Header("GPS配置")]
|
||
[SerializeField] [Range(1, 60)] private int gpsUpdateInterval = 1; // 秒
|
||
[SerializeField] [Range(1, 50)] private float desiredAccuracy = 5; // 米
|
||
[SerializeField] private float initializationTimeout = 20; // 秒
|
||
|
||
[Header("目标坐标")]
|
||
public Vector2 targetGPS = new Vector2(36.123456f, 117.654321f);
|
||
private Quaternion initialRotation; // 新增:保存初始旋转
|
||
|
||
private Vector2 currentDeviceGPS;
|
||
private bool isTracking = false;
|
||
private float lastUpdateTime;
|
||
private bool isRefreshing = false;
|
||
|
||
[Header("区域检测设置")]
|
||
public Vector2 cornerPoint1 = new Vector2(36.66174f, 117.01920f);
|
||
public Vector2 cornerPoint2 = new Vector2(36.66045f, 117.01930f);
|
||
public Vector2 cornerPoint3 = new Vector2(36.66104f, 117.01280f);
|
||
public Vector2 cornerPoint4 = new Vector2(36.66084f, 117.01290f);
|
||
public string targetSceneName = "SampleScene";
|
||
public string otherSceneName = "OtherScene";
|
||
private bool hasCheckedLocation = false;
|
||
|
||
[SerializeField][Header("是否测试")]
|
||
private bool isTest = false;
|
||
|
||
#region 测试
|
||
[SerializeField]private TMP_Text text;
|
||
#endregion
|
||
|
||
IEnumerator StartGPSTracking()
|
||
{
|
||
// 显式请求定位权限(Android需要)
|
||
if (Application.platform == RuntimePlatform.Android)
|
||
{
|
||
if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.FineLocation))
|
||
{
|
||
UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.FineLocation);
|
||
yield return new WaitForSeconds(1);
|
||
}
|
||
}
|
||
|
||
// 检查定位服务是否可用
|
||
if (!Input.location.isEnabledByUser)
|
||
{
|
||
Debug.LogError("定位服务未启用");
|
||
yield break;
|
||
}
|
||
|
||
// 修改启动参数为单次定位
|
||
Input.location.Start(desiredAccuracy, 0.1f); // 第二个参数0.1表示最小距离变化
|
||
|
||
float timer = 0;
|
||
while (Input.location.status == LocationServiceStatus.Initializing &&
|
||
timer < initializationTimeout)
|
||
{
|
||
yield return new WaitForSeconds(1);
|
||
timer++;
|
||
}
|
||
|
||
if (Input.location.status == LocationServiceStatus.Running)
|
||
{
|
||
// 仅获取一次数据后立即停止
|
||
UpdateDevicePosition();
|
||
CheckLocationAndLoadScene(); // 添加检查位置并加载场景
|
||
Input.location.Stop();
|
||
isTracking = true; // 标记为已获取
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"定位初始化失败: {Input.location.status}");
|
||
}
|
||
}
|
||
|
||
void Awake()
|
||
{
|
||
initialRotation = targetObject.rotation; // 保存初始旋转
|
||
}
|
||
|
||
void Start()
|
||
{
|
||
// initialRotation = targetObject.rotation; // 保存初始旋转
|
||
StartCoroutine(StartGPSTracking());
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
if (!isTracking) return;
|
||
|
||
// 每5秒检测一次区域
|
||
if (Time.time % 5f < Time.deltaTime)
|
||
{
|
||
CheckLocationAndLoadScene();
|
||
}
|
||
|
||
// 根据设定间隔更新位置
|
||
if (Time.time - lastUpdateTime >= gpsUpdateInterval)
|
||
{
|
||
UpdateDevicePosition();
|
||
lastUpdateTime = Time.time;
|
||
}
|
||
|
||
UpdateTargetPosition();
|
||
}
|
||
|
||
void UpdateDevicePosition()
|
||
{
|
||
currentDeviceGPS = new Vector2(
|
||
Input.location.lastData.latitude,
|
||
Input.location.lastData.longitude
|
||
);
|
||
Debug.Log($"GPS坐标更新: {currentDeviceGPS}");
|
||
}
|
||
|
||
Vector3 ConvertToLocalSpace(Vector2 target)
|
||
{
|
||
const float metersPerDegree = 111319.488f;
|
||
float latDelta = (target.x - currentDeviceGPS.x) * metersPerDegree;
|
||
float lonDelta = (target.y - currentDeviceGPS.y) * metersPerDegree
|
||
* Mathf.Cos(currentDeviceGPS.x * Mathf.Deg2Rad);
|
||
|
||
return new Vector3(lonDelta, 0, latDelta);
|
||
}
|
||
|
||
void UpdateTargetPosition()
|
||
{
|
||
Vector3 newPosition = ConvertToLocalSpace(targetGPS);
|
||
newPosition.y = targetObject.position.y; // 保持原有Y轴
|
||
|
||
// 仅修改位置,保持初始旋转
|
||
targetObject.position = newPosition;
|
||
targetObject.rotation = initialRotation; // 保持初始旋转
|
||
|
||
Debug.Log($"目标位置更新 - 位置: {newPosition}, 旋转: {initialRotation.eulerAngles}");
|
||
}
|
||
|
||
void OnApplicationQuit()
|
||
{
|
||
if (Input.location.isEnabledByUser)
|
||
Input.location.Stop();
|
||
}
|
||
|
||
// void OnGUI()
|
||
// {
|
||
// GUIStyle labelStyle = new GUIStyle(GUI.skin.label)
|
||
// {
|
||
// fontSize = 24,
|
||
// alignment = TextAnchor.MiddleCenter
|
||
// };
|
||
//
|
||
// if (!isTracking)
|
||
// {
|
||
// GUI.Label(new Rect(Screen.width/2 - 150, Screen.height/2 - 20, 300, 40),
|
||
// "正在初始化定位服务...",
|
||
// labelStyle);
|
||
// }
|
||
// else
|
||
// {
|
||
// string currentGPS = $"纬度: {currentDeviceGPS.x:F7}\n经度: {currentDeviceGPS.y:F7}";
|
||
// string targetPos = $"X: {targetObject.position.x:F7}m\nZ: {targetObject.position.z:F7}m";
|
||
// GUI.Label(new Rect(Screen.width/2 - 200, Screen.height/2 - 60, 400, 80),
|
||
// $"当前坐标:\n{currentGPS}",
|
||
// labelStyle);
|
||
//
|
||
// GUI.Label(new Rect(Screen.width/2 - 200, Screen.height/2 + 20, 400, 80),
|
||
// $"目标位置:\n{targetPos}",
|
||
// labelStyle);
|
||
// }
|
||
// }
|
||
|
||
void OnDrawGizmos()
|
||
{
|
||
if (!isTracking) return;
|
||
|
||
Gizmos.color = Color.green;
|
||
Gizmos.DrawWireCube(targetObject.position, Vector3.one * 2f);
|
||
|
||
Gizmos.color = Color.blue;
|
||
Gizmos.DrawSphere(arCamera.transform.position, 0.5f);
|
||
}
|
||
|
||
public void RequestGPSUpdate()
|
||
{
|
||
if (!isRefreshing)
|
||
{
|
||
StartCoroutine(RefreshGPS());
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("GPS刷新正在进行中");
|
||
}
|
||
}
|
||
|
||
private IEnumerator RefreshGPS()
|
||
{
|
||
isRefreshing = true;
|
||
|
||
// 停止现有服务
|
||
if (Input.location.isEnabledByUser)
|
||
{
|
||
Input.location.Stop();
|
||
yield return new WaitForSeconds(0.5f);
|
||
}
|
||
|
||
// 启动新请求
|
||
Input.location.Start(desiredAccuracy, 0.1f);
|
||
|
||
float timer = 0;
|
||
while (Input.location.status == LocationServiceStatus.Initializing &&
|
||
timer < initializationTimeout)
|
||
{
|
||
timer += Time.deltaTime;
|
||
yield return null;
|
||
}
|
||
|
||
if (Input.location.status == LocationServiceStatus.Running)
|
||
{
|
||
UpdateDevicePosition();
|
||
isTracking = true;
|
||
Debug.Log("GPS手动更新成功");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"GPS更新失败: {Input.location.status}");
|
||
}
|
||
|
||
Input.location.Stop();
|
||
isRefreshing = false;
|
||
}
|
||
|
||
// 添加检查位置并加载场景的方法
|
||
void CheckLocationAndLoadScene()
|
||
{
|
||
if (hasCheckedLocation) return; // 避免重复检查
|
||
|
||
if (IsPointInRegion(currentDeviceGPS))
|
||
{
|
||
Debug.Log("GPS位置在指定区域内,加载场景:" + targetSceneName);
|
||
//SceneManager.LoadScene(targetSceneName);
|
||
otherObject.gameObject.SetActive(false);
|
||
targetObject.gameObject.SetActive(true);
|
||
text.text = "GPS:" + targetSceneName;
|
||
}
|
||
else
|
||
{
|
||
Debug.Log("GPS位置不在指定区域内,加载场景:" + otherSceneName);
|
||
//SceneManager.LoadScene(otherSceneName);
|
||
if(isTest){
|
||
otherObject.gameObject.SetActive(false);
|
||
targetObject.gameObject.SetActive(true);
|
||
}else{
|
||
otherObject.gameObject.SetActive(true);
|
||
targetObject.gameObject.SetActive(false);
|
||
}
|
||
|
||
|
||
text.text = "GPS:" + otherSceneName;
|
||
}
|
||
|
||
hasCheckedLocation = true;
|
||
}
|
||
|
||
// 判断点是否在多边形区域内
|
||
bool IsPointInRegion(Vector2 point)
|
||
{
|
||
Vector2[] vertices = new Vector2[]
|
||
{
|
||
cornerPoint1,
|
||
cornerPoint2,
|
||
cornerPoint4,
|
||
cornerPoint3
|
||
};
|
||
|
||
return IsPointInPolygon(point, vertices);
|
||
}
|
||
|
||
// 使用射线法判断点是否在多边形内
|
||
bool IsPointInPolygon(Vector2 point, Vector2[] polygon)
|
||
{
|
||
int polygonLength = polygon.Length, i = 0;
|
||
bool inside = false;
|
||
|
||
// 射线法: 从点向右发射一条射线,计算与多边形边的交点数量
|
||
float pointX = point.x, pointY = point.y;
|
||
float startX, startY, endX, endY;
|
||
|
||
for (i = 0; i < polygonLength; i++)
|
||
{
|
||
startX = polygon[i].x;
|
||
startY = polygon[i].y;
|
||
endX = polygon[(i + 1) % polygonLength].x;
|
||
endY = polygon[(i + 1) % polygonLength].y;
|
||
|
||
// 检查射线与边的交点
|
||
if (((startY <= pointY && pointY < endY) || (endY <= pointY && pointY < startY)) &&
|
||
(pointX < (endX - startX) * (pointY - startY) / (endY - startY) + startX))
|
||
{
|
||
inside = !inside;
|
||
}
|
||
}
|
||
|
||
return inside;
|
||
}
|
||
|
||
// 添加公共方法,允许从外部触发位置检查
|
||
public void TriggerLocationCheck()
|
||
{
|
||
if (!isTracking) return;
|
||
|
||
CheckLocationAndLoadScene();
|
||
}
|
||
} |