TH1/Unity/Assets/Scripts/TH1_UI/HintUI/HintWindowController.cs
2025-09-20 02:56:54 +08:00

86 lines
3.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
{
// 1. 获取 Canvas 的缩放因子,这是解决打包后问题的关键
float scaleFactor = _rootCanvas.scaleFactor;
if (scaleFactor <= 0) scaleFactor = 1f; // 极端情况下的保护
// 2. 计算提示窗口在屏幕上的实际像素尺寸
float windowW = _selfRt.rect.width * scaleFactor;
float windowH = _selfRt.rect.height * scaleFactor;
// 3. 计算窗口左上角假想的屏幕坐标 (这里逻辑保持不变)
Vector2 desiredTopLeft = mouseScreenPos + _anchorOffset;
// 4. 修正:将假想的左上角位置限制在屏幕范围内
// 使用 Screen.width 和 Screen.height 更直接
float clampedX = Mathf.Clamp(desiredTopLeft.x, 0, Screen.width - windowW);
float clampedY = Mathf.Clamp(desiredTopLeft.y, windowH, Screen.height); // Y坐标从下到上所以下边界是windowH
// 5. 关键修正根据Pivot计算最终实际要设置的屏幕坐标点
// 我们已经得到了窗口内容左上角的正确位置(clampedX, clampedY)。
// 现在需要反推出在这种情况下窗口的Pivot应该在屏幕的哪个坐标。
Vector2 pivot = _selfRt.pivot;
Vector2 pivotScreenPos = new Vector2(
clampedX + pivot.x * windowW,
clampedY - (1 - pivot.y) * windowH
);
// 6. 将计算出的Pivot屏幕坐标转换为Canvas的局部坐标
RectTransform canvasRt = _rootCanvas.GetComponent<RectTransform>();
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvasRt,
pivotScreenPos,
_uiCamera, // 即使_uiCamera为null (Overlay模式),此方法依然有效
out Vector2 localPos);
// 7. 设置最终位置
_selfRt.localPosition = localPos;
_selfRt.SetAsLastSibling();
}
}