159 lines
5.8 KiB
C#
159 lines
5.8 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections;
|
|||
|
|
using Ichni.Diagnostics;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.Localization.Settings;
|
|||
|
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
|||
|
|
|
|||
|
|
namespace Ichni
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// Unity Localization 的统一启动入口。
|
|||
|
|
/// <para>
|
|||
|
|
/// 本类只包装 Unity 官方提供的 <see cref="LocalizationSettings.InitializationOperation"/>,
|
|||
|
|
/// 不自行初始化 Addressables、不查询 Locale Label,也不复制 Localization Package 的内部加载流程。
|
|||
|
|
/// 因此 Settings、Yarn 与其它本地化消费者始终等待同一个由 Unity 管理的初始化操作。
|
|||
|
|
/// </para>
|
|||
|
|
/// </summary>
|
|||
|
|
public static class LocalizationBootstrap
|
|||
|
|
{
|
|||
|
|
public enum InitializationState
|
|||
|
|
{
|
|||
|
|
NotStarted,
|
|||
|
|
Initializing,
|
|||
|
|
Ready,
|
|||
|
|
Failed
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static AsyncOperationHandle<LocalizationSettings> _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; }
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Unity Localization 官方初始化 Handle。
|
|||
|
|
/// 获取该属性会以幂等方式启动初始化,但不会同步阻塞主线程。
|
|||
|
|
/// </summary>
|
|||
|
|
public static AsyncOperationHandle<LocalizationSettings> InitializationOperation
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
BeginInitialization();
|
|||
|
|
return _initializationOperation;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 在关闭 Domain Reload 的 Editor 配置下,清理上一次 Play Mode 留下的项目侧状态。
|
|||
|
|
/// Localization Package 会自行重建其内部 Handle,本类不持有也不释放 Package 的资源。
|
|||
|
|
/// </summary>
|
|||
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
|||
|
|
private static void ResetState()
|
|||
|
|
{
|
|||
|
|
_initializationOperation = default;
|
|||
|
|
State = InitializationState.NotStarted;
|
|||
|
|
FailureReason = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 按 Unity 官方流程获取唯一的 Localization 初始化操作。
|
|||
|
|
/// 可以由多个系统重复调用;已经启动后不会创建第二条加载链。
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 刷新初始化结果。供 YarnTask 等不能直接 yield AsyncOperationHandle 的异步流程调用。
|
|||
|
|
/// </summary>
|
|||
|
|
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();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 供 MonoBehaviour 协程等待 Unity Localization 初始化。
|
|||
|
|
/// 此处直接 yield 官方 Handle,不使用轮询超时或自建 Addressables 状态机。
|
|||
|
|
/// </summary>
|
|||
|
|
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
|
|||
|
|
: "<null>";
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|