Taskを使ったタイマー(遅延処理)で、Unityオブジェクトにアクセスする方法

Unityオブジェクトは、メインスレッドでしかアクセスができません。Taskを使ってマルチスレッド側で処理すると、Unityオブジェクトにはアクセスすることができません。

サンプルプロジェクト

プロジェクト

適当に空のゲームオブジェクトを作成して、次のメソッドをアタッチます。
コードの実行結果、また、Transform.positionが変化しているのを確認します。

Unityオブジェクトのアクセスが必要がない

Unityオブジェクトへのアクセスがないケースを参考のため掲載します。コードはシンプルになりますね。

コード

using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class Sample1 : MonoBehaviour
{
    void Start()
    {
        ShowLog("1.Start 開始");

        Task.Run(() => AsyncCounter());

        ShowLog("2.Start 終了 ");
    }

    async Task AsyncCounter()
    {
        ShowLog("3.AsyncCounter 開始 ");

        await Task.Delay(3000);

        ShowLog("4.AsyncCounter 終了");
    }

    int order = 0;

    void ShowLog(string msg)
    {
        Debug.Log($"順番(確認:{++order}. {msg} スレッド番号:{Thread.CurrentThread.ManagedThreadId}");
    }
}

実行結果

呼び出し元で遅延させる方法

呼び出し元で、Taskの終了を待ってから実行するサンプルになります。

コード

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class SampleTaskMain1 : MonoBehaviour
{
    // xx. は、実行番号順 (例えば、1. Start 開始は、最初に実行される)
    async void Start()
    {
        ShowLog("1.Start 開始 ");

        Task task = Task.Run(() => AsyncCounter());

        ShowLog("2. Start 終了 ");

        await task;

        ShowLog("5. 移動処理");

        transform.position = new Vector3(1, 0, 0);

        ShowLog("6. Start全て 終了 ");
    }

    async Task AsyncCounter()
    {
        ShowLog("3. AsyncCounter 開始 ");

        await Task.Delay(3000);

        ShowLog("4. AsyncCounter 終了");
    }

    int order = 0;

    void ShowLog(string msg)
    {
        Debug.Log($"順番(確認:{++order}. {msg} スレッド番号:{Thread.CurrentThread.ManagedThreadId}");
    }
}

実行結果

呼び出し先で遅延させる方法

現在のスレッド(メインスレッド)で実行するように指定しています。
これにより、全てのコードがメインスレッドで実行されます。

コード

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class SampleTaskMain : MonoBehaviour
{
    SynchronizationContext context;

    // xx. は、実行番号順 (例えば、1. Start 開始は、最初に実行される)
    void Start()
    {
        context = SynchronizationContext.Current;

        ShowLog("1. Start 開始");

        Task task = Task.Run(() =>
        {
            context.Post(async _ =>
           {
               Task waitTask = AsyncCounter();

               await waitTask;

               ShowLog("6. Start全て 終了 ");
           }, null);
        });

        ShowLog("2. Start 終了 " + Thread.CurrentThread.ManagedThreadId);
    }

    async Task AsyncCounter()
    {
        ShowLog("3. AsyncCounter 開始 ");

        await Task.Delay(3000);

        ShowLog("4. 移動処理");

        transform.position = new Vector3(1, 0, 0);

        ShowLog("5. AsyncCounter 終了");
    }

    int order = 0;

    void ShowLog(string msg)
    {
        Debug.Log($"順番(確認:{++order}. {msg} スレッド番号:{Thread.CurrentThread.ManagedThreadId}");
    }
}

実行結果

参考(コルーチン)

呼び出し先で遅延させる方法

コルーチンは、メインスレッドで実行されるため、呼び出し先でUnityオブジェクトにアクセスすることができます。

コード

using System.Collections;
using System.Threading;
using UnityEngine;

public class LandaSample : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        ShowLog("1. Start 開始 ");

        StartCoroutine(A());

        ShowLog("3. Start 終了 ");
    }

    IEnumerator A()
    {
        ShowLog("2. コルーチン 開始 ");

        yield return new WaitForSeconds(3);

        ShowLog("4. 移動処理");

        transform.position = new Vector3(1, 0, 0);

        ShowLog("5. コルーチン 終了");
    }

    int order = 0;

    void ShowLog(string msg)
    {
        Debug.Log($"順番(確認:{++order}. {msg} スレッド番号:{Thread.CurrentThread.ManagedThreadId}");
    }
}

実行結果

C#,Unity

Posted by hidepon