127 lines
4.8 KiB
C#
127 lines
4.8 KiB
C#
using UnityEngine;
|
||
using UnityEngine.EventSystems;
|
||
|
||
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<RectTransform>();
|
||
if (pointerRect == null)
|
||
{
|
||
Debug.LogError("UIPointer: pointer не имеет RectTransform!");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError("UIPointer: pointer не назначен!");
|
||
}
|
||
|
||
// Если content не задан, пытаемся найти его автоматически
|
||
if (content == null)
|
||
{
|
||
content = GetComponent<RectTransform>();
|
||
}
|
||
}
|
||
|
||
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<RectTransform>();
|
||
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<RectTransform>();
|
||
if (selectedRect == null || !selectedRect.IsChildOf(content)) return;
|
||
|
||
pointerRect.anchoredPosition = GetTargetPosition(selectedRect);
|
||
}
|
||
}
|