Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Localization & Animator Extensions #577

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#if UNITASK_LOCALIZATION_SUPPORT

using Cysharp.Threading.Tasks.Linq;
using UnityEngine.Localization;

namespace Cysharp.Threading.Tasks
{
public static partial class LocalizationAsyncExtensions
{
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(
this LocalizedString localizedString)
{
return UniTaskAsyncEnumerable.Create<string>(async (writer, cancellationToken) =>
{
async void Handler(string newValue)
{
await writer.YieldAsync(newValue);
dexsper marked this conversation as resolved.
Show resolved Hide resolved
}

localizedString.StringChanged += Handler;
cancellationToken.Register(() => localizedString.StringChanged -= Handler);

while (!cancellationToken.IsCancellationRequested)
{
await UniTask.Yield();
}
});
}
}
}

#endif

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "UniTask.Localization",
"rootNamespace": "",
"references": [
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:eec0964c48f6f4e40bc3ec2257ccf8c5",
"GUID:5c01796d064528144a599661eaab93a6"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.localization",
"expression": "",
"define": "UNITASK_LOCALIZATION_SUPPORT"
}
],
"noEngineReferences": false
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
using System;
using System.Threading;
using UnityEngine;

namespace Cysharp.Threading.Tasks
{
public static partial class UnityAsyncExtensions
{
public static UniTask WaitAnimationComplete(this Animator animator, string animationName, int layer = 0,
IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.PostLateUpdate)
{
return WaitAnimationComplete(animator, Animator.StringToHash(animationName), layer, progress, timing,
cancellationToken: animator.GetCancellationTokenOnDestroy());
}

public static UniTask WaitAnimationComplete(this Animator animator, int layer = 0,
IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.PostLateUpdate)
{
return WaitAnimationComplete(animator, -1, layer, progress, timing,
cancellationToken: animator.GetCancellationTokenOnDestroy());
}

public static UniTask WaitAnimationComplete(this Animator animator, int animationHash = -1, int layer = 0,
IProgress<float> progress = null,
PlayerLoopTiming timing = PlayerLoopTiming.PostLateUpdate, CancellationToken cancellationToken = default,
bool cancelImmediately = false)
{
if (animator == null)
throw new ArgumentNullException(nameof(animator));

if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);

return new UniTask(
AnimatorStateSource.Create(animator, animationHash, layer, timing, progress, cancellationToken,
cancelImmediately,
out var token), token);
}

sealed class AnimatorStateSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AnimatorStateSource>
{
private static TaskPool<AnimatorStateSource> _pool;
private AnimatorStateSource _nextNode;

public ref AnimatorStateSource NextNode => ref _nextNode;

static AnimatorStateSource()
{
TaskPool.RegisterSizeGetter(typeof(AnimatorStateSource), () => _pool.Size);
}

private Animator _animator;
private int _animationHash;
private int _layer;
private IProgress<float> _progress;
private CancellationToken _cancellationToken;
private CancellationTokenRegistration _cancellationTokenRegistration;
private bool _cancelImmediately;
private bool _completed;

private UniTaskCompletionSourceCore<AsyncUnit> _core;

AnimatorStateSource()
{
}

public static IUniTaskSource Create(Animator animator, int animation, int layer, PlayerLoopTiming timing,
IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}

if (!_pool.TryPop(out var result))
{
result = new AnimatorStateSource();
}

result._animator = animator;
result._animationHash = animation;
result._layer = layer;
result._progress = progress;
result._cancellationToken = cancellationToken;
result._cancelImmediately = cancelImmediately;
result._completed = false;

if (result._cancelImmediately && result._cancellationToken.CanBeCanceled)
{
result._cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(
state =>
{
var source = (AnimatorStateSource)state;
source._core.TrySetCanceled(source._cancellationToken);
}, result);
}

TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);

token = result._core.Version;
return result;
}

public void GetResult(short token)
{
try
{
_core.GetResult(token);
}
finally
{
if (!(_cancelImmediately && _cancellationToken.IsCancellationRequested))
{
TryReturn();
}
}
}

public UniTaskStatus GetStatus(short token)
{
return _core.GetStatus(token);
}

public UniTaskStatus UnsafeGetStatus()
{
return _core.UnsafeGetStatus();
}

public void OnCompleted(Action<object> continuation, object state, short token)
{
_core.OnCompleted(continuation, state, token);
}

public bool MoveNext()
{
if (_completed || _animator == null || !_animator.enabled)
{
return false;
}

if (_cancellationToken.IsCancellationRequested)
{
_core.TrySetCanceled(_cancellationToken);
return false;
}

AnimatorStateInfo stateInfo = _animator.GetCurrentAnimatorStateInfo(_layer);

if (_animationHash != -1 && stateInfo.shortNameHash != _animationHash)
return true;

float normalizedTime = stateInfo.normalizedTime;
float progressValue = Mathf.Clamp01(normalizedTime);

_progress?.Report(progressValue);

if (progressValue < 1f)
return true;

_core.TrySetResult(AsyncUnit.Default);
return false;
}

private bool TryReturn()
{
TaskTracker.RemoveTracking(this);

_core.Reset();
_animator = default;
_progress = default;
_cancellationToken = default;
_cancellationTokenRegistration.Dispose();
_cancelImmediately = default;

return _pool.TryPush(this);
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.