UnityでTaskを数分で使えるようにする方法

# とりあえず使ってみる
Task初心者がUnityでTaskを使えるようにするためのメモ．
まず下記コードを作成しておきましょう．

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestScripts : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Test();
        Debug.Log(4);
        Test();
    }
    void Test()
    {
        Debug.Log(1);
        Debug.Log(2);
        Debug.Log(3);
    }
}
```
(1234123)をコンソールに出力するプログラムです．簡単ですね．
これをTaskを使って色々弄ってみましょう．

Taskはvoidをasync Taskに置き換えるだけで使用できます．そして，awaitを使うとその処理をコルーチンのように待ち，使わずに使えば普通の関数のように実行できます．
次のプログラムを実行してみよう！

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Taskを使用する準備
using System.Threading;
using System.Threading.Tasks;
public class TestScripts : MonoBehaviour
{
    //おまじない
    private SynchronizationContext context;
    // voidをasync Taskに置き換え
    async Task Start()
    {
        //おまじない
        context = SynchronizationContext.Current;
        //実行時にawaitを使用
        await Test();
        Debug.Log(4);
        //実行時にawaitを使用しない
        Test();
        await Test();
        //1000ms待つ
        await Task.Delay(1000);
        Test();
        Debug.Log("StartFuncFinish!");
    }
    //voidをasync Taskに置き換え
    async Task Test()
    {
        Debug.Log(1);
        //1000ms待つ
        await Task.Delay(1000);
        Debug.Log(2);
        //1000ms待つ
        await Task.Delay(1000);
        Debug.Log(3);
        //1000ms待つ
        await Task.Delay(1000);
    }
}
```
先ほどのコードのvoidをasync Taskに置き換えただけのものですね．
そして，待ちたいときはawait，待ちたくないときは普通に実行しています．
ここではStartFuncFinish!と1の出力が同時に行われ，最後には3が出力されているのが解りますか？

つまり
`関数をTaskに置き換えるだけであまり変わっていない`
これがTaskの真髄!これだけでも覚えておけば大丈夫です！

Taskは普通の関数のように引数の指定や，返り値を取得することも可能です.
また，返り値が返ってくるまで待つ処理が可能となります．
引数，返り値を使用する場合は次のように書きます．

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//タスクを使用する準備
using System.Threading;
using System.Threading.Tasks;
public class TestScripts : MonoBehaviour
{
    //おまじない
    private SynchronizationContext context;
    // Start is called before the first frame update
    async Task Start()
    {
        //おまじない
        context = SynchronizationContext.Current;
        //足し算の結果が返ってくるまで待つ．返ったらcに格納される．
        int c = await MyAddFuncAsync(5, 4);
        //cを出力
        Debug.Log(c);
        Debug.Log("StartFuncFinish!");
    }
    //引数2つを足し算し結果を返すTask
    //返り値はintなのでTask<int>のTaskとなる．
    async Task<int> MyAddFuncAsync(int a,int b)
    {
        await Task.Delay(1000);
        return a + b;
    }

    //待たない関数で普通に書いたらこうなる
    int MyAddFunc(int a, int b)
    {
        return a + b;
    }
}
```
最後にawaitを使う場合はasync Task型である必要があるが，普通の関数のように呼ぶ場合はvoidから呼べる．

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//タスクを使用する準備
using System.Threading;
using System.Threading.Tasks;
public class TestScripts : MonoBehaviour
{
    //おまじない
    private SynchronizationContext context;
    // Start is called before the first frame update
    void Start()
    {
        //awaitをつけずにTaskを実行
        StartAsync();
        Debug.Log("StartFuncFinish!");
    }

    async Task StartAsync()
    {
        //おまじない
        context = SynchronizationContext.Current;
        int c=await MyAddFunc(5, 4);
        Debug.Log(c);
        Debug.Log("StartAsyncFuncFinish!");
    }

    async Task<int> MyAddFunc(int a,int b)
    {
        await Task.Delay(1000);
        return a + b;
    }
}
```
おまじないについてはUnityはメインスレッドで処理を行っているため，UnityのAPIを呼ぶと例外が起こります．そのため，今回は理解しやすいようにAPIを使用してもエラーを吐かない方法で記述しています．本来なら非同期で行うことのできる処理をわざとメインスレッドでコルーチンのように同期的に行っています．
詳しくは　
https://hakase0274.hatenablog.com/entry/2018/12/07/202550
https://qiita.com/toRisouP/items/a2c1bb1b0c4f73366bc6
を参照してください．

この方法では，効率が良くないのでUniTaskを導入するのがおすすめです！
これはAssetStoreからUniRxを導入する必要があります．

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="result" data-user="ymtksp" data-slug-hash="LvRYZd" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="LvRYZd">
  <span>See the Pen <a href="https://codepen.io/ymtksp/pen/LvRYZd/">
  LvRYZd</a> by TKSP (<a href="https://codepen.io/ymtksp">@ymtksp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

UniTaskはUnityに最適化されたTaskで，TaskをUniTaskに置き換え，`using UniRx.Async`を宣言することで使用可能となります．
また，非同期処理を行いつつ自動でメインスレッドに戻ってきてくれるため，おまじないも必要ありません．
UniTaskでは`UniTask.Delay()`の他に`UniTask.DelayFrame()`があるためコルーチンの`yield return null`のようなフレーム単位での待ちが可能となります．

http://neue.cc/2018/07/12_567.html

先ほどのコードをUniTaskで書くと次のようになります．

```C#
using UnityEngine;
//UniTaskを使用する準備
using UniRx.Async;
public class TestScripts : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        //awaitをつけずにTaskを実行
        StartAsync();
        Debug.Log("StartFuncFinish!");
    }

    async UniTask StartAsync()
    {
        int c=await MyAddFunc(5, 4);
        Debug.Log(c);
        Debug.Log("StartAsyncFuncFinish!");
    }

    async UniTask<int> MyAddFunc(int a,int b)
    {
        await UniTask.Delay(1000);
        return a + b;
    }
}
```
さあ，みんなでTaskを使おう！

Taskをもっと詳しく知りたい人は
https://qiita.com/acple@github/items/8f63aacb13de9954c5da
を参照してください．