Reputation: 77
I am making a little program where two rectangles drive around a race car track. When I run the program everything goes as planned and I can move the rectangles around the track using the arrow keys for one and A, S, D, W for the other. The problem is that I if I am moving one with the arrow keys and I try to press D to move the other rectangle to the right at the same time, the one moving with the arrow keys stops. The goal is to have them be able to move concurrently. What should I do?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Race_Game
{
public partial class Form1 : Form
{
private int x1 = 24;
private int y1 = 16;
private int size1 = 115;
private int size2 = 50;
private Rectangle _rect1;
private int x2 = 24;
private int y2 = 74;
private int size3 = 115;
private int size4 = 50;
private Rectangle _rect2;
public Form1()
{
InitializeComponent();
}
private void pictureBox1_Paint_1(object sender, PaintEventArgs e)
{
_rect1 = new Rectangle(x1, y1, size1, size2);
e.Graphics.FillRectangle(Brushes.Red, _rect1);
_rect2 = new Rectangle(x2, y2, size3, size4);
e.Graphics.FillRectangle(Brushes.Black, _rect2);
}
private void pictureBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
this.KeyPreview = true;
this.KeyDown += new KeyEventHandler(Form1_KeyDown);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Right)
{
x1 += 15;
}
if (e.KeyData == Keys.Left)
{
x1 -= 15;
}
if (e.KeyData == Keys.Up)
{
y1 -= 15;
}
if (e.KeyData == Keys.Down)
{
y1 += 15;
}
if (e.KeyData == Keys.D)
{
x2 += 15;
}
if (e.KeyData == Keys.A)
{
x2 -= 15;
}
if (e.KeyData == Keys.W)
{
y2 -= 15;
}
if (e.KeyData == Keys.S)
{
y2 += 15;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Invalidate();
}
}
}
Visual Studio Generated Design Code:
namespace Race_Game
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.pictureBox1 = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Interval = 1;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// pictureBox1
//
this.pictureBox1.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pictureBox1.BackgroundImage")));
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(1944, 1066);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
this.pictureBox1.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureBox1_Paint_1);
this.pictureBox1.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.pictureBox1_PreviewKeyDown);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1916, 1053);
this.Controls.Add(this.pictureBox1);
this.Name = "Form1";
this.Text = "Form1";
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
this.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureBox1_Paint_1);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.PictureBox pictureBox1;
}
}
Upvotes: 3
Views: 563
Reputation: 29244
What I decided to do is on key-down to assign a velocity, and on key-up to zero the velocity. That and to combine the x and y coordinates into Point
and Size
objects.
You can do move the boxes independently and continuously.
public partial class Form1 : Form
{
const int velocity = 15;
Point position_A = new Point(24, 16);
Point position_B = new Point(24, 74);
Size size_A = new Size(115, 50);
Size size_B = new Size(115, 50);
Size velocity_A = new Size(0, 0);
Size velocity_B = new Size(0, 0);
public Rectangle Shape_A
{
get
{
return new Rectangle(position_A, size_A);
}
}
public Rectangle Shape_B
{
get
{
return new Rectangle(position_B, size_B);
}
}
public Form1()
{
InitializeComponent();
}
private void pictureBox1_Resize(object sender, EventArgs e)
{
pictureBox1.Refresh();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.Red, Shape_A);
e.Graphics.FillRectangle(Brushes.Black, Shape_B);
}
private void timer1_Tick(object sender, EventArgs e)
{
this.position_A+=velocity_A;
this.position_B+=velocity_B;
pictureBox1.Refresh();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
Debug.WriteLine($"KeyDown Code:{e.KeyCode}");
switch (e.KeyCode)
{
case Keys.Up:
this.velocity_A=new Size(velocity_A.Width, -velocity);
break;
case Keys.Down:
this.velocity_A=new Size(velocity_A.Width, +velocity);
break;
case Keys.Left:
this.velocity_A=new Size(-velocity, velocity_A.Height);
break;
case Keys.Right:
this.velocity_A=new Size(+velocity, velocity_A.Height);
break;
case Keys.W:
this.velocity_B=new Size(velocity_B.Width, -velocity);
break;
case Keys.S:
this.velocity_B=new Size(velocity_B.Width, +velocity);
break;
case Keys.A:
this.velocity_B=new Size(-velocity, velocity_B.Height);
break;
case Keys.D:
this.velocity_B=new Size(+velocity, velocity_B.Height);
break;
case Keys.Escape:
this.Close();
break;
}
pictureBox1.Invalidate();
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
case Keys.Down:
this.velocity_A=new Size(velocity_A.Width, 0);
break;
case Keys.Right:
case Keys.Left:
this.velocity_A=new Size(0, velocity_A.Height);
break;
case Keys.W:
case Keys.S:
this.velocity_B=new Size(velocity_B.Width, 0);
break;
case Keys.A:
case Keys.D:
this.velocity_B=new Size(0, velocity_B.Height);
break;
}
}
}
Upvotes: 3
Reputation: 28499
Instead of reacting to keypress events, use a timer that polls the state of the individual keys regularly via the WinAPI function GetKeyState
.
For the following example setup, I use a form with two NumericUpDown controls ("numericUpDownA" and "numericUpDownLeft") and a Timer "timerCheckKeyboard" with interval 100ms and set to enabled. The Timer has the OnClick event handler "timerCheckKeyboard_Tick".
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("USER32.dll")]
static extern short GetKeyState(int nVirtKey);
private const int KEY_PRESSED = 0x8000;
private const int VK_W = (int)'W';
private const int VK_A = (int)'A';
private const int VK_S = (int)'S';
private const int VK_D = (int)'D';
private const int VK_LEFT = 0x25;
private const int VK_UP = 0x26;
private const int VK_RIGHT = 0x27;
private const int VK_DOWN = 0x28;
private bool IsKeyPressed(int key)
{
return (GetKeyState(key) & KEY_PRESSED) != 0;
}
private void timerCheckKeyboard_Tick(object sender, EventArgs e)
{
if (IsKeyPressed(VK_A))
{
numericUpDownA.Value++;
}
if (IsKeyPressed(VK_LEFT))
{
numericUpDownLeft.Value++;
}
}
}
The value in the NumericUpDownA control increases while the A key is pressed. The value in the NumericUpDownLeft control increases while the Cursor Left key is pressed. You can press "A" and "Cursor left" at the same time and the values in both NumericUpDown controls increase.
Note that this will even work when the keys are pressed while your application is not the active one. So, you might want to test that first in the Timer-Tick-Event or disable the timer altogether while the form doesn't have the focus. (Events Activate
, Deactivate
of the form).
Upvotes: 1