C#在接受用户输入时使用计时器影响实例
我试图通过制作一个俄罗斯方块控制台应用程序来学习c#。我有一个gameBoard类(“代码中的gb”)和一个block类(“代码中的bl”)。下面的代码是迄今为止我用来左右移动一个区块的代码,但我无法在接受用户输入的同时思考如何使区块下降C#在接受用户输入时使用计时器影响实例,c#,events,timer,delegates,C#,Events,Timer,Delegates,我试图通过制作一个俄罗斯方块控制台应用程序来学习c#。我有一个gameBoard类(“代码中的gb”)和一个block类(“代码中的bl”)。下面的代码是迄今为止我用来左右移动一个区块的代码,但我无法在接受用户输入的同时思考如何使区块下降 while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Escape) { switch (keyInfo.Key) {
while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Escape)
{
switch (keyInfo.Key)
{
case ConsoleKey.LeftArrow:
currCol = bl.getCol();
if (currCol - 1 >= 0)
{
gb.removeBlock(bl.getCol(), bl.getRow());
bl.setCol(currCol - 1);
gb.putBlock(bl.getCol(), bl.getRow());
Console.Clear();
Console.WriteLine(gb.makeGrid());
}
break;
case ConsoleKey.RightArrow:
currCol = bl.getCol();
if (currCol + 1 <= 9)
{
gb.removeBlock(bl.getCol(), bl.getRow());
bl.setCol(currCol + 1);
gb.putBlock(bl.getCol(), bl.getRow());
Console.Clear();
Console.WriteLine(gb.makeGrid());
}
break;
}
}
计时器是正确的选择,还是我应该使用其他东西?如果计时器是我应该使用的,我应该从哪里开始学习如何使用它们
谢谢 hmmm这似乎是一个试图在UI线程上做多件事情的问题。计时器只是解决方案的一种可能性。如果您发现计时器不够可靠,请查看
BackgroundWorker
或Task
或Thread
根据您的需要,每个计时器都有不同的控制级别
这里有一些参考资料,只是为了让您了解如何使用这三个想法
好的,现在来解释一下为什么您的代码不能按预期工作。现在,您要求从控制台输入读取一个键。这将阻止控制台的所有执行,直到它读取密钥为止。看看这个,了解一下为什么会发生这种情况
不过,您也可以通过其他方法来实现这一点,而无需使用ReadKey()
查看此网站,了解如何设置低级键盘挂钩。此方法允许您在不阻塞控制台的情况下读取密钥。然而,这并不意味着它会阻止钥匙进入控制台。因此,在设计挂钩时,请记住这一点。我希望这对正在发生的事情有所帮助
另外,为了获得更多信息,请看一下这篇文章,它将为您提供有关修改ReadKey()
行为的其他方法的更多信息
因此,为了防止有关低级键盘挂钩的网站被删除,这里显示的代码如下:
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
看起来最简单的方法是定时器(在更复杂的情况下,你也可以使用不同的线程) 要将参数传递给OnTimedEvent函数,有不同的解决方案 1-您可以在类中使用计时器,而OnTimedEvent是类的函数,因此您可以轻松使用类字段
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace ConsoleApplication1
{
class Program
{
public static void Main()
{
GameManager gameManager = new GameManager();
gameManager.StartGame();
}
public class GameManager
{
System.Timers.Timer aTimer;
int Parameter
{
get;
set;
}
public GameManager()
{
}
public void StartGame()
{
aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 5 seconds.
aTimer.Interval = 1000;
aTimer.Enabled = true;
Console.WriteLine("Press \'q\' to quit the sample.");
Parameter = 200;
while (Console.Read() != 'q')
{
Parameter =+ 10;
}
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
Parameter++;
Console.WriteLine("Hello World!" + Parameter.ToString());
}
}
}
}
2-使用委托
public static void Main()
{
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed += delegate(object source, ElapsedEventArgs e) {
OnTimedEvent(source, e, "Say Hello");
};
// Set the Interval to 5 seconds.
aTimer.Interval = 1000;
aTimer.Enabled = true;
Console.WriteLine("Press \'q\' to quit the sample.");
while (Console.Read() != 'q') ;
}
// Specify what you want to happen when the Elapsed event is raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e, string parameter)
{
Console.WriteLine("parameter");
}
3-您也可以使用静态全局变量试试这个简单的例子,看看当您点击左箭头和右箭头,然后点击escape时会发生什么:
class Program
{
static void Main(string[] args)
{
const int delay = 100;
DateTime nextMove = DateTime.Now.AddMilliseconds(delay);
bool quit = false;
bool gameOver = false;
while (!quit && !gameOver)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true); // read key without displaying it
switch (key.Key)
{
case ConsoleKey.LeftArrow:
Console.Write("L");
break;
case ConsoleKey.RightArrow:
Console.Write("R");
break;
case ConsoleKey.Escape:
quit = true;
break;
}
}
if (!quit && !gameOver && DateTime.Now > nextMove)
{
// ... move the pieces ...
Console.Write(".");
nextMove = DateTime.Now.AddMilliseconds(delay);
}
System.Threading.Thread.Sleep(50);
}
}
}
我一定会研究低级别的键挂钩。这是否会阻止我的应用程序将按键加载到缓冲区中,并在我放开一个键后执行它们?嗯,让我为您研究一下,我相信每次按下一个键时,都会执行。因此,如果每次键盘向计算机重新发送某个键时都按住该键,它将执行。解决这个问题的一种方法是添加一个标志,在一两秒钟后重置。我需要更多地了解委托和事件。我不明白它们是如何工作得足够好,以便在我制作的类中使用它们的,但它们似乎非常有用。谢谢!这比我想做的要简单得多。另外,我不必改变我的类来实现它!
class Program
{
static void Main(string[] args)
{
const int delay = 100;
DateTime nextMove = DateTime.Now.AddMilliseconds(delay);
bool quit = false;
bool gameOver = false;
while (!quit && !gameOver)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true); // read key without displaying it
switch (key.Key)
{
case ConsoleKey.LeftArrow:
Console.Write("L");
break;
case ConsoleKey.RightArrow:
Console.Write("R");
break;
case ConsoleKey.Escape:
quit = true;
break;
}
}
if (!quit && !gameOver && DateTime.Now > nextMove)
{
// ... move the pieces ...
Console.Write(".");
nextMove = DateTime.Now.AddMilliseconds(delay);
}
System.Threading.Thread.Sleep(50);
}
}
}