- 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.
137 lines
5.5 KiB
C#
137 lines
5.5 KiB
C#
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;
|
||
}
|
||
}
|
||
}
|