从 Thread 更改主线程的 GameObject

我想以某种方式从异步线程更改游戏对象。我需要保留监听器/接口架构。


下面我创建了一个我当前实现的简单示例。


主脚本:


using UnityEngine;


public class MyScript : MonoBehaviour, IMyComponentListener

{

    public GameObject cube;


    private MyComponent _myComponent;


    private void Start()

    {

        _myComponent = new MyComponent(this);

    }


    public void OnThreadCompleted()

    {

        cube.transform.Rotate(Vector3.up, 10f);

    }

}

线程脚本:


using System.Threading;


public class MyComponent

{

    private readonly IMyComponentListener _listener;


    public MyComponent(IMyComponentListener listener)

    {

        _listener = listener;


        Thread myThread = new Thread(Run);


        myThread.Start();

    }


    private void Run()

    {

        Thread.Sleep(1000);


        _listener.OnThreadCompleted();

    }

}

监听接口:


public interface IMyComponentListener

{

    void OnThreadCompleted();

}

如果我尝试此代码,我将面临以下错误:


get_transform can only be called from the main thread.

Constructors and field initializers will be executed from the loading thread when loading a scene.

Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

UnityEngine.GameObject:get_transform()

MyScript:OnThreadCompleted() (at Assets/Scripts/MyScript.cs:18)

MyComponent:Run() (at Assets/Scripts/MyComponent.cs:20)

很明显我不能从异步线程更改主线程元素,但我该如何解决它或正确实现支持它的架构?


摇曳的蔷薇
浏览 158回答 2
2回答

慕森卡

这只是一个例子,因为你要求它。一般来说,UnityMainThreadDispatcher来自您的链接很好。无论如何,它只是在一种Update方法中处理队列。我不想使用这个 Singleton-Pattern 解决方案,而只是在MonoBehaviour您已有的组件中做同样的事情(Singleton 在大多数情况下只是一个快速但肮脏的解决方案)public class MyScript : MonoBehaviour, IMyComponentListener{&nbsp; &nbsp; public GameObject cube;&nbsp; &nbsp; private MyComponent _myComponent;&nbsp; &nbsp; private ConcurrentQueue<Action> callbacks = new ConcurrentQueue<Action>();&nbsp; &nbsp; private void Start()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; _myComponent = new MyComponent(this);&nbsp; &nbsp; }&nbsp; &nbsp; // Work the callbacks queue&nbsp; &nbsp; private void Update()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if(callbacks.Count == 0) return;&nbsp; &nbsp; &nbsp; &nbsp; while(callbacks.Count != 0)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Action a;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(!callbacks.TryDequeue(out a)) continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a.Invoke();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; public void ThreadCompleted()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; callbacks.Enqueue(OnThreadCompleted);&nbsp; &nbsp; }&nbsp; &nbsp; private void OnThreadCompleted()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; cube.transform.Rotate(Vector3.up, 10f);&nbsp; &nbsp; }}而不是你会打电话enqueue给那个,而不是像((MyScript)_listener).ThreadCompleted();问题可能是这里的类型转换..或者您可以将方法添加到接口。如果您有多个侦听器(似乎是由于界面),我也会使用您的方式,可能仍然没有单例,而是使用适当的引用,例如通过 Inspector。在我的智能手机上打字,所以没有保修,但我希望我能表达清楚

SMILET

我想出了一个使用外部组件UnityMainThreadDispatcher的解决方法。按照安装说明进行操作后,我已将调度程序更新为以下示例并发挥了魅力!线程脚本:using System.Threading;public class MyComponent{&nbsp; &nbsp; private readonly IMyComponentListener _listener;&nbsp; &nbsp; public MyComponent(IMyComponentListener listener)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; _listener = listener;&nbsp; &nbsp; &nbsp; &nbsp; Thread myThread = new Thread(Run);&nbsp; &nbsp; &nbsp; &nbsp; myThread.Start();&nbsp; &nbsp; }&nbsp; &nbsp; private void Run()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Thread.Sleep(1000);&nbsp; &nbsp; &nbsp; &nbsp; UnityMainThreadDispatcher.Instance().Enqueue(() => _listener.OnThreadCompleted());&nbsp; &nbsp; }}
打开App,查看更多内容
随时随地看视频慕课网APP