Handling multiple keys down in a multiplayer game - c#

I am coding my own game using Windows Forms Applications. It is supposed to be a multiplayer game. Every player can control its brick to keep the ball in the pitch, but the problem is that both players cannot press controls at the same time. Every time the second player presses the key during the movement of the first, the brick of the first player stops. But, if they press the keys at the same tame, both bricks move. I used the KeyDown event:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W && one.BrickLocationY > 0)
{
one.BrickLocationY -= 17;
}
if (e.KeyCode == Keys.S && one.BrickLocationY + Brick.BrickHeight < screenHeight)
{
one.BrickLocationY += 17;
}
if (e.KeyCode == Keys.Up)
{
two.BrickLocationY -= 17;
}
if (e.KeyCode == Keys.Down && two.BrickLocationY + Brick.BrickHeight < screenHeight)
{
two.BrickLocationY += 17;
}
if (e.KeyCode == Keys.Escape)
{
Application.Exit();
}
}
Ok, this allows me to move the brick up and down. If I press two keys at once, both bricks move in the wanted direction. The bricks are drawn on a paint event triggered by the timer tick whose interval is set to 1.
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawEllipse(ellipsePen, x, y, ballSize, ballSize );
e.Graphics.FillEllipse(Brushes.White ,x+1, y+1, ballSize, ballSize);
e.Graphics.FillRectangle(Brushes.White, one.BrickLocationX+1, one.BrickLocationY+1, Brick.BrickWidth, Brick.BrickHeight);
e.Graphics.FillRectangle(Brushes.White, two.BrickLocationX+1, two.BrickLocationY+1, Brick.BrickWidth, Brick.BrickHeight);
}
I also had attempts to do this with the combination of KeyUp and KeyPress but with no success. The only thing that came to my mind was threading bricks, but had no idea how to do it. I there any way I can handle multiplayer controls like this without threading?
P.S.The keyboard is capable of handling multiple buttons at once.

When the KeyDown event fires, you need to check which buttons are pressed every time. Here is what I found upon a quick search on the topic.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(Form1_FormClosing);
tm.Tick += new System.EventHandler(DoSomethingWithKeyboardInput);
this.Load += new System.EventHandler(Form1_Load);
textBox1.KeyDown += new System.Windows.Forms.KeyEventHandler(textbox1_KeyDown);
textBox1.KeyUp += new System.Windows.Forms.KeyEventHandler(textbox1_KeyDown);
}
private Timer tm = new Timer();
private List<System.Windows.Forms.Keys> MovementKeys = new List<System.Windows.Forms.Keys>();
private _MyInputKeys MyInputKeys = new _MyInputKeys();
private struct _MyInputKeys
{
public bool Jump;
public bool Left;
public bool Right;
}
private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
tm.Stop();
}
public void DoSomethingWithKeyboardInput(object sender, EventArgs e)
{
textBox1.Text = (MyInputKeys.Left ? "(left)" : "") +
(MyInputKeys.Right ? "(right)" : "") + (MyInputKeys.Jump ? "(jump)" : "");
}
private void Form1_Load(object sender, System.EventArgs e)
{
//define keys used for movement
MovementKeys.Add(Keys.Up); //Jump ?
MovementKeys.Add(Keys.Left); //Left Arrow - Move Left
MovementKeys.Add(Keys.Right); //Rigth Arrow - Move Right
tm.Interval = 50;
tm.Start();
}
private void textbox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (MovementKeys.IndexOf(e.KeyCode) != -1)
{
e.Handled = true;
MyInputKeys.Jump = IsKeyDown(Keys.Up);
MyInputKeys.Left = IsKeyDown(Keys.Left);
MyInputKeys.Right = IsKeyDown(Keys.Right);
}
}
public static bool IsKeyDown(Keys key)
{
return (GetKeyState(Convert.ToInt16(key)) & 0X80) == 0X80;
}
/// <summary>
/// If the high-order bit is 1, the key is down; otherwise, it is up.
/// If the low-order bit is 1, the key is toggled.
/// A key, such as the CAPS LOCK key, is toggled if it is turned on.
/// The key is off and untoggled if the low-order bit is 0.
/// A toggle key's indicator light (if any) on the keyboard will be on when
/// the key is toggled, and off when the key is untoggled.
/// </summary>
/// <param name="nVirtKey"></param>
[DllImport("user32.dll")]
public extern static Int16 GetKeyState(Int16 nVirtKey);
}
}

I believe you'll need to track this yourself. Basically on key down you recognize the w and consider it being held until key up releases the w. same with the s key.
Then in your run loop, you just look for what keys are active and do your direction logic there.

Related

How to make continuous movement using keydown event?

Currently, this code runs fine, and moves the picturebox, but it always moves it once, waits about a second, and then continues to move it. How do I make the movement keep going instead of stopping for a second?
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A)
{
x = pictureBox1.Location.X;
y = pictureBox1.Location.Y;
if (pictureBox1.Location.X > 0)
{
pictureBox1.Location = new Point(x - 10, y);
}
}
}
One way to achieve this outcome is to use a Timer and enable it on KeyDown event and disable it on KeyUp event. When MoveTimer.Tick occurs (in this case every 25 ms or so), handle the event by moving "once" to the left until the key is released.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
MoveTimer.Tick += (sender, e) =>
{
pictureBox1.Location = new Point(
pictureBox1.Location.X - 10,
pictureBox1.Location.Y);
};
}
Timer MoveTimer = new Timer { Interval = 25 };
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if(e.KeyData == Keys.A)
{
MoveTimer.Enabled = true;
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
MoveTimer.Enabled = false; // No need to check which key
}
}

How to register singular key press

I need to be able to press a key down and no matter how long it is held down for it only does the action once. I have tried using bools to keep track of the state but haven't had any luck.
bool downPressed = false;
int a = 0;
if (Keyboard.GetState().IsKeyDown(Keys.Down))
{
downPressed = true;
if (downPressed == true)
{
while(a == 0)
{
y += 100;
a++;
}
}
}
This is my latest attempt. The aim is for when the down key is pressed it moves the object by 100px but only once every time it is pressed.
You should add an EventHandler to your KeyDown Event of the Application/Form and just do the
y += 100;
in there.
public void OnKeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
y += 100;
}

C# KeyDown not working

I have a picturebox that I want to relocate when a key is pressed.
I'm making a game of pong. With the code below I want to make the paddle of player 1, which is a picturebox that should move up and down while a key is pressed. The following code doesn't seem to work:
public partial class Form1 : Form
{
bool upPressed, downPressed;
public Form1()
{
InitializeComponent();
}
private void PongTimer_Tick(object sender, EventArgs e)
{
if(upPressed){
paddlePlayer1.Location = new Point(paddlePlayer1.Location.X, paddlePlayer1.Location.Y + 5);
}
if (downPressed)
{
MessageBox.Show("Numlock");
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.Up){
upPressed = true;
}
if (e.KeyCode == Keys.NumLock)
{
downPressed = true;
}
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.Up){
upPressed = false;
}
}
}
I used the Numlock to see if the problem was in the picture, this isn't the case. I've put the KeyPreview propery to true but this didnt solve it either. The timer does work properly.
I hope somebody here can tell me what I'm doing wrong.
This is a code example to move a PictureBox:
Drag/drop a PictureBox on a form (in this case Form1).
Change the Background color of the PictureBox to e.g. red. (This way it is visible during runtime).
Implement the KeyDown event of Form1 (in this case Form1_KeyDown).
Run the application.
Start pressing an arrow key and the PictureBox will move.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
int x = pictureBox1.Location.X;
int y = pictureBox1.Location.Y;
if (e.KeyCode == Keys.Right) { x += 1; }
else if (e.KeyCode == Keys.Left) { x -= 1; }
else if (e.KeyCode == Keys.Up) { y -= 1; }
else if (e.KeyCode == Keys.Down) { y += 1; }
pictureBox1.Location = new Point(x, y);
}

Removing the delay after KeyDown event?

When I hold a key in my game to move my player:
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
{
Player.MoveUp();
}
}
The player instantly moves one step as soon as I press the down arrow, and then pauses for a short duration before starting to smoothly move again. Why's this? How can I prevent it?
The answer in the proposed duplicate is incorrect, unfortunately. It doesn't ignore repeated KeyDown events, and so will gradually increase the "delta" value in the direction being handled by each key case. It also doesn't respond to the keypress immediately (i.e. no action happens until the first timer tick).
This answer to Holding Arrow Keys Down For Character Movement C# .Net ISSUES explains how to ignore the subsequent KeyDown events, but doesn't explain how then your character would move.
In other words, I couldn't find a duplicate question that actually correctly answers your question. So…
The basic technique you want to do is:
Don't move on the actual key input. Instead, generate your own timing logic that will move the object.
Instead of using the KeyDown event to actually move the object, use it to set a movement direction, which is then processed by your timing logic.
There are a variety of ways to accomplish this. One version would look like this:
private bool _moveUp;
private bool _moveDown;
private bool _moveLeft;
private bool _moveRight;
// You can add the Timer in the Winforms Designer instead if you like;
// The Interval property can be configured there at the same time, along
// with the Tick event handler, simplifying the non-Designer code here.
private System.Windows.Forms.Timer _movementTimer = new Timer { Interval = 100 };
public MainForm()
{
InitializeComponent();
_movementTimer.Tick += movementTimer_Tick;
}
private void movementTimer_Tick(object sender, EventArgs e)
{
_DoMovement();
}
private void _DoMovement()
{
if (_moveLeft) Player.MoveLeft();
if (_moveRight) Player.MoveRight();
if (_moveUp) Player.MoveUp();
if (_moveDown) Player.MoveDown();
}
// You could of course override the OnKeyDown() method instead,
// assuming the handler is in the Form subclass generating the
// the event.
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.IsRepeat)
{
// Ignore key repeats...let the timer handle that
return;
}
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = true;
break;
case Keys.Down:
_moveDown = true;
break;
case Keys.Left:
_moveLeft = true;
break;
case Keys.Right:
_moveRight = true;
break;
}
_DoMovement();
_movementTimer.Start();
}
public void MainForm_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = false;
break;
case Keys.Down:
_moveDown = false;
break;
case Keys.Left:
_moveLeft = false;
break;
case Keys.Right:
_moveRight = false;
break;
}
if (!(_moveUp || _moveDown || _moveLeft || _moveRight))
{
_movementTimer.Stop();
}
}
Do note that the timer objects in .NET have limited resolution. I show an interval of 100 ms (10 times per second) above (same as in the other question's answer), and this is about as frequent an update as you're going to reliably get. Even then, the timer's Tick event may not (and probably won't) be raised on exactly 100 ms intervals. There will be some variation back and forth. But it will be close enough for a basic game.
If you need more precision than that, you will have to implement your own state-polling-and-animation loop somewhere. That's a whole other ball o' wax. :)
A subjectivly elegent approach:
public partial class Form1 : Form
{
private static Timer timer;
private static bool[] keys_down;
private static Keys[] key_props;
private void Form1_Load(object sender, EventArgs e)
{
keys_down = new bool[4];
key_props = new []{Keys.A, Keys.D, Keys.W, Keys.S};
timer = new Timer();
timer.Interval = 15; // Roughly 67 FPS
timer.Tick += tick;
timer.Start();
KeyDown += key_down_event;
KeyUp += key_up_event;
... // More things to do when the form loads.
}
private void tick(Object source, EventArgs e)
{
... // Do this every timing interval.
byte n = 0;
foreach (var v in keys_down)
{
if (n == 3 && v)
... // If the "s" key is being held down, no key delay issues. :)
n++;
}
...
}
private void key_down_event(object sender, KeyEventArgs e)
{
byte n = 0;
foreach (var v in keys_down)
{
if (e.KeyCode == key_props[n])
keys_down[n] = true;
n++;
}
}
private void key_up_event(object sender, KeyEventArgs e)
{
byte n = 0;
foreach (var v in keys_down)
{
if (e.KeyCode == key_props[n])
keys_down[n] = false;
n++;
}
}
public Form1()
{
InitializeComponent();
}
}
I was looking for solutions to create a small copy of Flappy Bird. Perhaps my decision will help someone.
I used the addition of a player position with a variable in timer to simulate gravity. When I press W, I turned off the further reception of the command using bool and simply inverted gravity
private void time_Tick(object sender, EventArgs e)
{
if (bird.Location.Y >= 540)
{
bird.Location = new Point(bird.Location.X, 540);
}
else
{
bird.Location = new Point(bird.Location.X, bird.Location.Y+grav);
}
}
private void Form1_KeyPress(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W && press == false)
{
press = true;
grav = -10;
}
return;
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W && press == true)
{
press = false;
grav = 5;
}
return;
}

How to fix this Two keys pressed conflict?

I have a problem that I'm struggling with.. I want to move an image using my keyboard to the left, right, up or down and in a diagonal way. I searched the web and found, that to use 2 diffrent keys I need to remember the previous key, so for that I'm using a bool dictionary.
in my main Form class this is how the KeyDown event looks like:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
baseCar.carAccelerate(e.KeyCode.ToString().ToLower());
carBox.Refresh(); //carbox is a picturebox in my form that store the image I want to move.
}
My KeyUp event:
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
baseCar.carBreak(e.KeyCode.ToString().ToLower());
}
My Paint event:
private void carBox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(Car, baseCar.CharPosX, baseCar.CharPosY); // Car is just an image
}
And my baseCar class:
private Dictionary KeysD = new Dictionary(); // there is a method to set the W|A|S|D Keys, like: KeysD.Add("w",false)
public void carAccelerate(string moveDir)
{
KeysD[moveDir] = true;
moveBeta();
}
public void moveBeta()
{
if (KeysD["w"])
{
this.CharPosY -= this.carMoveYSpeed;
}
if (KeysD["s"])
{
CharPosY += carMoveYSpeed;
}
if (KeysD["a"])
{
CharPosX -= carMoveXSpeed;
}
if (KeysD["d"])
{
CharPosX += carMoveXSpeed;
}
}
public void carBreak(string str)
{
KeysD[str] = false;
}
Anyway it works, but my problem is that I can't get back to the first pressed key for example:
I pressed W to move up and then the D key to go diagonal, how ever when I release the D key it wont go Up again because the KeyDown event is "dead" and wont call the carAccelerate() method again.. and I can't figure out how to fix it..
Can any one help me please? Maybe there is a better way to handle the keys? im open to any ideas! And I hope you can understand it, my english isnt the best :S
Typically you don't hande the key events directly for these kinds of things. Instead, you keep track of what keys are currently pressed. Physics calculations are done on some interval, which can be done with a timer. Quick and dirty example below. However, this is not the kind of thing you should be attempting with WinForms.
private const int ACCELERATION = 1;
private HashSet<Keys> pressed;
private int velocityX = 0;
private int velocityY = 0;
public Form1()
{
InitializeComponent();
pressed = new HashSet<Keys>();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
pressed.Add(e.KeyCode);
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
pressed.Remove(e.KeyCode);
}
private void timer1_Tick(object sender, EventArgs e)
{
car.Location = new Point(
car.Left + velocityX,
car.Top + velocityY);
if (pressed.Contains(Keys.W)) velocityY -= ACCELERATION;
if (pressed.Contains(Keys.A)) velocityX -= ACCELERATION;
if (pressed.Contains(Keys.S)) velocityY += ACCELERATION;
if (pressed.Contains(Keys.D)) velocityX += ACCELERATION;
}

Categories

Resources