Files
zeling_v2/Assets/_Game/Scripts/World/Map/RegionNameDisplay.cs
Joywayer f74d7f1877 Add independent review reports for Minimap system (Rounds 8, 9, and 26)
- 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.
2026-05-25 23:15:12 +08:00

137 lines
5.5 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 System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Localization;
namespace BaseGames.World.Map
{
/// <summary>
/// 进入新区域时在屏幕中央短暂渐显区域名称(架构 15_MapShopModule §1.6)。
/// 挂在 HUD 根节点下,订阅 EVT_RegionChanged执行淡入—保持—淡出动画序列。
/// 可通过 _regionNames 配置原始 RegionId 到本地化显示名的映射;未配置时直接显示 RegionId。
/// </summary>
[RequireComponent(typeof(CanvasGroup))]
public class RegionNameDisplay : MonoBehaviour
{
[SerializeField] private TMP_Text _regionText;
[Header("动画时长(秒)")]
[SerializeField] [Range(0.1f, 2f)] private float _fadeDuration = 0.4f;
[SerializeField] [Range(0.5f, 5f)] private float _holdDuration = 2.0f;
[Header("区域名映射(留空则直接显示 RegionId")]
[SerializeField] private RegionNameEntry[] _regionNames;
[Header("Event Channels")]
[SerializeField] private StringEventChannelSO _onRegionChanged;
private CanvasGroup _cg;
private Coroutine _showCoroutine;
private readonly CompositeDisposable _subs = new();
private Dictionary<string, RegionNameEntry> _regionDict;
private void Awake()
{
_cg = GetComponent<CanvasGroup>();
_cg.alpha = 0f;
gameObject.SetActive(false);
BuildRegionDict();
}
private void OnValidate() => BuildRegionDict();
private void OnEnable()
{
_onRegionChanged?.Subscribe(OnRegionChanged).AddTo(_subs);
}
private void OnDisable()
{
_subs.Clear();
// 协程持有 this 引用且 SetActive(false) 不会自动停止——必须在 OnDisable 显式终止,
// 否则禁用后重新启用时旧序列与新序列叠加CanvasGroup.alpha 与 SetActive 状态不一致。
if (_showCoroutine != null)
{
StopCoroutine(_showCoroutine);
_showCoroutine = null;
}
if (_cg != null) _cg.alpha = 0f;
}
// ── 事件响应 ──────────────────────────────────────────────────────────
private void OnRegionChanged(string regionId)
{
if (string.IsNullOrEmpty(regionId)) return;
if (_regionText != null)
_regionText.text = ResolveDisplayName(regionId);
if (_showCoroutine != null) StopCoroutine(_showCoroutine);
_showCoroutine = StartCoroutine(ShowSequence());
}
// ── 动画序列 ──────────────────────────────────────────────────────────
private IEnumerator ShowSequence()
{
gameObject.SetActive(true);
yield return StartCoroutine(FadeTo(1f));
yield return new WaitForSecondsRealtime(_holdDuration);
yield return StartCoroutine(FadeTo(0f));
gameObject.SetActive(false);
}
private IEnumerator FadeTo(float target)
{
float start = _cg.alpha;
float elapsed = 0f;
while (elapsed < _fadeDuration)
{
_cg.alpha = Mathf.Lerp(start, target, elapsed / _fadeDuration);
elapsed += Time.unscaledDeltaTime;
yield return null;
}
_cg.alpha = target;
}
// ── 辅助方法 ──────────────────────────────────────────────────────────
/// <summary>预建 RegionId → Entry 字典,将 ResolveDisplayName 从 O(N) 降至 O(1)。</summary>
private void BuildRegionDict()
=> _regionDict = MapServiceExtensions.BuildRegionDict(_regionNames);
private string ResolveDisplayName(string regionId)
=> MapServiceExtensions.ResolveRegionDisplayName(_regionDict, regionId);
}
[Serializable]
public struct RegionNameEntry
{
[Tooltip("场景/区域 ID与 EVT_RegionChanged 事件传递的字符串匹配。")]
public string RegionId;
[Tooltip("本地化 Key如 REGION_CITY_NAME。设置后运行时通过 LocalizationManager 自动解析。")]
public string LocKey;
[Tooltip("直接显示名LocKey 为空时使用)。建议优先配置 LocKey仅在开发或单语言项目中直接善用。")]
public string DisplayName;
/// <summary>返回最终显示名:优先读 LocKey其次 DisplayName最后回退到 RegionId。</summary>
public string GetDisplayName()
{
if (!string.IsNullOrEmpty(LocKey))
{
string localized = LocalizationManager.Get(LocKey, LocalizationTable.UI);
// LocalizationManager 在未找到 Key 时返回 Key 本身,判断是否是真正翻译结果
if (!string.IsNullOrEmpty(localized) && localized != LocKey)
return localized;
}
return !string.IsNullOrEmpty(DisplayName) ? DisplayName : RegionId;
}
}
}