winforms 如何从TextBox获取输入并在Windows窗体应用程序的游戏循环中使用该输入?

ttygqcqt  于 2023-02-05  发布在  Windows
关注(0)|答案(1)|浏览(176)

我正在做一个基于文本的冒险游戏。在游戏中,有一个列表框显示文本,文本框用户输入命令:

我把游戏循环放在Game_Load方法中,这会产生一个问题,因为它一直在检查TextBox内容,而且由于在用户有机会键入任何内容之前它是空的,所以它返回null并崩溃。
我已经做了一个类似的游戏作为一个控制台应用程序之前与此代码:

while(run) 
{ 
    Console.WriteLine("What is your next step?");
    var input = Console.ReadLine().ToLower();

    if (input == Text.Language.Quit)
        run = false;
    else
        Actions.Instance.Execute(input.Split(" "));
}

它运行得很好,现在我很难把它转换成Windows窗体,我不知道该把哪个方法放在哪里来避免这个问题。
如何使游戏在需要之前不要求输入?
我试着创建一个GetText方法,并调用它来代替var input = ......但也不起作用。我试着将while(run)从Form_Load中移出,但它并不总是像它应该的那样运行。

a2mppw5e

a2mppw5e1#

据我所知,您想要的东西,外观和行为像一个控制台,但在WinForms应用程序,这是一个风格的选择。* 否则有更好的方式来提示用户输入WinForms!*
运行一个类似于控制台应用程序的“游戏循环”是可能的,但需要特别小心。我们说WinForms是“事件驱动”的,因为应用程序有一个消息循环来监听鼠标点击和按键等事件。但这意味着,例如,这个循环将陷入等待按键的状态,因为它“阻塞了UI线程”来“检测”这些按键。

void BadGameLoop()
{
    while(run)  // Don't do this!
    { 
        // The `Form` becomes unresponsive i.e.
        // The app "freezes" and will have to be killed.
        string input = ReadLine();
        switch(input)
        {
            // Do something
        }
    }
}

另一方面,在async方法中使用的await关键字将导致该方法立即返回,但当“发生某些事情”时,会在此处继续:

async Task GoodGameLoop()
{
    while(run) 
    { 
        string input = await ReadLineAsync();
        switch(input)
        {
            // Do something
        }
    }
}

异步读取命令

在调用ReadLineAsync()时阻止。

SemaphoreSlim awaiter = new SemaphoreSlim(1, 1);
    private async Task<string> ReadLineAsync()
    {
        int charIndex = Console.GetFirstCharIndexOfCurrentLine();
        int line = Console.GetLineFromCharIndex(charIndex);
        string textB4 = Console.Lines[line];
        // Instruct the semaphore to block until further notice.
        awaiter.Wait(0);
        // Return from this method immediately.
        await awaiter.WaitAsync();
        // Resume here when [Enter] key unblocks the semaphore.
        string input = 
            string.IsNullOrWhiteSpace(textB4) ?
            Console.Lines[line] :
            Console.Lines[line].Replace(textB4, string.Empty);
        return input;
    }

按下[Enter]键时,解锁信号量。

private void onConsoleKeyDown(object? sender, KeyEventArgs e)
    {
        if (e.KeyData == Keys.Enter)
        {
            // Call Wait(0) so there's something to release
            // in case the awaiter isn't currently awaiting!
            try { awaiter.Wait(0); }
            finally{ awaiter.Release(); }
        }
    }

异步游戏循环示例

下面是我用来测试这个答案的代码:

enum GameState
{
    DisplayLogo,
    PromptName,
    Intro,
    Play,
}
public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
        Console.KeyDown += onConsoleKeyDown;
        _ = execGameLoop();
    }
    private GameState GameState = GameState.DisplayLogo;
    private async Task execGameLoop()
    {
        while (true)
        {
            switch (GameState)
            {
                case GameState.DisplayLogo:
                    Console.Font = new Font("Consolas", 10);
                    Console.Text =
@"
                           ---------------
            ---------------               0
____________               ---------------
0           ---------------               |
------------                                |
|               WELCOME TO GORK               |
 |                              ---------------
  |             ---------------                0
   |____________                 ---------------
   0             ---------------
    ------------
";
                    Console.Select(Console.Text.Length, 0);
                    await Task.Delay(TimeSpan.FromSeconds(1));
                    GameState= GameState.PromptName;
                    continue;
                case GameState.PromptName:
                    Console.AppendText(@"
What is your name hero? ");
                    break;
                case GameState.Intro:
                    Console.Clear();
                    Console.AppendText(@"
OK let's PLAY!

Here are the rules:
#1 Don't cheat, unless winning requires it.
#2 Never forget rule #1.

For a list of commands, type 'help'.
Press Enter to continue.
"
                    );
                    break;
                case GameState.Play:
                    Console.Clear();
                    Console.AppendText(@"
Enter command: "
                        );
                    break;
            }
            string input = await ReadLineAsync();
            if(string.IsNullOrWhiteSpace(input))
            {
                if(GameState.Equals(GameState.Intro))
                {
                    GameState = GameState.Play;
                }
            }
            else
            {
                switch (GameState)
                {
                    case GameState.PromptName:
                        Console.AppendText($"Welcome {input} to this adventure!" + Environment.NewLine);
                        for (int i = 0; i < 50; i++)
                        {
                            Console.AppendText(">");
                            Task.Delay(TimeSpan.FromMilliseconds(10)).Wait();
                        }
                        GameState= GameState.Intro;
                        break;
                    case GameState.Intro:
                        GameState= GameState.Play;
                        break;
                    case GameState.Play:
                        Console.AppendText($"You entered: {input}" + Environment.NewLine);
                        await Task.Delay(TimeSpan.FromSeconds(1.5));
                        break;
                }
            }
        }
        .
        .
        .
        // ReadLineAsync method ...
        // onConsoleKeyDown method ...
    }
}

相关问题