84 lines
2.6 KiB
C#
84 lines
2.6 KiB
C#
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
|
||
[RequireComponent(typeof(RectTransform))]
|
||
public class HintWindowController : MonoBehaviour
|
||
{
|
||
[SerializeField] private TextMeshProUGUI _text;
|
||
[SerializeField] private Vector2 _padding = new Vector2(16, 10);
|
||
[SerializeField] private Vector2 _anchorOffset = new Vector2(16, 16);
|
||
|
||
private RectTransform _selfRt;
|
||
private Canvas _rootCanvas;
|
||
private Camera _uiCamera;
|
||
|
||
private void Awake()
|
||
{
|
||
_selfRt = (RectTransform)transform;
|
||
_rootCanvas = GetComponentInParent<Canvas>();
|
||
_uiCamera = _rootCanvas.worldCamera;
|
||
|
||
// 兜底找 Text
|
||
if (_text == null)
|
||
_text = GetComponentInChildren<TextMeshProUGUI>(true);
|
||
|
||
if (_text == null)
|
||
Debug.LogError("[HintWindowController] 找不到 TextMeshProUGUI!", this);
|
||
}
|
||
|
||
public void Present(Vector2 mouseScreenPos)
|
||
{
|
||
if (_text == null) return;
|
||
|
||
RefreshSize();
|
||
ClampToScreen(mouseScreenPos);
|
||
}
|
||
|
||
public void RefreshSize()
|
||
{
|
||
_text.ForceMeshUpdate(false, false);
|
||
LayoutRebuilder.ForceRebuildLayoutImmediate(_text.rectTransform);
|
||
|
||
float w = _text.preferredWidth + _padding.x * 2f;
|
||
float h = _text.preferredHeight + _padding.y * 2f;
|
||
|
||
_selfRt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, w);
|
||
_selfRt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, h);
|
||
|
||
Canvas.ForceUpdateCanvases();
|
||
}
|
||
|
||
private void ClampToScreen(Vector2 mouseScreenPos)
|
||
{
|
||
Rect screenRect = _rootCanvas.pixelRect;
|
||
|
||
// 计算窗口左上角假想屏幕坐标
|
||
Vector2 topLeft = mouseScreenPos + _anchorOffset;
|
||
|
||
float windowW = _selfRt.rect.width;
|
||
float windowH = _selfRt.rect.height;
|
||
|
||
// 四边
|
||
float left = topLeft.x;
|
||
float right = left + windowW;
|
||
float top = topLeft.y;
|
||
float bottom = top - windowH; // 注意 Canvas 局部坐标 y 向下为正
|
||
|
||
// 修正到屏幕内
|
||
left = Mathf.Clamp(left, screenRect.xMin, screenRect.xMax - windowW);
|
||
top = Mathf.Clamp(top, screenRect.yMin + windowH, screenRect.yMax);
|
||
|
||
// 转回 Canvas 局部坐标
|
||
RectTransform canvasRt = _rootCanvas.GetComponent<RectTransform>();
|
||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||
canvasRt,
|
||
new Vector2(left, top),
|
||
_uiCamera,
|
||
out Vector2 localPos);
|
||
|
||
_selfRt.localPosition = localPos;
|
||
_selfRt.SetAsLastSibling();
|
||
}
|
||
}
|