[英]How to take input from TextBox and use that input in game loop in a Windows Forms App?
I am working on a text based adventure game.我正在开发一款基于文本的冒险游戏。 In the game, there is a ListBox which displays text, and TextBox where user types the commands:
在游戏中,有一个显示文本的 ListBox 和用户键入命令的 TextBox:
I put the game loop in the Game_Load method which makes a problem because it keeps checking the TextBox content at all times and since it's empty before the user gets a chance to type anything it returns null and crashes.我将游戏循环放在 Game_Load 方法中,这会产生问题,因为它一直在检查 TextBox 内容,并且由于在用户有机会键入任何内容之前它是空的,它会返回 null 并崩溃。
I've made a similar game as a Console app before with this code:我之前用这段代码制作了一个与控制台应用程序类似的游戏:
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(" "));
}
And it worked fine.而且效果很好。 Now I'm struggling to convert this to Windows Forms, I don't know where to put which method to avoid this problem.
现在我正在努力将其转换为 Windows Forms,我不知道将哪种方法放在哪里可以避免这个问题。
How do I make the game not ask for input before it is needed?如何让游戏在需要输入之前不要求输入?
I tried making a GetText method and calling that instead of var input =... but that didn't work either.我尝试制作一个 GetText 方法并调用它而不是 var input =... 但这也不起作用。 I tried moving the while(run) out of the Form_Load, but then it doesn't run at all times like it should.
我尝试将 while(run) 从 Form_Load 中移出,但它并没有像它应该的那样一直运行。
As I understand it, you want something that looks and behaves like a Console, but in a WinForms app, and this is a stylistic choice.据我了解,您想要的东西看起来和行为都像控制台,但在 WinForms 应用程序中,这是一种风格选择。 Otherwise there are better ways to prompt user for input in WinForms!
否则有更好的方法提示用户在 WinForms 中输入!
Running a "game loop" similar to your console app is possible but requires special care.运行类似于控制台应用程序的“游戏循环”是可能的,但需要特别小心。 We say that WinForms is "event-driven" because the application has a message loop that listens for events like mouse clicks and key presses.
我们说 WinForms 是“事件驱动的”,因为应用程序有一个消息循环,可以侦听鼠标单击和按键等事件。 But this means, for example, that this loop is going to get stuck waiting for keypresses because it's "blocking the UI thread" that detects those keypresses.
但这意味着,例如,此循环将在等待按键时卡住,因为它“阻塞了检测这些按键的 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
}
}
}
On the other hand, the await
keyword used inside an async
method will cause the method to return immediately, but then resume at this spot when "something happens":另一方面,在
async
方法中使用的await
关键字会导致方法立即返回,但当“发生某些事情”时会在此处恢复:
async Task GoodGameLoop()
{
while(run)
{
string input = await ReadLineAsync();
switch(input)
{
// Do something
}
}
}
Read a command asynchronously异步读取命令
Block when the ReadLineAsync()
is called.在调用
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;
}
Unblock the semaphore when the [Enter] key is pressed.当按下 [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(); }
}
}
Asynchronous Game Loop Example异步游戏循环示例
Here's the code I used to test this answer:这是我用来测试这个答案的代码:
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 ...
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.