ãã®è¨äºã¯Â Akatsuki Advent Calendar 2019 - Adventar 5æ¥ç®ã®è¨äºã§ãã
- ã¯ããã«
- UniTaskã¨ã¯ð¤ð¤ð¤
- ãããããã¨ð¤
- UniTaskã®ä¸èº«ã解æãã¦ã¿ããð¤
- ãªã¬ãªã¬Taskãä½ã£ã¦ã¿ããï¼
Â
ã¯ããã«
ã¯ã©ã¤ã¢ã³ãã¨ã³ã¸ãã¢ã®kawawa1989ã§ãã
ç¾å¨ã¯Unityã使ã£ã¦ã®éçºãè¡ã£ã¦ãã¾ããä»åã¯åãã¦å°å ¥ããUniTaskã«ã¤ãã¦ãç´¹ä»ããããã¨æãã¾ãã
Â
UniTaskã¨ã¯ð¤ð¤ð¤
CysharpãããéçºããããUnityã§ã¡ã¤ã³ã¹ã¬ããã使ã£ã¦async/awaitã使ããããã«ãããã©ã°ã¤ã³ã§ãã
ã¡ãªã¿ã«UniRxã¯äºåã«å
¥ããå¿
è¦ãããã¨ãããããããã¨ã¯ããã¾ããã§ããã
UniRxãªãã§ãåä½ã§æ±ããã¨ãã§ãã¾ãã
tech.cygames.co.jp
ä»ã¾ã§ã¯IEnumeratorã使ã£ã¦æ¸ãã¦ããç®æãasync/awaitã§ç½®ãæãããã¨ãã§ããããã«ãªãã¾ãã
Â
ãããããã¨ð¤
éä¿¡ãªã©ã®éåæå¦çãªã©ãå¤ãå ´åããéä¿¡ãæåãã(or失æãã)ãã®ã³ã¼ã«ããã¯é¢æ°ã ããã«ãªã£ã¦ãã¾ãã¾ãã
ä¾ãã°ãã°ã¤ã³ã®ããã¼ã§ãããªã³ã¼ãããã£ãã¨ãã¾ãã
// ãã°ã¤ã³ããã¼ // ä¾ãã°ãã°ã¤ã³ãã¿ã³ãã¿ããããã¦ãã°ã¤ã³ããã¨ããå ´å void OnLoginButtonClick() { // å©ç¨è¦ç´ãèªã¾ãã¦ããªããªãå©ç¨è¦ç´UIãåºããã if (!HasReadTermOfUse) { OpenTermOfUseDialog(onAgreed: OnAgreedEvent); return; } // ãã°ã¤ã³APIãå©ã NetworkManager.Login(onLoginSuccess: OnLoginSuccessEvent); } // å©ç¨è¦ç´ãåæããã void OnAgreedEvent() { // ãã°ã¤ã³APIãå©ã NetworkManager.Login(onLoginSuccess: OnLoginSuccessEvent); } // ãã°ã¤ã³å®äº void OnLoginSuccessEvent() { LoadNextScene(); }
 ã¶ã£ã¡ãããã®ã¬ãã«ãªãå人çã«ã¯è¨±å®¹ã§ããã®ã§ãããç§ãå®éã«çµé¨ããã³ã¼ãã ã¨ãã°ã¤ã³ä¸ã¤ã§ãããé¥ãã«è¶
ããè¤éãªä½ãã«ãªã£ã¦ãã¾ããã
ãªã®ã§ããããã¾ãå
·ä½çãªãã¨ã¯ç¤¾å¤ç§ã®ããã¨ãããããã®ç¯å²ã§...
ä¸è¨ã®ãããªå¦çããã§ãããªãä¸ã¤ã®é¢æ°ã®ä¸ã§ä½ããã£ã¦ããã®ããã¹ã¦ç解ã§ããããã«ãããã§ããã
Â
ã¾ãæåã«èããããæ¹ð¤
æåã¯ã³ã«ã¼ãã³ã§ãã£ã¦ãã¾ããã¨æãã¾ããã
IEnumerator LoginFlow() { if (!HasReadTermOfUse) { var dialog = OpenTermOfUseDialog(); // ãã¤ã¢ãã°ã表示ãã¦ããéãã£ã¨å¾ æ©ãç¶ãã yield return dialog; // çµäºãããæ¿èªããããã©ããã®å¤å®ãè¡ã // å©ç¨è¦ç´ã«åæãã¦ããããªãã£ããããã§çµäº if (!dialog.Agreed) { yield break; } } // ãã°ã¤ã³APIãå©ã var login = NetworkManager.Login(); yield return login; // ãã°ã¤ã³ã«æåããã®ãã©ãã // 失æãããªãçµäº if (!login.Success) { yield break; } LoadNextScene(); }
ããã§ä¸ã¤ã®é¢æ°å
ã«åãããã¨ãã§ãã¾ã
ãããã¡ãã¡AsyncOperationçãªãã¤ãåããªãã¨ãããªãã®ãé¢åãããã§ãã
UniRxã使ãã°FromCoroutineã§ã©ãã«ããªããã®ã®ãStartCoroutineãå¼ã³åºãå
ã«ãªãã¹ã¯ãªããããªãã¨åãããªãã®ãæ¬ ç¹ã§ãã
ã¿ãããªãã¨ãä¼ç¤¾ã®ååã¨è©±ãã¦ãã¦ãããããäºãããããªãUniTaskã£ã¦ã®ãããããããã使ã£ã¦ã¿ã¦ã¯ï¼ã
ã¨ããæ¡ãåºã¾ããã
ã¨ããããã§æ©éå°å
¥ãã¦ã¿ã¾ããã
åèã«ãªã£ãè¨äº
UniRx.Async(UniTask)機能紹介 - Qiita
å½åãIncremental Compilerãªããã®ãå¿
è¦ã§ã
ãããããã¯Previewçã¨ã®ãã¨ã ã£ãã®ã§è¥å¹²UniTaskã¯é¿ãã¦ããã®ã§ãããã©ãããããã¯æã®è©±ã§æè¿ã¯éããããã¨ç¥ãã¾ããã
Â
Incremental Compilerãä¸è¦ã«ï¼ð
neue cc - UniTask(UniRx.Async)から見るasync/awaitの未来
ã¨ãããã¨ã§ããã§å®å¿ãã¦å°å
¥ã§ãã¾ãï¼ï¼
async UniTask LoginFlow() { // ãã¤ã¢ãã°ã表示ãã¦çµæãè¿ã£ã¦ããã¾ã§å¾ æ©ããã if (!HasReadTermOfUse) { var agreed = await OpenTermOfUseDialog(); if (!agreed) { return; } } // ãã°ã¤ã³APIãå©ã var success = await NetworkManager.Login(); // ã¨ã©ã¼ã ã£ããçµäº if (!success) { return; } LoadNextScene(); }
ããã¹ãããªãã¾ããï¼
æ»ãå¤ãawaitã§å¾
æ©ããªããåãåããã®ã¯ä¾¿å©ã§ããï¼
ã¨ã¯ããå ¨é¨ããããªãUniTaskã«ç½®ãæããã®ã¯é£ãããªãï¼
ç§ãUniTaskãå°å
¥ããã®ã¯ããã¸ã§ã¯ãã®ããªãçµç¤ã§ããã
ãã®ãããå
¨ã¦ãUniTaskã«ç½®ãæãããã¨ã¯ã§ããªãã®ã§ãããããã§ãããããã¯ããã¾ãã
ä¾ãã°ãã¤ã¢ãã°é¢ä¿ã¯ãã®ããã«æ¸ãæãããã¨ãã§ãã¾ãã
// å ã®ã³ã¼ã public void ShowDialog(string title, string message, Action onClose) { var prefab = Resources.Load("HogeHogeDialog"); var dialog = Instantiate(prefab); dialog.Initialize(title, message, onClose); } // ãããå ã«æ¹é public UniTask ShowDialogAsync(string title, string message) { var source = new UniTaskCompletionSource(); var prefab = Resources.Load("HogeHogeDialog"); var dialog = Instantiate(prefab); dialog.Initialize(title, message, () => { source.TrySetResult(); }); return source.Task; }
UniTaskCompletionSource
UniTaskCompletionSourceã¨ãããã¤ã§çµäºæã«TrySetResultããã°å
ã®å®è£
ãããããå¿
è¦ãªãUniTaskã§å¾
ã¦ãããã«æ¹é ã§ãã¾ãï¼
ãããæåã¯èªåã¯ã¾ã£ããç¥ããªãã£ãæ©è½ã§ãç¥ãåã¾ã§ã¯æ¯å
var isDone = false; dialog.Initialize(title, message, () => { isDone = true; }); await UniTask.WaitUntil(() => isDone);
ãããªé¢¨ã«æ¸ãã¦ã¾ããã
UniTaskã®ä¸èº«ã解æãã¦ã¿ããð¤
ã©ãåãã¦ããã®ãé常ã«æ°ã«ãªã£ãã®ã§ãä¸èº«ã解æãã¦ã¿ã¾ããã
PlayerLoopHelper
ã³ã«ã¼ãã³ã®å ´åãStartCoroutineãèµ°ãããããã®MonoBehaviourãã©ããã¦ãå¿
è¦ã§ããã
ã§ããããPlayerLoopãä¸æ¸ãã§ããããã«ãªã£ãã®ã§ããã®ä»çµã¿ã§ç´æ¥UniTaskãèµ°ãããç¨ã®Updateé¢æ°ãç»é²ãã¦ããããã§ããã
PlayerLoopã«é¢ãã¦ã®åèãªã³ã¯
tsubakit1.hateblo.jp
PlayerLoopHelper.csã®ä¸èº«
public static class PlayerLoopHelper { public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContetext; public static int MainThreadId => mainThreadId; static int mainThreadId; static SynchronizationContext unitySynchronizationContetext; static ContinuationQueue[] yielders; static PlayerLoopRunner[] runners;
yieldersã¨runnersãã©ãã«ãæ°ã«ãªãã¾ãã
PlayerLoopHelperã®Initã¨ããã¡ã½ãããRuntimeInitializeOnLoadMethodã«ãã£ã¦èµ·åæã«å¼ã³åºããã¦ããããã§ãã
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void Init() { // capture default(unity) sync-context. unitySynchronizationContetext = SynchronizationContext.Current;
Initializeå
ã§ã¯ä»¥ä¸ã®ãããªæãã«ãªã£ã¦ãã¾ããã
InsertRunnerã§ã«ã¼ãå¦çã追å ãã¦ãã¾ãã
public static void Initialize(ref PlayerLoopSystem playerLoop) { yielders = new ContinuationQueue[7]; runners = new PlayerLoopRunner[7]; var copyList = playerLoop.subSystemList.ToArray(); copyList[0].subSystemList = InsertRunner(copyList[0], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[0] = new PlayerLoopRunner()); copyList[1].subSystemList = InsertRunner(copyList[1], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), yielders[1] = new ContinuationQueue(), typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), runners[1] = new PlayerLoopRunner()); copyList[2].subSystemList = InsertRunner(copyList[2], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), yielders[2] = new ContinuationQueue(), typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), runners[2] = new PlayerLoopRunner()); copyList[3].subSystemList = InsertRunner(copyList[3], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), yielders[3] = new ContinuationQueue(), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), runners[3] = new PlayerLoopRunner()); copyList[4].subSystemList = InsertRunner(copyList[4], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), yielders[4] = new ContinuationQueue(), typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), runners[4] = new PlayerLoopRunner()); copyList[5].subSystemList = InsertRunner(copyList[5], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), yielders[5] = new ContinuationQueue(), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), runners[5] = new PlayerLoopRunner()); copyList[6].subSystemList = InsertRunner(copyList[6], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), yielders[6] = new ContinuationQueue(), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[6] = new PlayerLoopRunner());
Â
InsertRunnerã®ä¸èº«ãè¦ãã¨ãããyieldLoopã¨ããã®ã¨runnerLoopã¨ããã«ã¼ããããã®ã§ããã
ãããUnityã®åPlayerLoopã«ï¼ã¤ãã¤æ¿å
¥ãã¦ãã¾ãã
Â
ãã®ã«ã¼ãã®ä¸ã§å®éã«ãã£ã¦ããå¦çã¯ContinuationQueueã¨PlayerLoopRunnerã¨ãããã¤ãããããèµ°ããã¦ãã¾ãã
yieldLoopå
ã§ContinuationQueueã®Runãèµ°ããã¦
runnerLoopå
ã§PlayerLoopRunnerã®Runãèµ°ããã¦ãã¾ãã
ã§ã¯æ¬¡ã«ContinuationQueueã¨PlayerLoopRunnerãè¦ã¦ã¿ã¾ãã
ContinuationQueue
å
é¨çã«ã¯actionListã¨ããActionã®é
åã管çãã¦ããããã§ãã
ããã¦ãããRunå®è¡æã«å¼ã³åºãã¦ããã
Enqueueã¨ããã¡ã½ãããç¨æããã¦ãã¦ããããå¼ã³åºããã¨ã§Actionãç»é²ã§ãããã§ãã
ã§ã¯ãã®Enqueueã¯ã©ãããå¼ã³åºããã¦ããã®ã ããï¼
ã¨æã£ã¦è¿½ã£ã¦ã¿ãããPlayerLoopHelperã§ç®¡çãã¦ããããã§ããã
ã¤ã¡ã¼ã¸ã¨ãã¦ã¯ä»¥ä¸ã®å³ã®ãããªæãã§ããããã
ã§ã¯ã次ã«ãã®AddActionã¨AddContinautionãã©ãããå¼ã³åºããã¦ããã調ã¹ã¦ã¿ã¾ãã
ReusablePromise.csãæ°ã«ãªãã®ã§è¦ã¦ã¿ã¾ãã
PlayerLoopReusablePromiseBaseã¨ããã¯ã©ã¹ã®IsCompletedã¨ããã¡ã½ããã§å¼ã³åºããã¦ãã¾ããã
PlayerLoopReusablePromiseBase
public override bool IsCompleted { get { if (Status == AwaiterStatus.Canceled || Status == AwaiterStatus.Faulted) return true; if (!isRunning) { isRunning = true; ResetStatus(false); OnRunningStart(); #if UNITY_EDITOR TaskTracker.TrackActiveTask(this, capturedStackTraceForDebugging); #endif PlayerLoopHelper.AddAction(timing, this); } return false; } }
PlayerLoopReusablePromiseBaseの実装ãè¦ãã°ãããã¾ãããIPlayerLoopã¨ãããã¤ãå®è£
ãã¦ãã¾ããã
public abstract class PlayerLoopReusablePromiseBase : ReusablePromise, IPlayerLoopItem {
ããã¯MoveNextã¨ããã¡ã½ãããæä¾ããã¤ã³ã¿ã¼ãã§ã¼ã¹ã®ããã§ã
MoveNextã¡ã½ããã¯PlayerLoopReusablePromiseBaseãç¶æ¿ããå
ã§å®è£
ããã¦ãã¾ããã
WaitUntilPromise
WaitWhilePromise
YieldPromise
PlayerLoopRunnerã¯åºæ¬çã«ãã®IPlayerLoopItemãé
åã§ç®¡çãã¦ãæ¯ã«ã¼ãæã«åIPlayerLoopItemã®MoveNextãå¼ã³åºãã¦ããããã§ãã
次ã¯UniTaskãè¦ã¾ãã
AddActionã¡ã½ããã«ãã°ãä»è¾¼ãã§ã¿ã¾ããã
ã§ãä¸è¨ã®ãããªãµã³ãã«ã³ã¼ããæ¸ãã¦ã¿ã¾ãããã¨ããããï¼ç§éå¾
ã¤ã¿ã¹ã¯ã§ãã
bool isDone = false; private void Awake() { Run().Forget(); StartCoroutine(WaitCoroutine()); } private IEnumerator WaitCoroutine() { yield return new WaitForSeconds(3.0f); isDone = true; } private async UniTask Run() { Debug.Log("Run Start!!"); await UniTask.WaitUntil(() => isDone); Debug.Log("Run End!!"); }
å¼ã°ãã¦ãã¾ãï¼
å¼ã³åºãå
ãè¦ã¦ã¿ãã¨ãPlayerLoopReusablePromiseBaseã®IsCompletedã¨ããããããã£ã§å¼ã³åºããã¦ããã®ããããã¾ãã
PlayerLoopReusablePromiseBaseã®IsCompletedãå¼ã³åºãã¦ããã®ã¯UniTaskã®IsCompletedããããã£ã®ããã§ãã
UniTask
IsCompleted
[DebuggerHidden] public bool IsCompleted { get { return awaiter == null ? true : awaiter.IsCompleted; } }
UniTaskãawaiterã¨ãããã£ã¼ã«ããæã£ã¦ãããã¨ããããã¾ãã
ãã®awaiterã«PlayerLoopReusablePromiseBaseã®ã¤ã³ã¹ã¿ã³ã¹ãè¨å®ãã¦ããã¨ãããã¨ã«ãªãã¾ã
ããã¦ããããå¼ã³åºãã¦ããã®ã¯Awaiterã¨ãããã¤ã®ããã§ãAwaiterã¯UniTaskå
ã«å®ç¾©ããã¦ãã¾ãã
Awaiter
Awaiter
public struct Awaiter : IAwaiter { readonly UniTask task; [DebuggerHidden] public Awaiter(UniTask task) { this.task = task; }
ããã¤ã®ä¸ã«UniTaskãå
å
ããã¦ããããã§ãã
ã§ãå®éã¯Awaiterã®IsCompletedããUniTaskã®ããããã£ã«ã¢ã¯ã»ã¹ãã¦ããããã§ãã
IAwaiter
IAwaiterã®å®ç¾©èªä½ã¯ãããªæãã§ãã
public interface IAwaiter : ICriticalNotifyCompletion { AwaiterStatus Status { get; } bool IsCompleted { get; } void GetResult(); }
ICriticalNotifyCompletion
ICriticalNotifyCompletionã¯ã¡ãã£ã¨ããããããªãã®ã§èª¿ã¹ã¾ãã
ã©ããTaskã§ã使ç¨ããã¦ããã¤ã³ã¿ã¼ãã§ã¼ã¹ã®ããã§ãã
.NET Frameworkæ¨æºã§æä¾ããã¦ããTaskAwaiter/TaskAwaiteråã¯INotifyCompletionã¤ã³ã¿ã¼ãã§ã¼ã¹ãç¶æ¿ããICriticalNotifyCompletionã¤ã³ã¿ã¼ãã§ã¼ã¹ãå®è£ ãã¦ãã¾ãã
参考記事
ãã®IsCompletedã¯ã©ãã§å¼ã°ãã¦ããã®ãï¼
IsCompletedå
é¨ã«ãã°ãä»è¾¼ãã§ã¿ã¾ãã
ã©ãããAsyncUniTaskMethodBuilderã¨ããããã¤ãå±
ãããã§ãããã¤ãå¼ã³åºãã¦ããããã§ãã
ã§ã¯ä»åº¦ã¯ããã¤ã®Startã¡ã½ããã«åããããªãã°ãä»è¾¼ãã§ã¿ã¾ãããã
ãã以ä¸å
ã¯ããä½ãããã¾ããã
ãã®AsyncUniTaskMethodBuilderãä½è
ãªã®ãï¼ã¨ãããã調ã¹ã¦ããããããã¯ã©ããã³ã³ãã¤ã©å´ã使ç¨ããã¯ã©ã¹ã®ããã§ã
www.atmarkit.co.jp
IAsyncStateMachineã¨ãããã¤ãä½ãé¢é£ãããããã§ãã
ã³ã¤ãã«ã¤ãã¦èª¿ã¹ã¦ã¿ã¾ãããã
docs.microsoft.com
éåæã¡ã½ããã«çæãããã¹ãã¼ã ãã·ã³ã表ãã¾ãã ãã®åã¯ã³ã³ãã¤ã©ã§ã®ã¿ä½¿ç¨ããã¾ãã
ä»ã«ããã®ãããªè¨äºãããã¾ããã
blog.xin9le.net
ã»async/awaitã¯ä½ãã®ç³è¡£æ§æã§ãå®éã¯ã³ã³ãã¤ã©ããã£ã¨è¤éãªå½¢ã«å¤æãã¦ãã
ã»exeãdllã«ã¯å¤æå¾ã®å½¢ã§æ ¼ç´ããã¦ãã
ã¨ã®ãã¨ãªã®ã§ã試ãã«ILSpyã§ä¸èº«ãéãã¦ã¿ã¾ãã
.method /* 06000006 */ private hidebysig instance valuetype [UniRx.Async]UniRx.Async.UniTask Run () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 12 54 65 73 74 43 6f 64 65 2b 3c 52 75 6e 3e 64 5f 5f 36 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2080 // Code size 59 (0x3b) .maxstack 2 .locals /* 11000001 */ init ( [0] class TestCode/'<Run>d__6', [1] valuetype [UniRx.Async]UniRx.Async.CompilerServices.AsyncUniTaskMethodBuilder ) IL_0000: newobj instance void TestCode/'<Run>d__6'::.ctor() /* 06000012 */ IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldarg.0 IL_0008: stfld class TestCode TestCode/'<Run>d__6'::'<>4__this' /* 04000008 */ IL_000d: ldloc.0 IL_000e: call valuetype [UniRx.Async]UniRx.Async.CompilerServices.AsyncUniTaskMethodBuilder [UniRx.Async]UniRx.Async.CompilerServices.AsyncUniTaskMethodBuilder::Create() /* 0A000012 */ IL_0013: stfld valuetype [UniRx.Async]UniRx.Async.CompilerServices.AsyncUniTaskMethodBuilder TestCode/'<Run>d__6'::'<>t__builder' /* 04000007 */ IL_0018: ldloc.0 IL_0019: ldc.i4.m1 IL_001a: stfld int32 TestCode/'<Run>d__6'::'<>1__state' /* 04000006 */ IL_001f: ldloc.0 IL_0020: ldfld valuetype [UniRx.Async]UniRx.Async.CompilerServices.AsyncUniTaskMethodBuilder TestCode/'<Run>d__6'::'<>t__builder' /* 04000007 */ IL_0025: stloc.1 IL_0026: ldloca.s 1 IL_0028: ldloca.s 0 IL_002a: call instance void [UniRx.Async]UniRx.Async.CompilerServices.AsyncUniTaskMethodBuilder::Start<class TestCode/'<Run>d__6'>(!!0&) /* 2B000001 */ IL_002f: ldloc.0 IL_0030: ldflda valuetype [UniRx.Async]UniRx.Async.CompilerServices.AsyncUniTaskMethodBuilder TestCode/'<Run>d__6'::'<>t__builder' /* 04000007 */ IL_0035: call instance valuetype [UniRx.Async]UniRx.Async.UniTask [UniRx.Async]UniRx.Async.CompilerServices.AsyncUniTaskMethodBuilder::get_Task() /* 0A000014 */ IL_003a: ret } // end of method TestCode::Run
AsyncUniTaskMethodBuilder::Create()ã¨ãAsyncUniTaskMethodBuilder::Start()ã¨ãå¼ã°ãã¦ã¾ãï¼
ILã³ã¼ãããããããã¾ããããcallã¨ãå¼ã°ãã¦ã¾ããã
ï¼ãã®è¾ºããã®ãã¡åå¼·ãã¦ã¿ããï¼
[åèãªã³ã¯]
www5b.biglobe.ne.jp
次ã«æ°ã«ãªã£ããã¨
ã©ããã£ã¦AsyncUniTaskMethodBuilderãå¼ã³åºãã¦ããï¼ð¤
ãããã¡ãã£ã¨è¬ã§ããããUniTask.csã®å®ç¾©ãããè¦ãã¨AsyncMethodBuilderã¨ãããã¤ãããã¾ãï¼
[AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder))] // âããï¼ï¼ï¼ï¼ public partial struct UniTask : IEquatable<UniTask>
AsyncMethodBuilderã«é¢ãã¦åèãªã³ã¯
qiita.com
ã¤ã¾ãawaitãå¼ã°ããã¨ãããã®æ»ãå¤ã®åã«å¯¾å¿ãããAsyncMethodBuilderãå¼ã³åºããã
ã¨ãããã¨ã§ããããï¼
ããã調ã¹ã¦ããã¾ãåºã¦ããªãã®ã§ã¡ãã£ã¨ããããªãã
ãããããããªæã
試ãã«
UnsafeOnCompleted(Action act)
ã§åãåãããªã²ã¼ããæå¾ã«å¼ã³åºããªãããã«ãã¦ã¿ãã
æ°¸é ã«awaitããæ»ã£ã¦ãããªãç¶æ
ã«ãªã£ãã®ã§ãããããããã¯å¦çãçµäºãããã¨ã«å¼ã³åºããªãã¨ãããªããã¤ã
GetAwaiterãä½è
ãªã®ãæ°ã«ãªã£ããã©ãããã¯æ»ãå¤ã®åãç¬èªå®ç¾©åã§ãããã¨ãªãã ããã¨é¢ä¿ãªãå¼ã°ããããã«è¦ãã¾ãã
docs.microsoft.com
ã¡ãªã¿ã«GetAwaiterãã³ã¡ã³ãã¢ã¦ãããã¨awaitããç®æå
¨ã¦ã§ã³ã³ãã¤ã«ã¨ã©ã¼ã«ãªã£ã¦æ»ã«ã¾ãã
è¦ç´
å
¨ä½ã®ã©ã¤ããµã¤ã¯ã«ã¯ãããªæãï¼ã ã¨æãããð¤
ãªã¬ãªã¬Taskãä½ã£ã¦ã¿ããï¼
試ãã«èªåã§awaitã§ããã¿ã¹ã¯ãä½ã£ã¦ã¿ã¾ãã
UniTaskã¯structã§å®ç¾©ããã¦ãã¾ããããstructã ã¨ããã©ã«ãã³ã³ã¹ãã©ã¯ã¿ãå®ç¾©ã§ããªãã®ã§ä»åã¯classã§å®ç¾©ãã¦ãã¾ãã
using System.Runtime.CompilerServices; using System; namespace Bz.Brotherhood { [AsyncMethodBuilder(typeof(BzTaskBuilder))] public class BzTask { IAwaiter awaiter; public BzTask() { UnityEngine.Debug.Log($"BzTask.Constructor"); } public BzTask(IAwaiter awaiter) { this.awaiter = awaiter; } public IAwaiter GetAwaiter() { UnityEngine.Debug.Log($"BzTask.GetAwaiter"); return new Awaiter(awaiter); } private struct Awaiter : IAwaiter { IAwaiter awaiter; public Awaiter(IAwaiter awaiter) { this.awaiter = awaiter; } public bool IsCompleted { get { var isCompleted = true; if (awaiter != null) { isCompleted = awaiter.IsCompleted; } UnityEngine.Debug.Log($"Awaiter.IsCompleted :{isCompleted}"); return isCompleted; } } public void OnCompleted(Action moveNext) { UnityEngine.Debug.Log("Awaiter OnCompleted"); awaiter?.OnCompleted(moveNext); } public void UnsafeOnCompleted(Action moveNext) { UnityEngine.Debug.Log("Awaiter UnsafeOnCompleted"); awaiter?.UnsafeOnCompleted(moveNext); } public void GetResult() { UnityEngine.Debug.Log("Awaiter GetResult"); awaiter?.GetResult(); } } }
AsyncMethodBuilderAttributeã¯åå¨ããªãï¼
ã³ã¼ããæ¸ãã¦ã¿ãã®ã§ããã¨ã©ã¼ãåºã¾ãã
UniTaskã¯AsyncMethodBuilderAttributeãå®ç¾©ãã¦ããããã§ããã
blog.meilcli.net
ã¾ããç¾æç¹ã§ã»ã¨ãã©ã®ãã©ãããã©ã¼ã ã§ã¯AsyncMethodBuilderAttributeå±æ§ãèªåã§å®ç¾©ããå¿ è¦ãããã¾ãã
çç±ã¯ããããããªãã®ã§ãããã¨ã«ãããã£ã¡ã§å®ç¾©ããªãã¨é§ç®ãããã
以ä¸ã®ãããªãªã¬ãªã¬ã¿ã¹ã¯ãã«ãã¼ãä½ã£ã¦ã¿ã¾ããï¼ã¨ãã£ã¦ãã»ã¼UniTaskBuilderã®ã³ããï¼
namespace Bz.Brotherhood { public struct BzTaskBuilder { IAwaiter awaiter; // 1. Static Create method. public static BzTaskBuilder Create() { UnityEngine.Debug.Log("BzTaskBuilder.Create"); var builder = new BzTaskBuilder(); return builder; } // 2. TaskLike Task property. public BzTask Task { get { UnityEngine.Debug.Log("BzTaskBuilder.Task"); return new BzTask(awaiter); } } // 3. SetException public void SetException(Exception exception) { UnityEngine.Debug.Log("BzTaskBuilder.SetException"); } // 4. SetResult public void SetResult() { UnityEngine.Debug.Log("BzTaskBuilder.SetResult"); } // 5. AwaitOnCompleted public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { UnityEngine.Debug.Log("BzTaskBuilder.AwaitOnCompleted"); this.awaiter = awaiter as IAwaiter; this.awaiter.OnCompleted(stateMachine.MoveNext); } // 6. AwaitUnsafeOnCompleted [SecuritySafeCritical] public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { UnityEngine.Debug.Log("BzTaskBuilder.AwaitUnsafeOnCompleted"); this.awaiter = awaiter as IAwaiter; this.awaiter.UnsafeOnCompleted(stateMachine.MoveNext); } // 7. Start public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { UnityEngine.Debug.Log("BzTaskBuilder.Start"); stateMachine.MoveNext(); } // 8. SetStateMachine public void SetStateMachine(IAsyncStateMachine stateMachine) { UnityEngine.Debug.Log("BzTaskBuilder.SetStateMachine"); } } }
ããã¦æå¾ã«ãããªãã¹ãã³ã¼ãã試ãã«ä½ã£ã¦ã¿ã¾ãã
void Start() { Debug.Log("Start"); TestTask(); } async BzTask HelloWorldTask() { Debug.Log("Hello World!!"); } async BzTask TestTask() { Debug.Log("TestTask Start"); await HelloWorldTask(); Debug.Log("TestTask End"); }
ããã§ãä¸å¿ã¨ã©ã¼ã«ãªãã¾ããï¼ã¾ã£ããå¾
æ©ã¨ãããªããã©ï¼
ããã¦å®è¡ãã¦ã¿ã¾ãã
åããï¼
ç¾å¨ã®ã¾ã¾ã ã¨awaitã¨ãè¨ã£ã¦ãããªããå
¨ãå¾
æ©ãã¦ããªãã®ã§ä»åº¦ã¯ä¾ãã°60ãã¬ã¼ã å¾ã«å¦çãé²è¡ããããã«ãã¦ã¿ããã§ãã
ãã®BzTaskã®IAwaiterã«ä½ãå®è£
ã渡ãã°ãã®å®è£
ã«åããã¦åãããã«ãªã£ã¦ãã¾ãã
IAwaiterã®ä¸èº«ã¯UniTaskã®ãã®ã¨ã»ã¼åãã§ãã
public interface IAwaiter : ICriticalNotifyCompletion { // BzTask.Awaiterå é¨ã§å¼ã³åºãããã bool IsCompleted { get; } void GetResult(); }
ããã¦ãã®IAwaiterãå®è£
ãã60ãã¬ã¼ã å¾
æ©ããã¯ã©ã¹ãä½ã£ã¦ã¿ã¾ãã
private interface IOnUpdate { bool OnUpdate(); } private class FrameAwater : IAwaiter, IOnUpdate { int frame = 0; int waitFrameCount = 0; Action moveNext = null; public FrameAwater(int waitFrameCount) { this.waitFrameCount = waitFrameCount; } public bool OnUpdate() { Debug.Log($"OnUpdate[frame:{frame}, waitFrameCount:{waitFrameCount}]"); if (frame == waitFrameCount) { moveNext(); return false; } frame += 1; return true; } // BzTask.Awaiterå é¨ã§å¼ã³åºãããã public bool IsCompleted { get { return false; } } public void GetResult() { } public void OnCompleted(Action continuation) { moveNext = continuation; } public void UnsafeOnCompleted(Action continuation) { moveNext = continuation; } }
ããã¦MonoBehaviourå´ã«IOnUpdateãUpdateæã«ã³ã¼ã«ãããã¿ãããªãã¤ã追å ãã¾ãã
List<IOnUpdate> list = new List<IOnUpdate>(); List<IOnUpdate> removeList = new List<IOnUpdate>(); void Update() { removeList.Clear(); foreach (var update in list) { if (!update.OnUpdate()) { removeList.Add(update); } } foreach (var item in removeList) { list.Remove(item); } }
ããã¦è©¦ãã«60ãã¬ã¼ã å¾ æ©ããããã«ä»¥ä¸ã®ããã«æ¸ãæãã¦ã¿ã¾ãã
void Start() { Debug.Log("Start"); TestTask(); } BzTask WaitForFrame(int frame) { Debug.Log("WaitForFrame"); var frameAwaiter = new FrameAwater(frame); list.Add(frameAwaiter); return new BzTask(frameAwaiter); } async BzTask TestTask() { Debug.Log("TestTask Start"); await WaitForFrame(60); Debug.Log("TestTask End"); }
æå³ããéãã«åãã®ã§ããã°ã
60ãã¬ã¼ã çµéãããUnsafeOnCompleted()ã§åãåã£ãããªã²ã¼ããå®è¡ããããã«ãã£ã¦ã¿ã¹ã¯ã¯çµäºããã¯ãã§ãã
ãã£ãã60ãã¬ã¼ã å¾
æ©ãã¦ããã¦ãã¾ãï¼
é·ããªãã¾ããããã¾ã«ãããã£ã¦ä»ã®äººã®ä½ã£ããã©ã°ã¤ã³ã®ã½ã¼ã¹ã³ã¼ããé¤ãã®ã¯æ¥½ããã§ããã
ããã¾ã§èªãã§ãã ãã£ã¦ãããã¨ããããã¾ããï¼