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);
}
}
}