using UnityEngine; using LightGlue.Unity.Game; using LightGlue.Unity.Networking; using LightGlue.Unity.Roma; using LightGlue.Unity.Runtime; using LightGlue.Unity.Config; namespace LightGlue.Unity.UI { /// /// 专用于游戏内的 LightGlue 相机朝向控制器 /// - 从 HardwareToPythonUdpBridge 读取算法返回的相机坐标(参考图像坐标系) /// - 将参考图坐标映射到游戏实际屏幕坐标(Screen.width / Screen.height) /// - 使用 ScreenPointToRay 计算射线方向,并更新 CameraToLook.ins.localRotation /// - 不依赖 UI Image,不会与 SimpleCameraPositionMarker 的 UI 逻辑耦合或冲突 /// public class LightGlueCameraAimController : MonoBehaviour { [Header("事件驱动(推荐插件模式)")] [Tooltip("为 true 时,仅使用 LightGlueRuntimeFacade.OnPositionUpdate 作为结果来源,不再依赖 LightGlueManager/RomaManager。")] public bool useOnPositionUpdate = true; [Header("结果来源")] [Tooltip("优先使用 LightGlueManager(旧场景);若为空则回退到 RomaManager(新 Roma 场景)。禁止直接从 Bridge 获取结果。")] public LightGlueManager manager; [Tooltip("Roma 场景下的结果来源。通常留空,自动使用 RomaManager.Instance。")] public RomaManager romaManager; [Header("参考图像尺寸(必须与 Python --resize 一致)")] [Tooltip("参考图像宽度(像素)")] public int referenceImageWidth = 640; [Tooltip("参考图像高度(像素)")] public int referenceImageHeight = 480; [Header("平滑参数来源(唯一来源,无本地参数)")] [Tooltip("留空则使用 LightGlueCursorSettings.Instance;所有平滑参数均由此引用读取,UI 修改后即时生效")] public LightGlueCursorSettings cursorSettings; private Quaternion _currentRotation; private bool _hasCurrentRotation; private Quaternion _smoothStartRot; private Quaternion _smoothTargetRot; private float _smoothProgress; private LightGluePositionUpdate _latestUpdate; private bool _hasLatestUpdate; private LightGlueCursorSettings GetCursorSettings() { if (cursorSettings != null) return cursorSettings; return LightGlueCursorSettings.Instance; } private void OnEnable() { ReferenceImageResizeService.EnsureInitialized(); SetReferenceImageSize(ReferenceImageResizeService.Width, ReferenceImageResizeService.Height); ReferenceImageResizeService.OnResizeChanged += SetReferenceImageSize; if (!useOnPositionUpdate) return; LightGlueRuntimeFacade.Ensure(); LightGlueRuntimeFacade.OnPositionUpdate += HandlePositionUpdate; } private void OnDisable() { ReferenceImageResizeService.OnResizeChanged -= SetReferenceImageSize; if (!useOnPositionUpdate) return; LightGlueRuntimeFacade.OnPositionUpdate -= HandlePositionUpdate; } private void HandlePositionUpdate(LightGluePositionUpdate update) { _latestUpdate = update; _hasLatestUpdate = true; } private void Start() { if (cursorSettings == null) cursorSettings = LightGlueCursorSettings.Instance; if (GetCursorSettings() == null) Debug.LogWarning("[LightGlueCameraAimController] 未找到 LightGlueCursorSettings,相机朝向平滑参数将不可用,请确保 GameManager 已生成设置预制。"); if (!useOnPositionUpdate) { if (manager == null) manager = LightGlueManager.Instance; if (manager == null && romaManager == null) romaManager = RomaManager.Instance; if (manager == null && romaManager == null) Debug.LogError("[LightGlueCameraAimController] 未找到 LightGlueManager / RomaManager,无法获取算法结果。请确认场景已生成对应 System 预制。"); } } private void Update() { LightGlueResult result; if (useOnPositionUpdate) { if (!_hasLatestUpdate) return; if (!_latestUpdate.IsValid) return; // Position/CameraLocation 均为参考图坐标(左上原点、Y向下) Vector2 p = _latestUpdate.CameraLocation != default ? _latestUpdate.CameraLocation : _latestUpdate.Position; result = new LightGlueResult( isValid: _latestUpdate.IsValid, numMatches: _latestUpdate.NumMatches, inliersRatio: _latestUpdate.InliersRatio, cameraX: p.x, cameraY: p.y); } else { if (manager != null) { if (!manager.HasLatestResult) return; result = manager.LatestResult; } else { if (romaManager == null) romaManager = RomaManager.Instance; if (romaManager == null || !romaManager.HasLatestResult) return; result = romaManager.LatestResult; } } LightGlueCursorSettings settings = GetCursorSettings(); if (settings == null) return; if (!result.IsValid) return; if (Camera.main == null || CameraToLook.ins == null) return; Vector2 gameScreenPos = result.GetMappedPosition( referenceWidth: referenceImageWidth, referenceHeight: referenceImageHeight, targetWidth: Screen.width, targetHeight: Screen.height, type: LightGlueResult.MappedCoordinateType.GameScreen); Vector3 screenPoint = new Vector3(gameScreenPos.x, gameScreenPos.y, 0f); Ray ray = Camera.main.ScreenPointToRay(screenPoint); Vector3 rayEndPoint = ray.GetPoint(200f); Quaternion targetRotation = Quaternion.LookRotation(rayEndPoint - Camera.main.transform.position); targetRotation = Quaternion.AngleAxis(-180f, Vector3.up) * targetRotation; bool useSmooth = settings.cameraAimEnableSmoothRotation; float lerpSpeed = settings.cameraAimRotationLerpSpeed; float jitterDeg = settings.cameraAimRotationJitterThresholdDeg; AnimationCurve curve = settings.cameraAimSmoothCurve; if (!useSmooth || !_hasCurrentRotation) { _currentRotation = targetRotation; _hasCurrentRotation = true; _smoothStartRot = targetRotation; _smoothTargetRot = targetRotation; _smoothProgress = 1f; } else { if (jitterDeg > 0f) { float angle = Quaternion.Angle(_currentRotation, targetRotation); if (angle < jitterDeg) { CameraToLook.ins.localRotation = _currentRotation; return; } } _smoothTargetRot = targetRotation; if (_smoothProgress >= 1f) { _smoothStartRot = _currentRotation; _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); _currentRotation = Quaternion.Slerp(_smoothStartRot, _smoothTargetRot, t); } CameraToLook.ins.localRotation = _currentRotation; } /// /// 允许在运行时从外部调整参考图像尺寸(例如根据 Python 参数动态设置) /// public void SetReferenceImageSize(int width, int height) { referenceImageWidth = width; referenceImageHeight = height; } } }