摄像机区域的架构改动
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"precompiledReferences": [],
|
||||
"name": "BaseGames.Localization",
|
||||
"defineConstraints": [],
|
||||
"noEngineReferences": false,
|
||||
"versionDefines": [],
|
||||
"rootNamespace": "BaseGames.Localization",
|
||||
"references": [
|
||||
"BaseGames.Core.Events",
|
||||
"BaseGames.Core.Save"
|
||||
],
|
||||
"autoReferenced": true,
|
||||
"overrideReferences": false,
|
||||
"includePlatforms": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4301f7e41bea5e2439e642f571885e60
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Assets/_Game/Scripts/Localization/ILocalizationService.cs
Normal file
24
Assets/_Game/Scripts/Localization/ILocalizationService.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace BaseGames.Localization
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地化服务接口。通过 ServiceLocator 注册,供 UI 和游戏系统获取本地化文本。
|
||||
/// </summary>
|
||||
public interface ILocalizationService
|
||||
{
|
||||
/// <summary>当前激活的语言。</summary>
|
||||
Language CurrentLanguage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取本地化字符串。查找顺序:当前语言 → 回退语言(English)→ 直接返回 key。
|
||||
/// </summary>
|
||||
string Get(string key, string table = "UI");
|
||||
|
||||
/// <summary>切换游戏语言并通知所有订阅者刷新文本。</summary>
|
||||
void SetLanguage(Language language);
|
||||
|
||||
/// <summary>语言切换时触发。</summary>
|
||||
event Action<Language> OnLanguageChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd601a913eab0be4896b058f8c6fe8d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/_Game/Scripts/Localization/Language.cs
Normal file
14
Assets/_Game/Scripts/Localization/Language.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace BaseGames.Localization
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏支持的语言列表。
|
||||
/// 添加新语言时同步在 Resources/Localization/ 下创建对应子目录和 JSON 表文件。
|
||||
/// </summary>
|
||||
public enum Language
|
||||
{
|
||||
ChineseSimplified,
|
||||
English,
|
||||
Japanese,
|
||||
Korean,
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Localization/Language.cs.meta
Normal file
11
Assets/_Game/Scripts/Localization/Language.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19c40515404112049b98f2632226fc78
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
12
Assets/_Game/Scripts/Localization/LanguageEventChannelSO.cs
Normal file
12
Assets/_Game/Scripts/Localization/LanguageEventChannelSO.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Localization
|
||||
{
|
||||
/// <summary>
|
||||
/// 语言切换事件频道。
|
||||
/// 用法:LocalizationManager 切换语言后广播,UI 组件订阅并刷新文本。
|
||||
/// 创建资产路径:Assets/Data/Events/EVT_LanguageChanged.asset
|
||||
/// </summary>
|
||||
[UnityEngine.CreateAssetMenu(menuName = "Events/LanguageChanged")]
|
||||
public class LanguageEventChannelSO : BaseEventChannelSO<Language> { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 828bc10f39b0bcc418209e0e7350220f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
176
Assets/_Game/Scripts/Localization/LocalizationManager.cs
Normal file
176
Assets/_Game/Scripts/Localization/LocalizationManager.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
// Assets/Scripts/Localization/LocalizationManager.cs
|
||||
// 本地化管理器(运行时 JSON 文件驱动)。
|
||||
//
|
||||
// 数据格式(放在 Resources/Localization/{Language}/{TableName}.json):
|
||||
// {
|
||||
// "entries": [
|
||||
// { "key": "ui_start", "value": "开始游戏" },
|
||||
// { "key": "ui_settings", "value": "设置" }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// 推荐用法(通过 ServiceLocator 获取 ILocalizationService 实例):
|
||||
// ServiceLocator.GetOrDefault<ILocalizationService>()?.Get("ui_start")
|
||||
// ServiceLocator.GetOrDefault<ILocalizationService>()?.SetLanguage(Language.English)
|
||||
//
|
||||
// 便捷静态方法(内部仍走 ServiceLocator,推荐在热路径之外使用):
|
||||
// LocalizationManager.Get("ui_start")
|
||||
// LocalizationManager.Get("dlg_hero", "Dialogue")
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Save;
|
||||
|
||||
namespace BaseGames.Localization
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地化管理器(MonoBehaviour,挂在 Persistent 场景)。
|
||||
/// 实现 ILocalizationService + ISaveable,通过 ServiceLocator 注册。
|
||||
/// 语言偏好持久化到 SaveData.Settings.Language,不使用 PlayerPrefs。
|
||||
/// </summary>
|
||||
public class LocalizationManager : MonoBehaviour, ILocalizationService, ISaveable
|
||||
{
|
||||
// 默认语言:回退链:当前语言 → 英语 → 直接返回 key
|
||||
private Language _currentLanguage = Language.ChineseSimplified;
|
||||
private readonly Language _fallbackLanguage = Language.English;
|
||||
|
||||
// 双层缓存:languageKey("ChineseSimplified/UI") → (key → value)
|
||||
private readonly Dictionary<string, Dictionary<string, string>> _cache = new();
|
||||
|
||||
// ILocalizationService 实例事件
|
||||
private event Action<Language> _onLanguageChanged;
|
||||
event Action<Language> ILocalizationService.OnLanguageChanged
|
||||
{
|
||||
add => _onLanguageChanged += value;
|
||||
remove => _onLanguageChanged -= value;
|
||||
}
|
||||
|
||||
// ── 生命周期 ──────────────────────────────────────────────────────────
|
||||
private void Awake()
|
||||
{
|
||||
if (ServiceLocator.GetOrDefault<ILocalizationService>() != null) { Destroy(gameObject); return; }
|
||||
ServiceLocator.Register<ILocalizationService>(this);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ServiceLocator.Unregister<ILocalizationService>(this);
|
||||
}
|
||||
|
||||
// ── ILocalizationService ──────────────────────────────────────────────
|
||||
public Language CurrentLanguage => _currentLanguage;
|
||||
|
||||
/// <summary>切换游戏语言并通知所有订阅者刷新文本。</summary>
|
||||
public void SetLanguage(Language language)
|
||||
{
|
||||
if (_currentLanguage == language) return;
|
||||
_currentLanguage = language;
|
||||
_onLanguageChanged?.Invoke(language);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取本地化字符串(显式接口实现)。
|
||||
/// 查找顺序:当前语言 → 回退语言(English)→ 直接返回 key。
|
||||
/// </summary>
|
||||
string ILocalizationService.Get(string key, string table)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key)) return string.Empty;
|
||||
|
||||
// 1. 尝试当前语言
|
||||
if (TryGetFromTable(_currentLanguage, table, key, out string text))
|
||||
return text;
|
||||
|
||||
// 2. 回退到 English
|
||||
if (_currentLanguage != _fallbackLanguage &&
|
||||
TryGetFromTable(_fallbackLanguage, table, key, out text))
|
||||
return text;
|
||||
|
||||
// 3. 最终回退:原始 key
|
||||
return key;
|
||||
}
|
||||
|
||||
// ── ISaveable ─────────────────────────────────────────────────────────
|
||||
public void OnSave(SaveData data)
|
||||
{
|
||||
if (data?.Settings == null) return;
|
||||
data.Settings.Language = _currentLanguage.ToString();
|
||||
}
|
||||
|
||||
public void OnLoad(SaveData data)
|
||||
{
|
||||
if (data?.Settings == null || string.IsNullOrEmpty(data.Settings.Language)) return;
|
||||
if (Enum.TryParse<Language>(data.Settings.Language, out var lang))
|
||||
SetLanguage(lang);
|
||||
}
|
||||
|
||||
// ── 静态便捷方法 ─────────────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
/// 静态快捷获取本地化字符串。委托给 ILocalizationService 实例;服务未注册时直接返回 key。
|
||||
/// </summary>
|
||||
public static string Get(string key, string table = "UI")
|
||||
=> ServiceLocator.GetOrDefault<ILocalizationService>()?.Get(key, table) ?? key;
|
||||
|
||||
// ── 内部 ─────────────────────────────────────────────────────────────
|
||||
private bool TryGetFromTable(Language language, string table, string key, out string value)
|
||||
{
|
||||
var cacheKey = $"{language}/{table}";
|
||||
if (!_cache.TryGetValue(cacheKey, out var dict))
|
||||
{
|
||||
dict = LoadTable(language, table);
|
||||
_cache[cacheKey] = dict; // 即使加载失败也存入空字典,避免每帧重试
|
||||
}
|
||||
if (dict != null && dict.TryGetValue(key, out value)) return true;
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 Resources/Localization/{language}/{table}.json 加载字符串表。
|
||||
/// 返回 null 表示文件不存在。
|
||||
/// </summary>
|
||||
private static Dictionary<string, string> LoadTable(Language language, string table)
|
||||
{
|
||||
string path = $"Localization/{language}/{table}";
|
||||
var asset = Resources.Load<TextAsset>(path);
|
||||
if (asset == null) return null;
|
||||
|
||||
var parsed = JsonUtility.FromJson<StringTableJson>(asset.text);
|
||||
if (parsed?.entries == null) return null;
|
||||
|
||||
var dict = new Dictionary<string, string>(parsed.entries.Count, StringComparer.Ordinal);
|
||||
foreach (var entry in parsed.entries)
|
||||
if (!string.IsNullOrEmpty(entry.key))
|
||||
dict[entry.key] = entry.value ?? string.Empty;
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
// ── 序列化辅助类型 ────────────────────────────────────────────────────
|
||||
|
||||
[Serializable]
|
||||
private class StringTableJson
|
||||
{
|
||||
public List<StringEntry> entries;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class StringEntry
|
||||
{
|
||||
public string key;
|
||||
public string value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dcd182e23f515147886cce01e26854d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user