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;
}
}
}
}