C# 俄罗斯方块克隆-Can';t使块正确响应箭头键组合

C# 俄罗斯方块克隆-Can';t使块正确响应箭头键组合,c#,keydown,arrow-keys,C#,Keydown,Arrow Keys,我正在用Visual C#2005编写一个俄罗斯方块游戏。这是我设计的最广泛的程序 我创建了一个shape类和一个block类来控制不同俄罗斯方块片段的位置、移动和显示。我为每个形状提供了moveDown()、moveLeft()和moveRight()函数(以及相应的canMoveDown()、canMoveLeft()、canMoveRight()布尔函数,用于验证是否可以移动)。这一切都运转得很好 我想使用向下、向右和向左箭头键让用户移动块,除了使用计时器让形状每隔这么多毫秒自动下降一行之

我正在用Visual C#2005编写一个俄罗斯方块游戏。这是我设计的最广泛的程序

我创建了一个shape类和一个block类来控制不同俄罗斯方块片段的位置、移动和显示。我为每个形状提供了moveDown()、moveLeft()和moveRight()函数(以及相应的canMoveDown()、canMoveLeft()、canMoveRight()布尔函数,用于验证是否可以移动)。这一切都运转得很好

我想使用向下、向右和向左箭头键让用户移动块,除了使用计时器让形状每隔这么多毫秒自动下降一行之外

我使用KeyDown事件处理程序来检查用户何时按下down、left和right箭头键。这并不难。问题是,我想允许对角线运动,我希望它尽可能平滑地工作。我尝试了许多不同的方法来解决这个问题,取得了不同程度的成功。但我不能完全正确

我最成功的方法是使用三个布尔变量来跟踪按下、向左和向右箭头键的时间。我会在KeyDown事件中将布尔值设置为true,在keydup事件中将布尔值设置为false。在KeyDown事件中,我还将告诉块如何移动,使用布尔变量检查当前按下的组合。除了一件事,它工作得非常好

如果我按下其中一个箭头键并按住,然后按下第二个箭头键,然后释放第二个键,则块将完全停止移动,而不是继续向尚未释放的第一个箭头键的方向移动。我认为这是因为第二个键触发了KeyDown事件,在其释放时,KeyUp事件被触发,而KeyDown事件完全停止了触发,即使第一个键被触发

我一辈子都找不到解决这个问题的令人满意的办法


任何帮助都将不胜感激=)

对不起,没有具体的帮助。。。毕竟是周六晚上。。。然而:


每个键都需要一个状态。当您得到一个keydown事件时,您可以知道哪个键被按下,并修改您正在维护的keystate。当钥匙再次打开时,通过查看事件,您可以分辨出它是哪一个。仅修改触发事件的密钥的状态:即,不要使用keyup事件重置所有状态,否则内存中保存的密钥的状态将损坏,如您所见。

如果您依靠key repeat反复发送key down事件以使块移动,我认为这不是你想要的方式。块的移动应始终独立于按键重复。因此,在关键事件期间不应移动块。您应该只在keydown和keyup事件期间跟踪关键点的状态,并在其他位置处理移动。实际的移动应该发生在某种计时器事件中(计时器控件定期触发事件,即使没有发生任何事情),或者应该有一个主循环,在适当的时候不断检查所有事物和移动对象的状态。如果您使用第二个选项,则需要查看“DoEvents”,因为如果您的代码一直在运行,而没有完成函数,则程序将不会处理任何其他事件,例如keyup和keydown事件。因此,您可能希望在每个循环中调用DoEvents来处理关键事件(其中包括移动窗口)。如果不需要经常处理事情,您可能还需要调用System.Threading.Thread.Sleep。如果您使用计时器控件,您不必担心任何问题。

大多数游戏不等待事件。他们在必要时轮询输入设备,并随机应变。事实上,如果你看过XNA,你会发现有一个Keyboard.GetState()方法(或Gamepad.GetState()),你会在更新例程中调用它,并根据结果更新游戏逻辑。在使用Windows.Forms时,没有现成的方法可以做到这一点,但是您可以P/调用GetKeyBoardState()函数来利用这一点。这样做的好处是,您可以一次轮询多个按键,因此您可以一次对多个按键做出反应。下面是我在网上找到的一个简单的类,有助于:

为了演示,我编写了一个简单的windows应用程序,它基本上基于键盘输入来移动球。它使用我链接到的类来轮询键盘的状态。您会注意到,如果一次按住两个键,它将沿对角线移动

首先,Ball.cs:

    public class Ball
    {
        private Brush brush;

        public float X { get; set; }
        public float Y { get; set; }
        public float DX { get; set; }
        public float DY { get; set; }
        public Color Color { get; set; }
        public float Size { get; set; }

        public void Draw(Graphics g)
        {
            if (this.brush == null)
            {
                this.brush = new SolidBrush(this.Color);
            }
            g.FillEllipse(this.brush, X, Y, Size, Size);
        }

        public void MoveRight()
        {
            this.X += DX;
        }

        public void MoveLeft()
        {
            this.X -= this.DX;
        }

        public void MoveUp()
        {
            this.Y -= this.DY;
        }

        public void MoveDown()
        {
            this.Y += this.DY;
        }
    }
真的一点也不奇怪

下面是Form1代码:

    public partial class Form1 : Form
    {
        private Ball ball;
        private Timer timer;
        public Form1()
        {
            InitializeComponent();
            this.ball = new Ball
            {
                X = 10f,
                Y = 10f,
                DX = 2f,
                DY = 2f,
                Color = Color.Red,
                Size = 10f
            };
            this.timer = new Timer();
            timer.Interval = 20;
            timer.Tick += new EventHandler(timer_Tick);
            timer.Start();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            var left = KeyboardInfo.GetKeyState(Keys.Left);
            var right = KeyboardInfo.GetKeyState(Keys.Right);
            var up = KeyboardInfo.GetKeyState(Keys.Up);
            var down = KeyboardInfo.GetKeyState(Keys.Down);

            if (left.IsPressed)
            {
                ball.MoveLeft();
                this.Invalidate();
            }

            if (right.IsPressed)
            {
                ball.MoveRight();
                this.Invalidate();
            }

            if (up.IsPressed)
            {
                ball.MoveUp();
                this.Invalidate();
            }

            if (down.IsPressed)
            {
                ball.MoveDown();
                this.Invalidate();
            }


        }


        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (this.ball != null)
            {
                this.ball.Draw(e.Graphics);
            }
        }
    }

简单的小应用程序。只需创建一个球和一个计时器。每隔20毫秒,它会检查键盘状态,如果按下某个键,它会移动该键并使其无效,以便可以重新绘制。

嘿,非常感谢,我在链接中试用了您的代码和类,效果非常好。我必须在编译之前对它进行大量修改,但我认为这主要是因为我使用的是VisualStudio2005,而不是2008。我一直在网上搜索,却找不到任何像这样好用的东西。我希望它也能帮助其他人。哦,那是我的错。你提到你在VS2005工作,我没注意到。是的,我非常习惯使用自动属性,以及对象初始值设定项和“var”关键字。很抱歉很高兴它对你有用。你应该看看XNA的游戏,它有很多非常棒的东西。