using System; using System.Collections; using Ichni.Diagnostics; using UnityEngine; using UnityEngine.Localization.Settings; using UnityEngine.ResourceManagement.AsyncOperations; namespace Ichni { /// /// Unity Localization 的统一启动入口。 /// /// 本类只包装 Unity 官方提供的 , /// 不自行初始化 Addressables、不查询 Locale Label,也不复制 Localization Package 的内部加载流程。 /// 因此 Settings、Yarn 与其它本地化消费者始终等待同一个由 Unity 管理的初始化操作。 /// /// public static class LocalizationBootstrap { public enum InitializationState { NotStarted, Initializing, Ready, Failed } private static AsyncOperationHandle _initializationOperation; public static InitializationState State { get; private set; } = InitializationState.NotStarted; public static bool IsReady => State == InitializationState.Ready; public static bool HasFailed => State == InitializationState.Failed; public static string FailureReason { get; private set; } /// /// Unity Localization 官方初始化 Handle。 /// 获取该属性会以幂等方式启动初始化,但不会同步阻塞主线程。 /// public static AsyncOperationHandle InitializationOperation { get { BeginInitialization(); return _initializationOperation; } } /// /// 在关闭 Domain Reload 的 Editor 配置下,清理上一次 Play Mode 留下的项目侧状态。 /// Localization Package 会自行重建其内部 Handle,本类不持有也不释放 Package 的资源。 /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetState() { _initializationOperation = default; State = InitializationState.NotStarted; FailureReason = null; } /// /// 按 Unity 官方流程获取唯一的 Localization 初始化操作。 /// 可以由多个系统重复调用;已经启动后不会创建第二条加载链。 /// public static void BeginInitialization() { if (State != InitializationState.NotStarted) { return; } try { // 不设置 InitializeSynchronously,也不直接访问 SelectedLocale。 // InitializationOperation 会异步完成 Locale 选择及所有配置为 Preload 的数据库初始化。 _initializationOperation = LocalizationSettings.InitializationOperation; State = InitializationState.Initializing; Log("开始等待 Unity Localization 官方 InitializationOperation。"); RefreshState(); } catch (Exception exception) { Fail("无法创建 Unity Localization 初始化操作。", exception); } } /// /// 刷新初始化结果。供 YarnTask 等不能直接 yield AsyncOperationHandle 的异步流程调用。 /// public static void RefreshState() { if (State != InitializationState.Initializing) { return; } if (!_initializationOperation.IsValid()) { Fail("Unity Localization 返回了无效的 InitializationOperation Handle。"); return; } if (!_initializationOperation.IsDone) { return; } if (_initializationOperation.Status != AsyncOperationStatus.Succeeded) { Fail("Unity Localization 初始化失败。", _initializationOperation.OperationException); return; } LocaleReady(); } /// /// 供 MonoBehaviour 协程等待 Unity Localization 初始化。 /// 此处直接 yield 官方 Handle,不使用轮询超时或自建 Addressables 状态机。 /// public static IEnumerator WaitForInitialization() { BeginInitialization(); if (State == InitializationState.Initializing) { yield return _initializationOperation; RefreshState(); } } private static void LocaleReady() { State = InitializationState.Ready; FailureReason = null; // SelectedLocale 只有在完整 InitializationOperation 成功后才允许同步读取。 string localeCode = LocalizationSettings.SelectedLocale != null ? LocalizationSettings.SelectedLocale.Identifier.Code : ""; Log($"Unity Localization 初始化完成。SelectedLocale={localeCode}"); } private static void Fail(string message, Exception exception = null) { FailureReason = exception == null ? message : $"{message} Exception={exception}"; State = InitializationState.Failed; Debug.LogError($"[LocalizationBootstrap] {FailureReason}"); BuildHangTracer.Log("LOCALIZATION_BOOTSTRAP", $"[Failed] {FailureReason}"); } private static void Log(string message) { Debug.Log($"[LocalizationBootstrap] {message}"); BuildHangTracer.Log("LOCALIZATION_BOOTSTRAP", message); } } }