SimpleCameraPositionMarker.cs 12 KB

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