【unity】 Loom实现多线程


通常情况下,unity中在子线程中改变变量的值,但是子线程尚未结束时,主线程无法使用该变量。

因此使用Loom作为中介,子线程调用并传值给Loom,Loom调用主线程的API。

实现步骤
创建Loom空物体,并挂载Loom脚本

//Loom.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class Loom : MonoBehaviour
{
    static bool isInitialized;

    private static Loom instance;
    public static Loom Instance
    {
        get
        {
            Initialize();
            return instance;
        }
    }

    public static void Initialize()
    {
        if (!isInitialized)
        {
            if (!Application.isPlaying)
                return;
            isInitialized = true;
            var obj = new GameObject("Loom");
            instance = obj.AddComponent<Loom>();

            DontDestroyOnLoad(obj);
        }
    }

    private void Awake()
    {
        instance = this;
        isInitialized = true;
    }

    struct NoDelayQueueItem
    {
        public Action<int> action;
        public int param;
    }

    List<NoDelayQueueItem> listNoDelayActions = new List<NoDelayQueueItem>();

    struct DelayQueueItem
    {
        public Action<int> action;
        public int param;
        public float time;
    }

    List<DelayQueueItem> listDelayedActions = new List<DelayQueueItem>();

    public static void QueueOnMainThread(Action<int> taction, int param)
    {
        QueueOnMainThread(taction, param, 0f);
    }

    public static void QueueOnMainThread(Action<int> action, int param, float time)
    {
        if (time != 0)
        {
            lock (Instance.listDelayedActions)
            {
                Instance.listDelayedActions.Add(new DelayQueueItem { time = Time.time + time, action = action, param = param });
            }
        }
        else
        {
            lock (Instance.listNoDelayActions)
            {
                Instance.listNoDelayActions.Add(new NoDelayQueueItem { action = action, param = param });
            }
        }
    }

    List<NoDelayQueueItem> currentActions = new List<NoDelayQueueItem>();
    List<DelayQueueItem> currentDelayed = new List<DelayQueueItem>();

    private void Update()
    {
        //无延迟的执行队列中存在任务
        if (listNoDelayActions.Count > 0)
        {
            lock (listNoDelayActions)
            {
                //把所有任务放入当前执行队列
                currentActions.Clear();
                currentActions.AddRange(listNoDelayActions);
                listNoDelayActions.Clear();
            }
            //挨个执行完任务
            for (int i = 0; i < currentActions.Count; i++)
            {
                currentActions[i].action(currentActions[i].param);
            }
        }
        //有延迟的执行队列中存在任务
        if (listDelayedActions.Count > 0)
        {
            lock (listDelayedActions)
            {
                //将此刻之前的所有未完成的任务放入当前执行队列
                currentDelayed.Clear();
                currentDelayed.AddRange(listDelayedActions.Where(d => Time.time >= d.time));
                for (int i = 0; i < currentDelayed.Count; i++)
                {
                    listDelayedActions.Remove(currentDelayed[i]);
                }
            }

            for (int i = 0; i < currentDelayed.Count; i++)
            {
                currentDelayed[i].action(currentDelayed[i].param);
            }
        }
    }
    private void OnDisable()
    {
        if (instance == this)
        {
            instance = null;
        }
    }
}

子线程中调用Loom类,Loom中执行主线程API

private void Awake()
{
    button.GetComponent<UnityEngine.UI.Button>().onClick.AddListener(OnPointClick);
    onPositionChangedEvent += onPositionChanged;
    onPositionChanged();
}

public UnityEngine.UI.Button button;

public Action onPositionChangedEvent;

//子线程会改变此变量,此变量发生变化时,会执行委托(主线程API)
private int _position = 10;
public int Position
{
    get { return _position; }
    set
    {
        _position = value;
        if (onPositionChangedEvent != null)
        {
            onPositionChangedEvent();
        }
    }
}

Thread thread = null;
int direct = 1;
// button开启子线程,此线程一直开启
public void OnPointClick()
{
    if (thread == null)
    {
        thread = new Thread(() =>
        {
            while (true)
            {
                if (Position < -100)
                {
                    direct = 1;
                }
                else if (Position > 100)
                {
                    direct = -1;
                }
				//子线程更改的变量,此变量每次发生改变时需要在主线程中使用
                Position += 1 * direct;

                Thread.Sleep(100);
            }
        });
        thread.IsBackground = true;
        thread.Start();
    }

}

//Position发生变化时,就会调用该方法
private void onPositionChanged()
{
    //Loom会在子线程执行,但是可以调用主线程API
    Loom.QueueOnMainThread((Position) =>
    {
        transform.position = new Vector3(Position, 0, 0);
    }, Position);
}

posted @ 2024-10-23 09:35  Sitar  阅读(31)  评论(0编辑  收藏  举报