地图系统

This commit is contained in:
2026-06-05 18:41:33 +08:00
parent 613f2a4d13
commit fe4fd60083
234 changed files with 33090 additions and 4899 deletions

View File

@@ -7,6 +7,7 @@ using TMPro;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Core.Save;
using BaseGames.Localization;
namespace BaseGames.World.Map
{
@@ -42,6 +43,17 @@ namespace BaseGames.World.Map
[Header("玩家位置")]
[SerializeField] private Image _playerIconImg; // _roomContainer 内的玩家图标
[Tooltip("勾选后未启用定位能力IMapService.IsLocatorEnabled时隐藏玩家图标与当前房间高亮——\n" +
"设计:拿到指南针护符前能看地图但不知自己在哪。\n" +
"默认 false 保持现有\"恒显\"行为。与 MinimapHUD._requireLocatorForPlayerDot 对称。")]
[SerializeField] private bool _requireLocatorForPlayerIcon;
[Header("传送选择")]
[Tooltip("勾选后,点击地图上\"已解锁且已探索\"的传送站房间会触发 OnTeleportStationSelected\n" +
"由 UI 侧 MapTeleportConfirmController 弹出确认框并调用 ITeleportService.RequestTeleport。\n" +
"取消勾选可禁止从全屏地图发起传送(如改为仅在传送站处选择目的地)。")]
[SerializeField] private bool _allowTeleportFromMap = true;
[Header("地图标记")]
[SerializeField] private Image _pinPrefab;
[SerializeField] private MapPinConfigSO _pinConfig; // R12-N3 集中 PinType→Sprite 映射,替代旧的 PinSpriteEntry[]
@@ -74,6 +86,15 @@ namespace BaseGames.World.Map
private IMapService _mapSvc;
private IPlayerPositionProvider _playerProvider;
private IPinService _pinService;
private ITeleportService _teleportSvc;
/// <summary>
/// 玩家在地图上点击了一个"可传送"的已解锁站点(参数 RoomId
/// UI 侧MapTeleportConfirmController位于 BaseGames.UI 程序集)订阅此事件,
/// 弹出确认框并在确认后调用 ITeleportService.RequestTeleport——
/// MapPanel 自身不依赖 UI 程序集,避免与 BaseGames.UI 形成循环引用。
/// </summary>
public event Action<string> OnTeleportStationSelected;
private void Awake()
{
@@ -99,6 +120,7 @@ namespace BaseGames.World.Map
RenderPins();
UpdatePlayerIcon();
RefreshTeleportMarks(); // 每次打开刷新可传送标记(覆盖在站点新解锁后重开地图的场景)
CenterOnCurrentRoom();
// R12-N8移除 _onMapUpdated 订阅,避免与 OnExplorationChanged 双重刷新;
// _onMapUpdated 字段保留但标记 HideInInspector防止旧 Prefab 数据丢失。
@@ -144,10 +166,12 @@ namespace BaseGames.World.Map
_mapSvc.OnDatabaseChanged += OnDatabaseChanged;
_mapSvc.OnExplorationChanged += OnExplorationChanged;
_mapSvc.OnRoomMapped += OnRoomMappedAnim;
_mapSvc.OnLocatorChanged += OnLocatorChanged;
}
}
_playerProvider ??= ServiceLocator.GetOrDefault<IPlayerPositionProvider>();
_pinService ??= ServiceLocator.GetOrDefault<IPinService>();
_teleportSvc ??= ServiceLocator.GetOrDefault<ITeleportService>(); // 可选:未注册时地图仍正常工作,仅不显示可传送标记
if (_mapSvc != null && _playerProvider != null && _pinService != null)
_servicesReady = true;
}
@@ -162,6 +186,7 @@ namespace BaseGames.World.Map
_mapSvc.OnDatabaseChanged -= OnDatabaseChanged;
_mapSvc.OnExplorationChanged -= OnExplorationChanged;
_mapSvc.OnRoomMapped -= OnRoomMappedAnim;
_mapSvc.OnLocatorChanged -= OnLocatorChanged;
}
}
@@ -190,6 +215,13 @@ namespace BaseGames.World.Map
RebuildAll();
}
/// <summary>定位能力开关变化:重置 dirty 标记强制 UpdatePlayerIcon 重新评估门控。</summary>
private void OnLocatorChanged()
{
_lastIconRoomId = null; // 绕过 LateUpdate 的 roomId/normPos 脏检查,确保门控状态立即生效
if (isActiveAndEnabled) UpdatePlayerIcon();
}
/// <summary>R10-N12 探索进度变化:仅刷新格子可见性,不重建结构(轻量级)。</summary>
private void OnExplorationChanged()
{
@@ -252,6 +284,27 @@ namespace BaseGames.World.Map
if (cell == null) continue;
cell.SetVisibility(_mapSvc.GetVisibility(roomId));
}
RefreshTeleportMarks(); // 探索状态影响 CanTeleportTo同步刷新可传送标记
}
// ── 传送选择 ──────────────────────────────────────────────────────────
/// <summary>房间是否可作为传送目的地:允许从地图传送 + 传送服务存在 + 已解锁且已探索。</summary>
private bool IsTeleportable(string roomId)
=> _allowTeleportFromMap && _teleportSvc != null && _teleportSvc.CanTeleportTo(roomId);
/// <summary>格子点击回调:仅对可传送站点转发选择事件,交由 UI 侧确认并执行传送。</summary>
private void OnCellClicked(string roomId)
{
if (IsTeleportable(roomId))
OnTeleportStationSelected?.Invoke(roomId);
}
/// <summary>刷新所有格子的"可传送"标记(探索/传送解锁变化、面板重开时调用)。</summary>
private void RefreshTeleportMarks()
{
foreach (var (roomId, cell) in _cells)
if (cell != null) cell.SetTeleportable(IsTeleportable(roomId));
}
// ── 格子 & 出口连接 ──────────────────────────────────────────────────
@@ -279,6 +332,8 @@ namespace BaseGames.World.Map
// R11-N8 布局单独调用 SetGridLayout与 MinimapHUD.PlaceCell 职责对称
cell.SetGridLayout(room, MapGridConstants.FullMapCellPixels);
cell.SetColors(_colorExplored, _colorMapped, _colorUnknown);
cell.SetClickHandler(OnCellClicked);
cell.SetTeleportable(IsTeleportable(room.RoomId));
_cells[room.RoomId] = cell;
}
DrawExits();
@@ -343,6 +398,15 @@ namespace BaseGames.World.Map
private void UpdatePlayerIcon()
{
if (_playerIconImg == null || _playerProvider == null) return;
// 定位门控:未启用定位能力时隐藏玩家图标与当前房间高亮(由指南针护符解锁)
if (_requireLocatorForPlayerIcon && (_mapSvc == null || !_mapSvc.IsLocatorEnabled))
{
_playerIconImg.enabled = false;
UpdateCellHighlight(null);
return;
}
var roomId = _playerProvider.CurrentRoomId;
if (string.IsNullOrEmpty(roomId) || !_cells.TryGetValue(roomId, out var cell))
{
@@ -469,7 +533,8 @@ namespace BaseGames.World.Map
private void ShowTooltip(string text)
{
if (_tooltipPanel == null || string.IsNullOrEmpty(text)) return;
if (_tooltipText != null) _tooltipText.text = text;
// 房间名走本地化text 为本地化 Key 时解析为译文;为普通名称(未命中 Key时原样显示向后兼容
if (_tooltipText != null) _tooltipText.text = LocalizationManager.Get(text, LocalizationTable.UI);
_tooltipPanel.SetActive(true);
}