using UnityEngine; using UnityEngine.UI; using LightGlue.Unity.Game; using LightGlue.Unity.Networking; using LightGlue.Unity.Roma; using LightGlue.Unity.Config; namespace LightGlue.Unity.UI { /// /// 简单的相机位置标记器 /// - 直接将相机坐标映射到屏幕坐标 /// - 只需要一个Image图标,用RectTransform设置位置 /// - 不需要考虑图片大小,直接用坐标定位 /// public class SimpleCameraPositionMarker : MonoBehaviour { [Header("结果来源")] [Tooltip("必须指定 LightGlueManager,由其统一分发算法结果。禁止直接从 Bridge 获取结果。")] public LightGlueManager manager; [Tooltip("Roma 场景可选:若没有 LightGlueManager,则使用 RomaManager 获取算法结果。")] public RomaManager romaManager; [Header("UI图标")] [Tooltip("要移动的Image图标(在Canvas下的任意Image组件)")] public Image markerImage; [Header("参考图像尺寸(用于坐标映射)")] [Tooltip("参考图像的宽度(像素)")] public int referenceImageWidth = 640; [Tooltip("参考图像的高度(像素)")] public int referenceImageHeight = 480; [Header("屏幕显示区域")] [Tooltip("目标显示区域的宽度(像素,通常等于Canvas宽度或屏幕宽度)")] public float screenWidth = 1920f; [Tooltip("目标显示区域的高度(像素,通常等于Canvas高度或屏幕高度)")] public float screenHeight = 1080f; [Tooltip("是否自动使用Canvas尺寸(如果为true,会自动获取Canvas尺寸)")] public bool autoUseCanvasSize = true; [Header("坐标偏移(用于微调位置)")] [Tooltip("是否自动计算偏移量(如果坐标映射有整体偏移,可以自动修正)")] public bool autoCalculateOffset = true; [Tooltip("X轴偏移(像素,如果autoCalculateOffset=false则使用此值)")] public float offsetX = 0f; [Tooltip("Y轴偏移(像素,如果autoCalculateOffset=false则使用此值)")] public float offsetY = 0f; [Header("平滑参数来源(唯一来源,无本地参数)")] [Tooltip("留空则使用 LightGlueCursorSettings.Instance;所有平滑参数均由此引用读取,UI 修改后即时生效")] public LightGlueCursorSettings cursorSettings; private RectTransform _markerRect; private Canvas _canvas; private Vector2 _currentScreenPos; private bool _hasCurrentScreenPos; private Vector2 _smoothStartPos; private Vector2 _smoothTargetPos; private float _smoothProgress; // 由 LightGlueManager 分发的最新结果缓存 private LightGlueResult _latestResult; private bool _hasLatestResult; private LightGlueCursorSettings GetCursorSettings() { if (cursorSettings != null) return cursorSettings; return LightGlueCursorSettings.Instance; } private void OnEnable() { ReferenceImageResizeService.EnsureInitialized(); SetReferenceImageSize(ReferenceImageResizeService.Width, ReferenceImageResizeService.Height); ReferenceImageResizeService.OnResizeChanged -= SetReferenceImageSize; ReferenceImageResizeService.OnResizeChanged += SetReferenceImageSize; } private void OnDisable() { ReferenceImageResizeService.OnResizeChanged -= SetReferenceImageSize; } private void Start() { if (cursorSettings == null) cursorSettings = LightGlueCursorSettings.Instance; if (GetCursorSettings() == null) Debug.LogWarning("[SimpleCameraPositionMarker] 未找到 LightGlueCursorSettings,标记器平滑参数将不可用,请确保 GameManager 已生成设置预制。"); // 结果来源:强制通过全局 LightGlueManager 获取 if (manager == null) { manager = LightGlueManager.Instance; } if (manager != null) { manager.OnResultUpdated += OnResultUpdated; if (manager.HasLatestResult) OnResultUpdated(manager.LatestResult); } else { // Roma:统一通过 RomaManager 获取(RomaManager 内部会自动桥接 RomaHardwareToPythonBridge) if (romaManager == null) romaManager = RomaManager.Instance; if (romaManager != null) { romaManager.OnResultUpdated += OnResultUpdated; if (romaManager.HasLatestResult) OnResultUpdated(romaManager.LatestResult); } else { Debug.LogError("[SimpleCameraPositionMarker] 未找到 LightGlueManager 或 RomaManager,无法获取算法结果。"); } } // 获取Image的RectTransform if (markerImage != null) { _markerRect = markerImage.GetComponent(); if (_markerRect == null) { Debug.LogError("[SimpleCameraPositionMarker] Image组件必须附加在带有RectTransform的GameObject上"); } else { // 确保anchor和pivot设置正确(左下角anchor,中心pivot) _markerRect.anchorMin = new Vector2(0, 0); _markerRect.anchorMax = new Vector2(0, 0); _markerRect.pivot = new Vector2(0.5f, 0.5f); // 中心pivot,这样图标会以中心点定位 // 输出当前设置用于调试 Debug.Log($"[SimpleCameraPositionMarker] RectTransform设置: " + $"anchor=({_markerRect.anchorMin.x},{_markerRect.anchorMin.y}), " + $"pivot=({_markerRect.pivot.x},{_markerRect.pivot.y}), " + $"当前位置=({_markerRect.anchoredPosition.x},{_markerRect.anchoredPosition.y})"); } } else { Debug.LogWarning("[SimpleCameraPositionMarker] 请指定要移动的Image图标"); } // 自动获取Canvas尺寸 if (autoUseCanvasSize) { if (markerImage != null) { _canvas = markerImage.canvas; if (_canvas != null) { RectTransform canvasRect = _canvas.GetComponent(); if (canvasRect != null) { screenWidth = canvasRect.rect.width; screenHeight = canvasRect.rect.height; Debug.Log($"[SimpleCameraPositionMarker] 自动获取Canvas尺寸: {screenWidth}x{screenHeight}"); } } } // 如果还是没找到Canvas,使用屏幕尺寸 if (screenWidth <= 0 || screenHeight <= 0) { screenWidth = Screen.width; screenHeight = Screen.height; Debug.Log($"[SimpleCameraPositionMarker] 使用屏幕尺寸: {screenWidth}x{screenHeight}"); } } // 自动计算偏移量(如果需要) if (autoCalculateOffset) { // 如果anchor设置为左下角(0,0)但实际需要中心点偏移,自动计算 // 通常这种情况是因为Canvas坐标系原点在中心而不是左下角 // 偏移量 = -中心点坐标 offsetX = -screenWidth * 0.5f; offsetY = -screenHeight * 0.5f; Debug.Log($"[SimpleCameraPositionMarker] 自动计算偏移量: offsetX={offsetX:F1}, offsetY={offsetY:F1} (Canvas中心点偏移)"); } } private void Update() { if (manager == null && romaManager == null) return; if (!_hasLatestResult || !_latestResult.IsValid) return; LightGlueCursorSettings settings = GetCursorSettings(); if (settings == null) return; var result = _latestResult; Vector2 targetScreenPos = result.GetMappedPosition( referenceWidth: referenceImageWidth, referenceHeight: referenceImageHeight, targetWidth: screenWidth, targetHeight: screenHeight, type: LightGlueResult.MappedCoordinateType.UIAnchored, offset: new Vector2(offsetX, offsetY)); bool useSmooth = settings.markerEnableSmoothFollow; float lerpSpeed = settings.markerPositionLerpSpeed; float jitter = settings.markerJitterThresholdPixels; AnimationCurve curve = settings.markerSmoothCurve; if (!useSmooth || !_hasCurrentScreenPos) { _currentScreenPos = targetScreenPos; _hasCurrentScreenPos = true; _smoothStartPos = targetScreenPos; _smoothTargetPos = targetScreenPos; _smoothProgress = 1f; } else { if (jitter > 0f && Vector2.Distance(_currentScreenPos, targetScreenPos) < jitter) targetScreenPos = _currentScreenPos; _smoothTargetPos = targetScreenPos; if (_smoothProgress >= 1f) { _smoothStartPos = _currentScreenPos; _smoothProgress = 0f; } _smoothProgress += Time.deltaTime * lerpSpeed; _smoothProgress = Mathf.Clamp01(_smoothProgress); float t = (curve != null && curve.keys.Length > 0) ? curve.Evaluate(_smoothProgress) : _smoothProgress; t = Mathf.Clamp01(t); _currentScreenPos = Vector2.Lerp(_smoothStartPos, _smoothTargetPos, t); } if (_markerRect != null) { _markerRect.anchoredPosition = _currentScreenPos; } // 调试输出(每30帧输出一次,避免日志过多) //if (Time.frameCount % 30 == 0) //{ // Debug.Log($"[SimpleCameraPositionMarker] 坐标映射: " + // $"参考图({result.CameraPosition.x:F1},{result.CameraPosition.y:F1}) -> " + // $"屏幕({targetScreenPos.x:F1},{targetScreenPos.y:F1}), " + // $"Canvas尺寸={screenWidth}x{screenHeight}, " + // $"参考图尺寸={referenceImageWidth}x{referenceImageHeight}"); //} // 显示图标 if (markerImage != null && !markerImage.gameObject.activeSelf) { markerImage.gameObject.SetActive(true); } } /// /// 设置参考图像尺寸(供外部调用) /// public void SetReferenceImageSize(int width, int height) { referenceImageWidth = width; referenceImageHeight = height; } /// /// 设置屏幕显示区域尺寸(供外部调用) /// public void SetScreenSize(float width, float height) { screenWidth = width; screenHeight = height; } /// /// 设置坐标偏移(供外部调用) /// public void SetOffset(float x, float y) { offsetX = x; offsetY = y; } /// /// 由 LightGlueManager 触发的结果更新回调,仅做结果缓存,坐标计算在 Update 每帧执行。 /// private void OnResultUpdated(LightGlueResult result) { _latestResult = result; _hasLatestResult = true; } private void OnDestroy() { if (manager != null) { manager.OnResultUpdated -= OnResultUpdated; } if (romaManager != null) { romaManager.OnResultUpdated -= OnResultUpdated; } } } }