using UnityEngine; using UnityEngine.EventSystems; namespace RR.Common.UI { public class UIPointer : MonoBehaviour { [Header("Настройки")] [SerializeField] private GameObject pointer; [SerializeField] private RectTransform content; // Content ScrollView, где находятся элементы [SerializeField] private float smoothSpeed = 10f; // Скорость перемещения указателя [SerializeField] private Vector2 offset = Vector2.zero; // Смещение указателя относительно элемента private RectTransform pointerRect; private void Start() { if (pointer != null) { pointerRect = pointer.GetComponent(); if (pointerRect == null) { Debug.LogError("UIPointer: pointer не имеет RectTransform!"); } } else { Debug.LogError("UIPointer: pointer не назначен!"); } // Если content не задан, пытаемся найти его автоматически if (content == null) { content = GetComponent(); } } private void LateUpdate() { if (!pointerRect || !content) return; // Получаем текущий выбранный элемент из EventSystem GameObject selected = EventSystem.current?.currentSelectedGameObject; // Если ничего не выбрано, скрываем указатель if (selected == null) { if (pointer.activeSelf) pointer.SetActive(false); return; } // Проверяем, что выбранный элемент находится внутри нашего content RectTransform selectedRect = selected.GetComponent(); if (!selectedRect || !selectedRect.IsChildOf(content)) { if (pointer.activeSelf) pointer.SetActive(false); return; } // Показываем указатель, если он был скрыт if (!pointer.activeSelf) pointer.SetActive(true); // Вычисляем целевую позицию Vector2 targetPos = GetTargetPosition(selectedRect); // Плавно перемещаем указатель if (smoothSpeed > 0) { pointerRect.anchoredPosition = Vector2.Lerp( pointerRect.anchoredPosition, targetPos, Time.unscaledDeltaTime * smoothSpeed ); } else { pointerRect.anchoredPosition = targetPos; } } private Vector2 GetTargetPosition(RectTransform target) { // Получаем позицию целевого элемента относительно content Vector2 targetAnchoredPos = target.anchoredPosition; // Если элементы не прямые дети content, нужно пересчитать позицию if (target.parent == content) return new Vector2( pointerRect.anchoredPosition.x + offset.x, // X оставляем как есть (или применяем offset) targetAnchoredPos.y + offset.y // Y берем от целевого элемента ); // Конвертируем мировую позицию в локальную позицию content Vector3[] corners = new Vector3[4]; target.GetWorldCorners(corners); Vector3 center = (corners[0] + corners[2]) * 0.5f; RectTransformUtility.ScreenPointToLocalPointInRectangle( content, RectTransformUtility.WorldToScreenPoint(null, center), null, out Vector2 localPos ); targetAnchoredPos = localPos; // Применяем смещение return new Vector2( pointerRect.anchoredPosition.x + offset.x, // X оставляем как есть (или применяем offset) targetAnchoredPos.y + offset.y // Y берем от целевого элемента ); } // Метод для ручного обновления позиции указателя (если нужно) public void UpdatePointerPosition() { if (pointerRect == null || content == null) return; GameObject selected = EventSystem.current?.currentSelectedGameObject; if (selected == null) return; RectTransform selectedRect = selected.GetComponent(); if (selectedRect == null || !selectedRect.IsChildOf(content)) return; pointerRect.anchoredPosition = GetTargetPosition(selectedRect); } } }