我试图统一创建一个消息框类,并且希望它与Windows窗体中的消息框以相同的方式工作,该窗口等待一个按钮被按下,然后在此之后执行代码。

        var mbox = MessageBox.Show("Test", "test", MessageBoxButtons.YesNo);
        var test = mbox == DialogResult.Cancel; <- it will wait here


我尝试用两种方式重新创建

正在加入2个线程

    public void TestClick()
    {
        Thread thread1 = new Thread(TestMethod);
        thread1.Start();
        thread1.Join();
        Debug.Log("Done");
    }

    private void TestMethod()
    {
        float time = 0;
        while (time <= 20)
        {
            Thread.Sleep(100);
            time++;
            Debug.Log("Im doing heavy work");
        }
    }


该线程阻塞了主线程,并且仅在TestMethod完成后才会恢复,但我不希望那样,因为在此期间用户将无法与消息框进行交互。

异步方式

    public delegate int AsyncTask();

    public void TestClick()
    {
        RunAsyncAndWait();
        Debug.Log("Done");
    }

    public int Method1()
    {
        float time = 0;
        while (time <= 20)
        {
            Thread.Sleep(100);
            time++;
            Debug.Log("Im doing heavy work");
        }
        return 0;
    }


    public void RunAsyncAndWait()
    {
        AsyncTask ac1 = Method1;

        WaitHandle[] waits = new WaitHandle[1];
        IAsyncResult r1 = ac1.BeginInvoke(null, null);
        waits[0] = r1.AsyncWaitHandle;

        WaitHandle.WaitAll(waits);

    }


它的工作原理与第一个完全相同,但是如果将诸如WaitHandle[]的大小更改为WaitHandle[] waits = new WaitHandle[2];的内容,它的行为将很奇怪。

现在,它的工作方式更像我需要的,因为它不断在控制台中编写内容,而不是像以前的方法那样一次只发布21条消息,但是它运行的那一刻暂停了统一场景(我可以手动恢复它,并且程序将只运行很好),并继续在控制台中打印内容,并且出现此错误


  ArgumentNullException:空句柄
  参数名称:waitHandles
  System.Threading.WaitHandle.CheckArray(System.Threading.WaitHandle []句柄,布尔值waitAll)(位于/Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Threading/WaitHandle.cs:77)
  System.Threading.WaitHandle.WaitAll(System.Threading.WaitHandle [] waitHandles)(位于/Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Threading/WaitHandle.cs:109)
  Assets.Scripts.Test.RunAsyncAndWait()(在Assets / Scripts / Test.cs:40)
  Assets.Scripts.Test.TestClick()(在Assets / Scripts / Test.cs:16处)
  UnityEngine.Events.InvokableCall.Invoke(System.Object [] args)(在C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:153)
  UnityEngine.Events.InvokableCallList.Invoke(System.Object []参数)(在C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:630)
  UnityEngine.Events.UnityEventBase.Invoke(System.Object []参数)(在C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:765)
  UnityEngine.Events.UnityEvent.Invoke()(在C:/buildslave/unity/build/Runtime/Export/UnityEvent_0.cs:53)
  UnityEngine.UI.Button.Press()(在C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:35)
  UnityEngine.UI.Button.OnPointerClick(UnityEngine.EventSystems.PointerEventData eventData)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:44)
  UnityEngine.EventSystems.ExecuteEvents.Execute(IPointerClickHandler处理程序,UnityEngine.EventSystems.BaseEventData eventData)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:52)
  UnityEngine.EventSystems.ExecuteEvents.Execute [IPointerClickHandler](UnityEngine.GameObject目标,UnityEngine.EventSystems.BaseEventData eventData,UnityEngine.EventSystems.EventFunction`1函数)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI) /EventSystem/ExecuteEvents.cs:269)
  UnityEngine.EventSystems.EventSystem:Update()


第一行听起来像我在这里可能需要一个回调函数,所以我快速添加了一些东西来进行测试

IAsyncResult r1 = ac1.BeginInvoke(ar => Debug.Log("Done"), null);


但没有运气,没有任何改变。

有什么技巧可以解决整个问题(制作一个消息框,阻塞线程直到按下按钮),或者有关Microsoft如何在Windows窗体中实现该问题的更多信息?

最佳答案

WinForms和Unity之间有很大的区别。在WinForms中,您有一个UI线程,可能被模式窗体阻止。在Unity中,您有多个具有多种方法的对象,其中脚本的执行顺序和某些引擎机制决定了如何在每个帧中执行它们。

但是,如果要在Unity中使用模式消息框,则可以通过向其添加布尔检查或禁用脚本来简单地阻止执行特定脚本的Update或FixedUpdate。第一种方法提供了更多选择,但第二种方法则更容易。但是请注意,禁用脚本会停止脚本中除Invoke和Coroutine之外的所有内容。

您可以通过在对象上放置一个简单的SpriteRenderer或Image来阻止用户与它们的交互。此蒙版的透明度可以为零,应为全屏尺寸,并且必须启用Raycast Target

我希望有一个带有全屏蒙版的消息框,它后面有一个带有alpha = .1的简单黑色精灵

public GameObject ModalMessageBox;//game object of message box with a mask

public void TestClick()
{
    StartCoroutine(TestMethod);
    ModalMessageBox.setActive(true);
}

IEnumerator TestMethod()
{
    float time = 0;
    while (time <= 20)
    {
        yield return new WaitForSeconds(.1f);
        time++;
        Debug.Log("Im doing heavy work");
    }
    ModalMessageBox.setActive(false);
}

void Update()
{
    if(ModalMessageBox.activeSelf)
    {
        //handle message box
    }
    else
    {
        //handle normal update stuff
    }
}


请注意,所有其他脚本仍将运行。如果还必须阻止其他脚本的执行,则需要一个接一个地执行。

注意:

由于禁用脚本不会停止其启动的协程,因此您最好禁用脚本本身

public Script1 script1;
public Script2 script2;
public Script3 script3;

void BlockScripts(bool block)
{
    //for singleton scripts:
    Script1.Instance.enabled = !block;
    Script2.Instance.enabled = !block;
    Script3.Instance.enabled = !block;
    //for referenced scripts:
    script1.enabled = !block;
    script2.enabled = !block;
    script3.enabled = !block;

    //self
    enabled = !block;
}

public void TestClick()
{
    StartCoroutine(TestMethod);
    ModalMessageBox.setActive(true);

    BlockScripts(true);
}

IEnumerator TestMethod()
{
    float time = 0;
    while (time <= 20)
    {
        yield return new WaitForSeconds(.1f);
        time++;
        Debug.Log("Im doing heavy work");
    }

    ModalMessageBox.setActive(false);

    BlockScripts(false);
}

void Update()
{
}


其中Script1,2,3是单例类,而script1,2,3是您要阻止的脚本的引用。

08-06 11:25