using System; using UnityEngine.Localization.Operations; using UnityEngine.Localization.Settings; using UnityEngine.Localization.Tables; using UnityEngine.Pool; using UnityEngine.ResourceManagement.AsyncOperations; namespace UnityEngine.Localization { #if MODULE_AUDIO || PACKAGE_DOCS_GENERATION /// /// Provides a specialized which can be used to localize [AudioClip](https://docs.unity3d.com/ScriptReference/AudioClip.html) assets. /// /// /// This example shows how to use the LocalizedAudioClip in a [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html). /// /// [Serializable] public partial class LocalizedAudioClip : LocalizedAsset {} #endif /// /// Provides a specialized which can be used to localize [Prefabs](https://docs.unity3d.com/Manual/Prefabs.html). /// [Serializable] public partial class LocalizedGameObject : LocalizedAsset {} /// /// Provides a specialized which can be used to localize a [Mesh](https://docs.unity3d.com/ScriptReference/Mesh.html). /// [Serializable] public partial class LocalizedMesh : LocalizedAsset {} /// /// Provides a specialized which can be used to localize [Materials](https://docs.unity3d.com/ScriptReference/Material.html). /// [Serializable] public partial class LocalizedMaterial : LocalizedAsset {} /// /// Provides a which you can use to localize any [UnityEngine.Object](https://docs.unity3d.com/ScriptReference/Object.html). /// [Serializable] public partial class LocalizedObject : LocalizedAsset {} /// /// Provides a which you can use to localize [Sprites](https://docs.unity3d.com/ScriptReference/Sprite.html). /// [Serializable] public partial class LocalizedSprite : LocalizedAsset {} /// /// Provides a which you can use to localize [Textures](https://docs.unity3d.com/ScriptReference/Texture.html) assets. /// [Serializable] public partial class LocalizedTexture : LocalizedAsset {} #if PACKAGE_TMP || (UNITY_2023_2_OR_NEWER && PACKAGE_UGUI) || PACKAGE_DOCS_GENERATION /// /// Provides a which you can use to localize a TextMeshPro [TMPro.TMP_FontAsset](https://docs.unity3d.com/Packages/com.unity.textmeshpro@latest?subfolder=/api/TMPro.TMP_FontAsset)/>. /// [Serializable] public partial class LocalizedTmpFont : LocalizedAsset {} #endif /// /// Provides a which you can use to localize a [Font](https://docs.unity3d.com/ScriptReference/Font.html)/>. /// [Serializable] public partial class LocalizedFont : LocalizedAsset {} /// /// Base class for all localized assets. /// public abstract partial class LocalizedAssetBase : LocalizedReference { /// /// Returns the localized asset as a [UnityEngine.Object](https://docs.unity3d.com/ScriptReference/Object.html). /// /// public abstract AsyncOperationHandle LoadAssetAsObjectAsync(); /// /// Overrides the asset's default type. This loads a type from with the and the /// the asset that matches. /// This helps to filter sub-assets when trying to load them and they share a common type among other sub-assets with the same name. ///
For example, an asset could have the following structure: ///
Main Asset [GameObject] ///
- Sub Asset[GameObject] ///
- Sub Asset[Mesh] /// If you were using a , calling or to load /// "Main Asset/Sub Asset" would return the first matching asset. In this case, this would be the GameObject version. /// With this method, you could provide the type Mesh to filter out the GameObject version and return the Mesh. ///
/// The type to use instead of the default. /// Returns the loading operation for the request. public abstract AsyncOperationHandle LoadAssetAsync() where TObject : Object; } /// /// Provides a way to reference an inside of a specific and request the localized asset. /// /// The type that should be supported. This can be any type that inherits from [UnityEngine.Object](https://docs.unity3d.com/ScriptReference/Object.html). /// /// This example shows how a can be used to localize a [Prefabs](https://docs.unity3d.com/Manual/Prefabs.html). /// See also and . /// /// /// /// This example shows how a can be used to localize a [ScriptableObject](https://docs.unity3d.com/ScriptReference/ScriptableObject.html). /// /// /// /// This example shows how to bind to a UI Toolkit property. Note that this requires Unity 2023.2 and above. /// /// [Serializable] public partial class LocalizedAsset : LocalizedAssetBase, IDisposable where TObject : Object { CallbackArray m_ChangeHandler; Action m_SelectedLocaleChanged; Action> m_AutomaticLoadingCompleted; AsyncOperationHandle m_PreviousLoadingOperation; /// /// Delegate used by . /// /// The localized asset. public delegate void ChangeHandler(TObject value); /// public override bool WaitForCompletion { set { if (value == WaitForCompletion) return; base.WaitForCompletion = value; #if !UNITY_WEBGL // WebGL does not support WaitForCompletion if (value && CurrentLoadingOperationHandle.IsValid() && !CurrentLoadingOperationHandle.IsDone) CurrentLoadingOperationHandle.WaitForCompletion(); #endif } } internal override bool ForceSynchronous => WaitForCompletion || LocalizationSettings.AssetDatabase.AsynchronousBehaviour == AsynchronousBehaviour.ForceSynchronous; /// /// The current loading operation for the asset when using . This is if a loading operation is not available. /// public AsyncOperationHandle CurrentLoadingOperationHandle { get; internal set; } /// /// Provides a callback that will be invoked when the asset is available or has changed. /// /// /// The following events will trigger an update: /// - The first time the action is added to the event. /// - The changing. /// - The or changing. /// /// When the first is added, a loading operation (see ) will automatically /// start and the localized asset will be sent to the subscriber when completed. Any adding additional subscribers added after /// loading has completed will also be sent the latest localized asset when they are added. /// This ensures that a subscriber will always have the correct localized value regardless of when it was added. /// /// /// This example shows how the event could be used to change the Font on some localized Text. /// /// public event ChangeHandler AssetChanged { add { if (value == null) throw new ArgumentNullException(); m_ChangeHandler.Add(value); if (m_ChangeHandler.Length == 1) { LocalizationSettings.ValidateSettingsExist(); ForceUpdate(); // We subscribe after the first update as its possible that a SelectedLocaleChanged may be fired // during ForceUpdate when using WaitForCompletion and we want to avoid this. LocalizationSettings.SelectedLocaleChanged += m_SelectedLocaleChanged; } else if (CurrentLoadingOperationHandle.IsValid() && CurrentLoadingOperationHandle.IsDone) { // Call the event with the latest value. value(CurrentLoadingOperationHandle.Result); } } remove { m_ChangeHandler.RemoveByMovingTail(value); if (m_ChangeHandler.Length == 0) { LocalizationSettings.SelectedLocaleChanged -= m_SelectedLocaleChanged; ClearLoadingOperation(); } } } /// /// Returns if has any subscribers. /// public bool HasChangeHandler => m_ChangeHandler.Length != 0; /// /// Initializes and returns an empty instance of a . /// public LocalizedAsset() { m_SelectedLocaleChanged = HandleLocaleChange; m_AutomaticLoadingCompleted = AutomaticLoadingCompleted; } /// /// Provides a localized asset from a with the and the /// the asset that matches . /// /// /// The asset may have already been loaded, either during a previous operation or if Preload mode is used. Check the property to see if the asset is already loaded and therefore is immediately available. /// See [Async operation handling](https://docs.unity3d.com/Packages/com.unity.addressables@latest/index.html?subfolder=/manual/AddressableAssetsAsyncOperationHandle.html) for further details. /// /// Returns the loading operation for the request. /// /// This example shows how can be used to request a sprite asset when the changes. /// /// public AsyncOperationHandle LoadAssetAsync() => LoadAssetAsync(); /// public override AsyncOperationHandle LoadAssetAsync() { LocalizationSettings.ValidateSettingsExist("Can not Load Asset."); return LocalizationSettings.AssetDatabase.GetLocalizedAssetAsync(TableReference, TableEntryReference, locale: LocaleOverride); } class ConvertToObjectOperation : WaitForCurrentOperationAsyncOperationBase { public static readonly ObjectPool Pool = new ObjectPool( () => new ConvertToObjectOperation(), collectionCheck:false); AsyncOperationHandle m_Operation; public void Init(AsyncOperationHandle operation) { AddressablesInterface.ResourceManager.Acquire(operation); m_Operation = operation; CurrentOperation = operation; } protected override void Execute() { if (m_Operation.IsDone) OnCompleted(m_Operation); else m_Operation.Completed += OnCompleted; } void OnCompleted(AsyncOperationHandle op) { Complete(op.Result, op.Status == AsyncOperationStatus.Succeeded, null); } protected override void Destroy() { AddressablesInterface.Release(m_Operation); Pool.Release(this); } } /// public override AsyncOperationHandle LoadAssetAsObjectAsync() { var wrappedOperation = LoadAssetAsync(); var operation = ConvertToObjectOperation.Pool.Get(); operation.Init(wrappedOperation); return AddressablesInterface.ResourceManager.StartOperation(operation, default); } /// /// Provides a localized asset from a with the and the /// the asset that matches . /// Uses [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) to force the loading to complete synchronously. /// Please note that [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) is not supported on /// [WebGL](https://docs.unity3d.com/Packages/com.unity.addressables@latest/index.html?subfolder=/manual/SynchronousAddressables.html#webgl). /// /// Returns the localized asset. public TObject LoadAsset() { return LoadAssetAsync().WaitForCompletion(); } /// protected internal override void ForceUpdate() { if (m_ChangeHandler.Length != 0) { HandleLocaleChange(null); } } void HandleLocaleChange(Locale locale) { #if UNITY_EDITOR m_CurrentTable = TableReference; m_CurrentTableEntry = TableEntryReference; #if MODULE_UITK && UNITY_2023_3_OR_NEWER HandleLocaleChangeDataBinding(locale); #endif // Dont update if we have no selected Locale if (!LocalizationSettings.Instance.IsPlayingOrWillChangePlaymode && LocaleOverride == null && LocalizationSettings.SelectedLocale == null) { ClearLoadingOperation(); return; } #endif if (IsEmpty) { ClearLoadingOperation(); #if UNITY_EDITOR // If we are empty and playing or previewing then we should force an update. if (!LocalizationSettings.Instance.IsPlayingOrWillChangePlaymode) InvokeChangeHandler(null); #endif return; } // Track the previous loading operation so we can release it when the new one completes (LOC-976). m_PreviousLoadingOperation = CurrentLoadingOperationHandle; CurrentLoadingOperationHandle = LoadAssetAsync(); AddressablesInterface.Acquire(CurrentLoadingOperationHandle); if (!CurrentLoadingOperationHandle.IsDone) { #if !UNITY_WEBGL if (WaitForCompletion || LocalizationSettings.AssetDatabase.AsynchronousBehaviour == AsynchronousBehaviour.ForceSynchronous) { CurrentLoadingOperationHandle.WaitForCompletion(); } else #endif { CurrentLoadingOperationHandle.Completed += m_AutomaticLoadingCompleted; return; } } AutomaticLoadingCompleted(CurrentLoadingOperationHandle); } void AutomaticLoadingCompleted(AsyncOperationHandle loadOperation) { if (loadOperation.Status != AsyncOperationStatus.Succeeded) { CurrentLoadingOperationHandle = default; return; } InvokeChangeHandler(loadOperation.Result); ClearPreviousLoadingOperation(); } void InvokeChangeHandler(TObject value) { try { m_ChangeHandler.LockForChanges(); var len = m_ChangeHandler.Length; if (len == 1) { m_ChangeHandler.SingleDelegate(value); } else if (len > 1) { var array = m_ChangeHandler.MultiDelegates; for (int i = 0; i < len; ++i) array[i](value); } } catch (Exception ex) { Debug.LogException(ex); } m_ChangeHandler.UnlockForChanges(); } internal void ClearLoadingOperation() { ClearLoadingOperation(CurrentLoadingOperationHandle); CurrentLoadingOperationHandle = default; } void ClearPreviousLoadingOperation() { ClearLoadingOperation(m_PreviousLoadingOperation); m_PreviousLoadingOperation = default; } void ClearLoadingOperation(AsyncOperationHandle operationHandle) { if (operationHandle.IsValid()) { // We should only call this if we are not done as its possible that the internal list is null if its not been used. if (!operationHandle.IsDone) operationHandle.Completed -= m_AutomaticLoadingCompleted; AddressablesInterface.Release(operationHandle); } } /// protected override void Reset() { ClearLoadingOperation(); } /// /// Removes and releases internal references to Addressable assets. /// ~LocalizedAsset() { ClearLoadingOperation(); } /// /// Removes and releases internal references to Addressable assets. /// void IDisposable.Dispose() { m_ChangeHandler.Clear(); LocalizationSettings.SelectedLocaleChanged -= m_SelectedLocaleChanged; ClearLoadingOperation(); GC.SuppressFinalize(this); } } }