繁体   English   中英

C#在接受用户输入的同时使用计时器影响实例

[英]C# Using a Timer to Affect Instances While Accepting User Input

我正在尝试通过创建tetris控制台应用程序来学习c#。 我有一个gameBoard类(在代码中为“ gb”)和一个块类(在代码中为“ bl”。)到目前为止,下面的代码是我可以左右移动一个块的方法,但是我无法包装我的代码我正在接受用户输入的同时,探讨如何使障碍物掉落。

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;
            }
        }

我假设计时器可能是执行此操作的方法,但是我不知道如何将实例传递给ElapsedEventHandler的OnTimedEvent函数

public static void Main()
 {
     System.Timers.Timer aTimer = new System.Timers.Timer();
     aTimer.Elapsed+=new ElapsedEventHandler(OnTimedEvent);
     // Set the Interval to 5 seconds.
     aTimer.Interval=5000;
     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)
 {
     Console.WriteLine("Hello World!");
 }

计时器是走的路,还是我应该使用其他东西? 如果应该使用计时器,应该从哪里开始学习如何使用计时器?

谢谢!

嗯,这似乎是在UI线程上尝试做多件事的问题。 计时器只是解决方案的一种可能。 如果发现计时器不够强大,请根据需要使用BackgroundWorkerTaskThread进行不同级别的控制。

这里有一些参考BackgroundWorkerThreadTask-Parallel-Library ,因此您对如何使用这三个想法有一些了解。

好的,现在就对为什么您的代码无法按预期工作进行解释。 现在,您要从控制台输入中读取一个键。 这将阻止所有执行到控制台,直到它读取密钥为止。 看看这是为什么发生这种情况的解释, ReadKey()

但是,还有其他方法可以不用使用ReadKey()来执行此操作,请访问此网站来设置低级键盘挂钩Low-Level Key Hook 该方法将允许您在不对控制台进行任何阻止的情况下读取键。 但是,这并不意味着它将阻止密钥进入控制台。 因此,在设计挂钩时要牢记这一点。 我希望这对正在发生的事情有所帮助。

同样,只是为了获得更多信息,请查看此Console.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-您也可以使用静态全局变量

试试这个简单的例子,看看当您按下向左和向右箭头,然后转义时会发生什么:

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);
        }

    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM