SimpleCameraPositionMarker.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using LightGlue.Unity.Game;
  4. using LightGlue.Unity.Networking;
  5. using LightGlue.Unity.Roma;
  6. using LightGlue.Unity.Config;
  7. namespace LightGlue.Unity.UI
  8. {
  9. /// <summary>
  10. /// 简单的相机位置标记器
  11. /// - 直接将相机坐标映射到屏幕坐标
  12. /// - 只需要一个Image图标,用RectTransform设置位置
  13. /// - 不需要考虑图片大小,直接用坐标定位
  14. /// </summary>
  15. public class SimpleCameraPositionMarker : MonoBehaviour
  16. {
  17. [Header("结果来源")]
  18. [Tooltip("必须指定 LightGlueManager,由其统一分发算法结果。禁止直接从 Bridge 获取结果。")]
  19. public LightGlueManager manager;
  20. [Tooltip("Roma 场景可选:若没有 LightGlueManager,则使用 RomaManager 获取算法结果。")]
  21. public RomaManager romaManager;
  22. [Header("UI图标")]
  23. [Tooltip("要移动的Image图标(在Canvas下的任意Image组件)")]
  24. public Image markerImage;
  25. [Header("参考图像尺寸(用于坐标映射)")]
  26. [Tooltip("参考图像的宽度(像素)")]
  27. public int referenceImageWidth = 640;
  28. [Tooltip("参考图像的高度(像素)")]
  29. public int referenceImageHeight = 480;
  30. [Header("屏幕显示区域")]
  31. [Tooltip("目标显示区域的宽度(像素,通常等于Canvas宽度或屏幕宽度)")]
  32. public float screenWidth = 1920f;
  33. [Tooltip("目标显示区域的高度(像素,通常等于Canvas高度或屏幕高度)")]
  34. public float screenHeight = 1080f;
  35. [Tooltip("是否自动使用Canvas尺寸(如果为true,会自动获取Canvas尺寸)")]
  36. public bool autoUseCanvasSize = true;
  37. [Header("坐标偏移(用于微调位置)")]
  38. [Tooltip("是否自动计算偏移量(如果坐标映射有整体偏移,可以自动修正)")]
  39. public bool autoCalculateOffset = true;
  40. [Tooltip("X轴偏移(像素,如果autoCalculateOffset=false则使用此值)")]
  41. public float offsetX = 0f;
  42. [Tooltip("Y轴偏移(像素,如果autoCalculateOffset=false则使用此值)")]
  43. public float offsetY = 0f;
  44. [Header("平滑参数来源(唯一来源,无本地参数)")]
  45. [Tooltip("留空则使用 LightGlueCursorSettings.Instance;所有平滑参数均由此引用读取,UI 修改后即时生效")]
  46. public LightGlueCursorSettings cursorSettings;
  47. private RectTransform _markerRect;
  48. private Canvas _canvas;
  49. private Vector2 _currentScreenPos;
  50. private bool _hasCurrentScreenPos;
  51. private Vector2 _smoothStartPos;
  52. private Vector2 _smoothTargetPos;
  53. private float _smoothProgress;
  54. // 由 LightGlueManager 分发的最新结果缓存
  55. private LightGlueResult _latestResult;
  56. private bool _hasLatestResult;
  57. private LightGlueCursorSettings GetCursorSettings()
  58. {
  59. if (cursorSettings != null) return cursorSettings;
  60. return LightGlueCursorSettings.Instance;
  61. }
  62. private void OnEnable()
  63. {
  64. ReferenceImageResizeService.EnsureInitialized();
  65. SetReferenceImageSize(ReferenceImageResizeService.Width, ReferenceImageResizeService.Height);
  66. ReferenceImageResizeService.OnResizeChanged -= SetReferenceImageSize;
  67. ReferenceImageResizeService.OnResizeChanged += SetReferenceImageSize;
  68. }
  69. private void OnDisable()
  70. {
  71. ReferenceImageResizeService.OnResizeChanged -= SetReferenceImageSize;
  72. }
  73. private void Start()
  74. {
  75. if (cursorSettings == null)
  76. cursorSettings = LightGlueCursorSettings.Instance;
  77. if (GetCursorSettings() == null)
  78. Debug.LogWarning("[SimpleCameraPositionMarker] 未找到 LightGlueCursorSettings,标记器平滑参数将不可用,请确保 GameManager 已生成设置预制。");
  79. // 结果来源:强制通过全局 LightGlueManager 获取
  80. if (manager == null)
  81. {
  82. manager = LightGlueManager.Instance;
  83. }
  84. if (manager != null)
  85. {
  86. manager.OnResultUpdated += OnResultUpdated;
  87. if (manager.HasLatestResult)
  88. OnResultUpdated(manager.LatestResult);
  89. }
  90. else
  91. {
  92. // Roma:统一通过 RomaManager 获取(RomaManager 内部会自动桥接 RomaHardwareToPythonBridge)
  93. if (romaManager == null)
  94. romaManager = RomaManager.Instance;
  95. if (romaManager != null)
  96. {
  97. romaManager.OnResultUpdated += OnResultUpdated;
  98. if (romaManager.HasLatestResult)
  99. OnResultUpdated(romaManager.LatestResult);
  100. }
  101. else
  102. {
  103. Debug.LogError("[SimpleCameraPositionMarker] 未找到 LightGlueManager 或 RomaManager,无法获取算法结果。");
  104. }
  105. }
  106. // 获取Image的RectTransform
  107. if (markerImage != null)
  108. {
  109. _markerRect = markerImage.GetComponent<RectTransform>();
  110. if (_markerRect == null)
  111. {
  112. Debug.LogError("[SimpleCameraPositionMarker] Image组件必须附加在带有RectTransform的GameObject上");
  113. }
  114. else
  115. {
  116. // 确保anchor和pivot设置正确(左下角anchor,中心pivot)
  117. _markerRect.anchorMin = new Vector2(0, 0);
  118. _markerRect.anchorMax = new Vector2(0, 0);
  119. _markerRect.pivot = new Vector2(0.5f, 0.5f); // 中心pivot,这样图标会以中心点定位
  120. // 输出当前设置用于调试
  121. Debug.Log($"[SimpleCameraPositionMarker] RectTransform设置: " +
  122. $"anchor=({_markerRect.anchorMin.x},{_markerRect.anchorMin.y}), " +
  123. $"pivot=({_markerRect.pivot.x},{_markerRect.pivot.y}), " +
  124. $"当前位置=({_markerRect.anchoredPosition.x},{_markerRect.anchoredPosition.y})");
  125. }
  126. }
  127. else
  128. {
  129. Debug.LogWarning("[SimpleCameraPositionMarker] 请指定要移动的Image图标");
  130. }
  131. // 自动获取Canvas尺寸
  132. if (autoUseCanvasSize)
  133. {
  134. if (markerImage != null)
  135. {
  136. _canvas = markerImage.canvas;
  137. if (_canvas != null)
  138. {
  139. RectTransform canvasRect = _canvas.GetComponent<RectTransform>();
  140. if (canvasRect != null)
  141. {
  142. screenWidth = canvasRect.rect.width;
  143. screenHeight = canvasRect.rect.height;
  144. Debug.Log($"[SimpleCameraPositionMarker] 自动获取Canvas尺寸: {screenWidth}x{screenHeight}");
  145. }
  146. }
  147. }
  148. // 如果还是没找到Canvas,使用屏幕尺寸
  149. if (screenWidth <= 0 || screenHeight <= 0)
  150. {
  151. screenWidth = Screen.width;
  152. screenHeight = Screen.height;
  153. Debug.Log($"[SimpleCameraPositionMarker] 使用屏幕尺寸: {screenWidth}x{screenHeight}");
  154. }
  155. }
  156. // 自动计算偏移量(如果需要)
  157. if (autoCalculateOffset)
  158. {
  159. // 如果anchor设置为左下角(0,0)但实际需要中心点偏移,自动计算
  160. // 通常这种情况是因为Canvas坐标系原点在中心而不是左下角
  161. // 偏移量 = -中心点坐标
  162. offsetX = -screenWidth * 0.5f;
  163. offsetY = -screenHeight * 0.5f;
  164. Debug.Log($"[SimpleCameraPositionMarker] 自动计算偏移量: offsetX={offsetX:F1}, offsetY={offsetY:F1} (Canvas中心点偏移)");
  165. }
  166. }
  167. private void Update()
  168. {
  169. if (manager == null && romaManager == null)
  170. return;
  171. if (!_hasLatestResult || !_latestResult.IsValid)
  172. return;
  173. LightGlueCursorSettings settings = GetCursorSettings();
  174. if (settings == null)
  175. return;
  176. var result = _latestResult;
  177. Vector2 targetScreenPos = result.GetMappedPosition(
  178. referenceWidth: referenceImageWidth,
  179. referenceHeight: referenceImageHeight,
  180. targetWidth: screenWidth,
  181. targetHeight: screenHeight,
  182. type: LightGlueResult.MappedCoordinateType.UIAnchored,
  183. offset: new Vector2(offsetX, offsetY));
  184. bool useSmooth = settings.markerEnableSmoothFollow;
  185. float lerpSpeed = settings.markerPositionLerpSpeed;
  186. float jitter = settings.markerJitterThresholdPixels;
  187. AnimationCurve curve = settings.markerSmoothCurve;
  188. if (!useSmooth || !_hasCurrentScreenPos)
  189. {
  190. _currentScreenPos = targetScreenPos;
  191. _hasCurrentScreenPos = true;
  192. _smoothStartPos = targetScreenPos;
  193. _smoothTargetPos = targetScreenPos;
  194. _smoothProgress = 1f;
  195. }
  196. else
  197. {
  198. if (jitter > 0f && Vector2.Distance(_currentScreenPos, targetScreenPos) < jitter)
  199. targetScreenPos = _currentScreenPos;
  200. _smoothTargetPos = targetScreenPos;
  201. if (_smoothProgress >= 1f)
  202. {
  203. _smoothStartPos = _currentScreenPos;
  204. _smoothProgress = 0f;
  205. }
  206. _smoothProgress += Time.deltaTime * lerpSpeed;
  207. _smoothProgress = Mathf.Clamp01(_smoothProgress);
  208. float t = (curve != null && curve.keys.Length > 0) ? curve.Evaluate(_smoothProgress) : _smoothProgress;
  209. t = Mathf.Clamp01(t);
  210. _currentScreenPos = Vector2.Lerp(_smoothStartPos, _smoothTargetPos, t);
  211. }
  212. if (_markerRect != null)
  213. {
  214. _markerRect.anchoredPosition = _currentScreenPos;
  215. }
  216. // 调试输出(每30帧输出一次,避免日志过多)
  217. //if (Time.frameCount % 30 == 0)
  218. //{
  219. // Debug.Log($"[SimpleCameraPositionMarker] 坐标映射: " +
  220. // $"参考图({result.CameraPosition.x:F1},{result.CameraPosition.y:F1}) -> " +
  221. // $"屏幕({targetScreenPos.x:F1},{targetScreenPos.y:F1}), " +
  222. // $"Canvas尺寸={screenWidth}x{screenHeight}, " +
  223. // $"参考图尺寸={referenceImageWidth}x{referenceImageHeight}");
  224. //}
  225. // 显示图标
  226. if (markerImage != null && !markerImage.gameObject.activeSelf)
  227. {
  228. markerImage.gameObject.SetActive(true);
  229. }
  230. }
  231. /// <summary>
  232. /// 设置参考图像尺寸(供外部调用)
  233. /// </summary>
  234. public void SetReferenceImageSize(int width, int height)
  235. {
  236. referenceImageWidth = width;
  237. referenceImageHeight = height;
  238. }
  239. /// <summary>
  240. /// 设置屏幕显示区域尺寸(供外部调用)
  241. /// </summary>
  242. public void SetScreenSize(float width, float height)
  243. {
  244. screenWidth = width;
  245. screenHeight = height;
  246. }
  247. /// <summary>
  248. /// 设置坐标偏移(供外部调用)
  249. /// </summary>
  250. public void SetOffset(float x, float y)
  251. {
  252. offsetX = x;
  253. offsetY = y;
  254. }
  255. /// <summary>
  256. /// 由 LightGlueManager 触发的结果更新回调,仅做结果缓存,坐标计算在 Update 每帧执行。
  257. /// </summary>
  258. private void OnResultUpdated(LightGlueResult result)
  259. {
  260. _latestResult = result;
  261. _hasLatestResult = true;
  262. }
  263. private void OnDestroy()
  264. {
  265. if (manager != null)
  266. {
  267. manager.OnResultUpdated -= OnResultUpdated;
  268. }
  269. if (romaManager != null)
  270. {
  271. romaManager.OnResultUpdated -= OnResultUpdated;
  272. }
  273. }
  274. }
  275. }