- Round 8 report highlights improvements in architecture, editor usability, and data robustness, with a total score of 80/100. - Round 9 report focuses on editor extension capabilities, identifying issues with room data indexing and layout editing, resulting in a score of 76/100. - Round 26 report evaluates the system against commercial standards, noting new issues and confirming previous fixes, with a score of 95.8/100.
128 lines
5.6 KiB
C#
128 lines
5.6 KiB
C#
using UnityEngine;
|
||
using UnityEngine.EventSystems;
|
||
using UnityEngine.UI;
|
||
using BaseGames.Input;
|
||
|
||
namespace BaseGames.World.Map
|
||
{
|
||
/// <summary>
|
||
/// 全屏地图输入处理器(架构 15_MapShopModule §1.3.1)。
|
||
/// 挂在与 MapPanel 相同的 GameObject 上(MapPanel OnEnable/OnDisable 联动启停)。
|
||
/// <list type="bullet">
|
||
/// <item>鼠标滚轮缩放(以鼠标位置为缩放中心)</item>
|
||
/// <item>方向键 / 摇杆平移(经由 InputReaderSO.NavigateEvent 派发)</item>
|
||
/// </list>
|
||
/// </summary>
|
||
[RequireComponent(typeof(MapPanel))]
|
||
public class MapInputHandler : MonoBehaviour, IScrollHandler
|
||
{
|
||
[SerializeField] private InputReaderSO _inputReader;
|
||
[SerializeField] private ScrollRect _scrollRect;
|
||
[SerializeField] private RectTransform _zoomTarget; // 通常为 _roomContainer(格子根节点)
|
||
|
||
[Header("缩放")]
|
||
[SerializeField, Range(0.2f, 1f)] private float _zoomMin = 0.4f;
|
||
[SerializeField, Range(1f, 5f)] private float _zoomMax = 3.0f;
|
||
[SerializeField, Range(0.05f, 0.5f)] private float _zoomStep = 0.12f;
|
||
|
||
[Header("平移")]
|
||
[SerializeField] private float _keyPanSpeed = 600f; // px / 秒
|
||
|
||
private Vector2 _navInput;
|
||
private MapPanel _panel;
|
||
|
||
private void Awake()
|
||
{
|
||
_panel = GetComponent<MapPanel>();
|
||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||
// R25-N2 _zoomTarget 须配置为 MapPanel 的格子根节点(与 _roomContainer 相同),
|
||
// 否则 OnScroll 写入的 scale 与 MapPanel.CurrentZoom 读取的 scale 将来自不同节点,导致静默状态分裂。
|
||
if (_zoomTarget == null)
|
||
Debug.LogWarning("[MapInputHandler] _zoomTarget 未配置,缩放功能将失效。请在 Inspector 中指定 MapPanel 的格子根节点(_roomContainer)。", this);
|
||
#endif
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
if (_inputReader != null)
|
||
{
|
||
_inputReader.NavigateEvent += OnNavigate;
|
||
// R13-N4 居中到玩家快捷键
|
||
_inputReader.MapCenterEvent += OnMapCenter;
|
||
}
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
_navInput = Vector2.zero;
|
||
|
||
if (_inputReader != null)
|
||
{
|
||
_inputReader.NavigateEvent -= OnNavigate;
|
||
_inputReader.MapCenterEvent -= OnMapCenter;
|
||
}
|
||
}
|
||
|
||
private void OnNavigate(Vector2 dir) => _navInput = dir;
|
||
|
||
// R13-N4 将视图居中到玩家所在房间
|
||
private void OnMapCenter() => _panel?.CenterOnCurrentRoom();
|
||
|
||
private void Update()
|
||
{
|
||
if (_scrollRect == null || _navInput == Vector2.zero) return;
|
||
|
||
// R18-N1 contentSize 需乘以当前缩放系数,否则缩放后平移速度感知偏差。
|
||
// R19-N1 / R24-N2 统一读 _panel.CurrentZoom(_zoomTarget.localScale.x),
|
||
// 消除独立 _zoom 字段与 CurrentZoom 的双份状态——OnScroll 直接写 localScale,
|
||
// CurrentZoom 即时反映最新值,不再需要额外同步。
|
||
var content = _scrollRect.content;
|
||
var viewport = _scrollRect.viewport != null
|
||
? _scrollRect.viewport
|
||
: (RectTransform)_scrollRect.transform;
|
||
Vector2 contentSize = content.rect.size;
|
||
Vector2 viewportSize = viewport.rect.size;
|
||
float currentZoom = _panel != null ? _panel.CurrentZoom
|
||
: (_zoomTarget != null ? _zoomTarget.localScale.x : 1f);
|
||
float rangeX = contentSize.x * currentZoom - viewportSize.x;
|
||
float rangeY = contentSize.y * currentZoom - viewportSize.y;
|
||
|
||
if (rangeX <= 0f && rangeY <= 0f) return; // 内容比视口小,无需平移
|
||
|
||
Vector2 delta = _navInput * (_keyPanSpeed * Time.unscaledDeltaTime);
|
||
Vector2 norm = _scrollRect.normalizedPosition;
|
||
if (rangeX > 0f) norm.x = Mathf.Clamp01(norm.x + delta.x / rangeX);
|
||
if (rangeY > 0f) norm.y = Mathf.Clamp01(norm.y + delta.y / rangeY);
|
||
_scrollRect.normalizedPosition = norm;
|
||
}
|
||
|
||
// ── 鼠标滚轮缩放 ─────────────────────────────────────────────────────
|
||
|
||
public void OnScroll(PointerEventData eventData)
|
||
{
|
||
if (_zoomTarget == null) return;
|
||
|
||
// R24-N2 直接读 _zoomTarget.localScale.x 作为当前缩放,消除独立 _zoom 字段
|
||
float currentZoom = _zoomTarget.localScale.x;
|
||
float newZoom = Mathf.Clamp(
|
||
currentZoom + eventData.scrollDelta.y * _zoomStep,
|
||
_zoomMin, _zoomMax);
|
||
|
||
if (Mathf.Approximately(newZoom, currentZoom)) return;
|
||
|
||
// 将鼠标屏幕坐标转为 zoomTarget 本地坐标(缩放前)
|
||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||
_zoomTarget, eventData.position, eventData.pressEventCamera, out Vector2 pivotBefore);
|
||
|
||
_zoomTarget.localScale = new Vector3(newZoom, newZoom, 1f);
|
||
|
||
// 将同一屏幕点再次映射(缩放后),计算偏移量保持鼠标下方内容不动
|
||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||
_zoomTarget, eventData.position, eventData.pressEventCamera, out Vector2 pivotAfter);
|
||
|
||
Vector2 offset = pivotAfter - pivotBefore;
|
||
_zoomTarget.anchoredPosition += offset * newZoom;
|
||
}
|
||
}
|
||
}
|