#if DEVELOPMENT_BUILD || UNITY_EDITOR #define LITMOTION_DEBUG #endif using System; using System.Collections; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using UnityEngine; namespace LitMotion { /// /// Provides methods for manipulating the motion entity pointed to by MotionHandle. /// public static class MotionHandleExtensions { /// /// Checks if a motion is valid. /// /// This motion handle /// True if motion is valid, otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValid(this MotionHandle handle) { return MotionManager.IsValid(handle); } /// /// Checks if a motion is active. /// /// This motion handle /// True if motion is active, otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsActive(this MotionHandle handle) { return MotionManager.IsActive(handle); } /// /// Checks if a motion is currently playing. /// /// This motion handle /// True if motion is playing, otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsPlaying(this MotionHandle handle) { return MotionManager.IsPlaying(handle); } /// /// Gets the debug name set for the MotionHandle. If not specified, returns the result of ToString(). /// /// This motion handle /// Debug name [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string GetDebugName(this MotionHandle handle) { #if LITMOTION_DEBUG return MotionManager.GetManagedDataRef(handle, false).DebugName ?? handle.ToString(); #else return handle.ToString(); #endif } /// /// Preserves the MotionHandle so that it is not destroyed until Cancel() is explicitly called. /// /// This motion handle /// Returns itself for method chaining [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MotionHandle Preserve(this MotionHandle handle) { MotionManager.GetDataRef(handle).State.IsPreserved = true; return handle; } /// /// Complete motion. /// /// This motion handle [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Complete(this MotionHandle handle) { MotionManager.Complete(handle); } /// /// Attempt to complete the motion. /// /// This motion handle /// Returns true if the operation was successful. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryComplete(this MotionHandle handle) { return MotionManager.TryComplete(handle); } /// /// Cancel motion. /// /// This motion handle [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Cancel(this MotionHandle handle) { MotionManager.Cancel(handle); } /// /// Attempt to cancel the motion. /// /// This motion handle /// Returns true if the operation was successful. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryCancel(this MotionHandle handle) { return MotionManager.TryCancel(handle); } /// /// Add this motion handle to CompositeMotionHandle. /// /// This motion handle /// target CompositeMotionHandle public static MotionHandle AddTo(this MotionHandle handle, CompositeMotionHandle compositeMotionHandle) { compositeMotionHandle.Add(handle); return handle; } /// /// Link the motion lifecycle to the target object. /// /// This motion handle /// Target object public static MotionHandle AddTo(this MotionHandle handle, GameObject target) { GetOrAddComponent(target).Register(handle, LinkBehavior.CancelOnDestroy); return handle; } /// /// Link the motion lifecycle to the target object. /// /// This motion handle /// Target object /// Link behaviour public static MotionHandle AddTo(this MotionHandle handle, GameObject target, LinkBehavior linkBehaviour) { GetOrAddComponent(target).Register(handle, linkBehaviour); return handle; } /// /// Link the motion lifecycle to the target object. /// /// This motion handle /// Target object public static MotionHandle AddTo(this MotionHandle handle, Component target) { GetOrAddComponent(target.gameObject).Register(handle, LinkBehavior.CancelOnDestroy); return handle; } /// /// Link the motion lifecycle to the target object. /// /// This motion handle /// Target object /// Link behaviour public static MotionHandle AddTo(this MotionHandle handle, Component target, LinkBehavior linkBehaviour) { GetOrAddComponent(target.gameObject).Register(handle, linkBehaviour); return handle; } #if UNITY_2022_2_OR_NEWER /// /// Link the motion lifecycle to the target object. /// /// This motion handle /// Target object public static MotionHandle AddTo(this MotionHandle handle, MonoBehaviour target) { target.destroyCancellationToken.Register(() => { if (handle.IsActive()) handle.Cancel(); }, false); return handle; } #endif static TComponent GetOrAddComponent(GameObject target) where TComponent : Component { if (!target.TryGetComponent(out var component)) { component = target.AddComponent(); } return component; } /// /// Convert MotionHandle to IDisposable. /// /// This motion handle /// Behavior when disposing /// Disposable motion public static IDisposable ToDisposable(this MotionHandle handle, DisposeBehavior disposeBehavior = DisposeBehavior.Cancel) { return new MotionHandleDisposable(handle, disposeBehavior); } /// /// Wait for the motion to finish in a coroutine. /// /// This motion handle /// public static IEnumerator ToYieldInstruction(this MotionHandle handle) { while (handle.IsActive()) { yield return null; } } public static MotionAwaiter GetAwaiter(this MotionHandle handle) { return new MotionAwaiter(handle); } /// /// Convert motion handle to ValueTask. /// /// This motion handle /// CancellationToken /// public static ValueTask ToValueTask(this MotionHandle handle, CancellationToken cancellationToken = default) { if (!handle.IsActive()) return default; var source = ValueTaskMotionTaskSource.Create(handle, CancelBehavior.Cancel, true, cancellationToken, out var token); return new ValueTask(source, token); } /// /// Convert motion handle to ValueTask. /// /// This motion handle /// Behavior when canceling /// CancellationToken /// public static ValueTask ToValueTask(this MotionHandle handle, CancelBehavior cancelBehavior, CancellationToken cancellationToken = default) { if (!handle.IsActive()) return default; var source = ValueTaskMotionTaskSource.Create(handle, cancelBehavior, true, cancellationToken, out var token); return new ValueTask(source, token); } /// /// Convert motion handle to ValueTask. /// /// This motion handle /// Behavior when canceling /// Whether to link MotionHandle.Cancel() to task cancellation /// CancellationToken /// public static ValueTask ToValueTask(this MotionHandle handle, CancelBehavior cancelBehavior, bool cancelAwaitOnMotionCanceled, CancellationToken cancellationToken = default) { if (!handle.IsActive()) return default; var source = ValueTaskMotionTaskSource.Create(handle, cancelBehavior, cancelAwaitOnMotionCanceled, cancellationToken, out var token); return new ValueTask(source, token); } #if UNITY_2023_1_OR_NEWER /// /// Convert motion handle to Awaitable. /// /// This motion handle /// CancellationToken /// public static Awaitable ToAwaitable(this MotionHandle handle, CancellationToken cancellationToken = default) { if (!handle.IsActive()) return AwaitableMotionTaskSource.CompletedSource.Awaitable; return AwaitableMotionTaskSource.Create(handle, CancelBehavior.Cancel, true, cancellationToken).Awaitable; } /// /// Convert motion handle to Awaitable. /// /// This motion handle /// Behavior when canceling /// CancellationToken /// public static Awaitable ToAwaitable(this MotionHandle handle, CancelBehavior cancelBehavior, CancellationToken cancellationToken = default) { if (!handle.IsActive()) return AwaitableMotionTaskSource.CompletedSource.Awaitable; return AwaitableMotionTaskSource.Create(handle, cancelBehavior, true, cancellationToken).Awaitable; } /// /// Convert motion handle to Awaitable. /// /// This motion handle /// Behavior when canceling /// Whether to link MotionHandle.Cancel() to task cancellation /// CancellationToken /// public static Awaitable ToAwaitable(this MotionHandle handle, CancelBehavior cancelBehavior, bool cancelAwaitOnMotionCanceled, CancellationToken cancellationToken = default) { if (!handle.IsActive()) return AwaitableMotionTaskSource.CompletedSource.Awaitable; return AwaitableMotionTaskSource.Create(handle, cancelBehavior, cancelAwaitOnMotionCanceled, cancellationToken).Awaitable; } #endif } /// /// Specifies the behavior when the motion converted to IDisposable is disposed. /// public enum DisposeBehavior { Cancel, Complete, } internal sealed class MotionHandleDisposable : IDisposable { public MotionHandleDisposable(MotionHandle handle, DisposeBehavior disposeBehavior) { this.handle = handle; this.disposeBehavior = disposeBehavior; } public readonly MotionHandle handle; public readonly DisposeBehavior disposeBehavior; public void Dispose() { switch (disposeBehavior) { case DisposeBehavior.Cancel: handle.TryCancel(); break; case DisposeBehavior.Complete: handle.TryComplete(); break; } } } }