sorry again for asking a probable simple question but I have been searching among the internet for this solution but I am unable to. I am in Highschool programming working on a final project. I have everything done except this little part. I have 2 background workers made(One for each player) and I have keydown events turning these background workers on. Here is the code:
if (e.keycode == Keys.D)
{
p1f = true;
bgwPlayerOne.RunWorkerAsync();
}
if (e.keycode == Left)
{
p2f = true;
bgwPlayerTwo.RunWorkerAsync();
}
Okay, so I had to type that out because it is on my other computer which has no internet but that is the code in the keydown event. The variables tells that in the background worker which if statement to do. But, the main issue is that whenever I press D and Left Arrow Key at the same time, The one going before the other click is stopped and the most recent key event is started. I didn't have this issue when I was using timers previously(I was having a lag issue so I switched to multithread). If there are any questions about this please tell me, any help would be appreciated, thanks!
Updated*****
private void frmTankBattle_MapOne_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.D)
{
p1f = true;
bgwPlayerOne.RunWorkerAsync();
}
//System.Threading.Thread.Sleep(1);
if (e.KeyCode == Keys.A)
{
p1b = true;
bgwPlayerOne.RunWorkerAsync();
}
//System.Threading.Thread.Sleep(1);
if (e.KeyCode == Keys.Left)
{
p2f = true;
bgwPlayerTwo.RunWorkerAsync();
}
//System.Threading.Thread.Sleep(1);
if (e.KeyCode == Keys.Right)
{
p2b = true;
bgwPlayerTwo.RunWorkerAsync();
}
if (e.KeyCode == Keys.Space)
{
p1c = true;
tmrPlayerOneCombat.Start();
}
// System.Threading.Thread.Sleep(1);
}
private void frmTankBattle_MapOne_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.D)
{
p1f = false;
p1b = false;
bgwPlayerOne.CancelAsync();
}
if (e.KeyCode == Keys.A)
{
p1f = false;
p1b = false;
bgwPlayerOne.CancelAsync();
}
if (e.KeyCode == Keys.Left)
{
p2f = false;
p2b = false;
bgwPlayerTwo.CancelAsync();
}
if (e.KeyCode == Keys.Right)
{
p2f = false;
p2b = false;
bgwPlayerTwo.CancelAsync();
}
}
There are couple things wrong with your code and I will go thru them one by one.
Firstly KeyDown event will fire multiple times per second as long as any key has been hold down.
This means if RunWorkerAsync(); did not finish before the event fires for the second time you will receive an InvalidOperationException.
If you have previously hold down a key lets say key A, and then you try to hold down D key the new key code will be D instead of A even though two keys are held down at the same time. You can test out this behavior in notepad.
Since you are on WinForm you don't have the access to the Win32 Keyboard wrapper. You will need to do some manual tracking yourself.
You can have a HashSet<Keys> to store the keys currently being held down. You add the new keys in KeyDown event and Removes them in KeyUp event. Then you can iterate thru the list to check which keys are currently down and act accordingly.
Related
I'm making a game in C# in Forms in Visual Studio and I'm currently registering key presses from the player with the keyDown event and it works well. When the player is holding down a button it gets pressed once, there is a delay and then the key is registered many times over and over, which is good, but if any other key that I'm checking for gets pressed while the first key is being held, the new key is registered once and then nothing more, just like when typing. I want it so that if key is pressed while another is being held, the first key is registered once, and then the first key, that's being held, keeps outputting.
I'm currently getting:
aaaaaaaaaaaaaaaaaap
but I want:
aaaaaaaaaaaaaaaaaapaaaaaaaaaaaaaa
if that makes sense
Is it possible?
Here is my current method:
private void GUItetris_KeyDown(object sender, KeyEventArgs e)
{
if (!Tetris.gameOver && Tetris.gameBoard != null)
{
if (e.KeyCode == Keys.A) // a
{
Tetris.gameBoard.activePiece.MoveLeft(Tetris.gameBoard.boardColor);
Tetris.gameBoard.UpdatePiece();
}
if (e.KeyCode == Keys.D) //d
{
Tetris.gameBoard.activePiece.MoveRight(Tetris.gameBoard.boardColor);
Tetris.gameBoard.UpdatePiece();
}
if (e.KeyCode == Keys.S) //s
{
Tetris.gameBoard.activePiece.MoveDown(Tetris.gameBoard.boardColor);
Tetris.gameBoard.UpdatePiece();
}
if (e.KeyCode == Keys.P) // p
{
Tetris.gameBoard.activePiece.Rotate(Tetris.gameBoard.boardColor, true);
Tetris.gameBoard.UpdatePiece();
}
if (e.KeyCode == Keys.L) // l
{
Tetris.gameBoard.activePiece.Rotate(Tetris.gameBoard.boardColor, false);
Tetris.gameBoard.UpdatePiece();
}
}
}
So what im looking to do is find if i have the space bar pressed at all and while it is i would like to have the space bar be released and then pressed again mutiple times with a delay of about 10 ms. i have tried to read and understand this link (https://msdn.microsoft.com/en-us/library/system.windows.forms.control.keypress.aspx) but it is still confusing any help? im brand new to c# but i have some experience with pascal of which i have found very similar
(using visual studio 2015 due to my computer not allowing me to update to windows 8.1)
10ms delay mean 100 times per second, no one could do it.
Key events occur in the following order:
KeyDown
KeyPress
KeyUp
Events KeyDown and KeyUp use KeyEventArgs, while KeyPress is KeyPressEventArgs,
KeyEventArgs could details how many keys are pressed at same time. KeyEventArgs.KeyCode is Keys which is a [Flags] enum, it could contains multiple keys, like CTRL+SHift+F+G
if your hotkey is Ctrl+Shift+Space, you can check with:
var hotkeyPressed = e.Control && e.Shift && e.KeyCode == Keys.Space;
if your hotkey is Ctrl+F10+Space, you can check with:
var hotkeyPressed = e.Control && e.KeyCode == (Keys.Space | Keys.F10);
but do not use:
var hotkeyPressed = e.Control && e.KeyCode.HasFlag(Keys.F10) && e.KeyCode.HasFlag(Keys.Space); // e.KeyCode probably contains other flags
KeyPressEventArgs.KeyChar is a string, take a look source code, focus on its comments
[ComVisible(true)]
public class KeyPressEventArgs : EventArgs
{
...
/// <summary>Gets or sets the character corresponding to the key pressed.</summary>
/// <returns>The ASCII character that is composed. For example, if the user presses SHIFT + K, this property returns an uppercase K.</returns>
public char KeyChar { get; set; }
...
}
to use which event, it depends on your requirement.
here is sample code in KeyDown:
private int counting = 0, limit = 10;
private void txt_KeyDown(object sender, KeyEventArgs e)
{
if (!e.KeyCode.HasFlag(Keys.Space)) //check if your expected key is pressed
{
counting = 0;
return;
}
//start counting
counting++;
if (counting > limit)
{
e.Handled = true;
//do you business work, like: Send something somewhere
}
else
{
//do something else, like: show the number 'counting' in GUI
}
}
if you want limit timespan with next space, use a Timer
private void txt_KeyDown(object sender, KeyEventArgs e)
{
timer1.Stop();
if (!e.KeyCode.HasFlag(Keys.Space))
{
counting = 0;
return;
}
//start counting
timer1.Start();
counting++;
if (counting > limit)
{
e.Handled = true;
//do you business work, like: Send something somewhere
}
else
{
//do something else, like: show the number 'counting' in GUI
}
}
//timer1.Interval = 100; //100ms timeout. user has to press space again within 100ms
private void timer1_Tick(object sender, EventArgs e)
{
counting = 0;
}
im working on a simple game like "space invaders",and i got into a problem.
Im trying to give the user, the possiblity to move as much as he want from Left to Right, and in the same time have the possiblty to shot using the "Space bar".
My problem is: when i pressed more then 1 key, only 1 function run.
here a few stuff i tried:
Storing the keys in a List<Keys> (but i didnt find any good way to excute the functions and everything become messy)
2.normal handling of the key_down event like this:
protected void Form1_keysDown(object obj, KeyEventArgs e)
{
(e.KeyData == Keys.Space)
spaceShip.FireBullets();
if (e.KeyCode == Keys.Left)
spaceShip.MoveLeft();
if (e.KeyCode == Keys.Right)
spaceShip.MoveRight();
}
my qustion is: what is a good way to make this work?
(sorry for my english)
You are relying on the keyboard controller repeating the key when you hold it down. That stops working when you press another key. This requires a different approach.
First you need an enum that indicates the motion state of the spaceship with values like NotMoving, MovingLeft and MovingRight. Add a variable of that type to your class. You'll need both the KeyDown and KeyUp events. When you get a KeyDown for, say, Keys.Left then set the variable to MovingLeft. When you get the KeyUp event for Keys.Left then first check if the state variable is still MovingLeft and, if it is, change it NotMoving.
In your game loop, use the variable value to move the spaceship. Some sample code:
private enum ShipMotionState { NotMoving, MovingLeft, MovingRight };
private ShipMotionState shipMotion = ShipMotionState.NotMoving;
protected override void OnKeyDown(KeyEventArgs e) {
if (e.KeyData == Keys.Left) shipMotion = ShipMotionState.MovingLeft;
if (e.KeyData == Keys.Right) shipMotion = ShipMotionState.MovingRight;
base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyEventArgs e) {
if ((e.KeyData == Keys.Left && shipMotion == ShipMotionState.MovingLeft) ||
(e.KeyData == Keys.Right && shipMotion == ShipMotionState.MovingRight) {
shipMotion = ShipMotionState.NotMoving;
}
base.OnKeyUp(e);
}
private void GameLoop_Tick(object sender, EventArgs e) {
if (shipMotion == ShipMotionState.MovingLeft) spaceShip.MoveLeft();
if (shipMotion == ShipMotionState.MovingRight) spaceShip.MoveRight();
// etc..
}
This is purely for a usability request my clients have asked me.
They work with a datagridview typing in grades for students. So they pretty much go into zombie mode and start tapping in numbers, and they've told me that it would be easier if they could tap the numpad enter key to have the next vertical select focused.
Here's what I've tried to use:
private void dataGridView1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
SendKeys.Send("{DOWN}");
}
}
Unfortunately, it doesn't work as expected. Sometimes it seems to fire that key many times, causing the focus to scroll 2 or 3 or 4 cells downwards and really shaking up the users focus.
How can I make this work and make my clients happy?
private void datagridView1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
bindingsource.MoveNext();
}
}
Try this - I have tested it and it works.
private void datagridView12_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
int col = dataGridView12.CurrentCell.ColumnIndex;
int row = dataGridView12.CurrentCell.RowIndex;
if (row != dataGridView12.NewRowIndex)
{
if (col == (dataGridView12.Columns.Count - 1))
{
col = -1;
row++;
}
dataGridView12.CurrentCell = dataGridView12[col, row + 1];
}
e.Handled = true;
base.OnKeyDown(e);
}
}
You could use a boolean that is set to true on keyDown and set to false on keyUp.
Only run your sendKeys.Send when the boolean is true. That should ensure that the event is only fired once for every key press.
That's often how I've seen it done in other languages, I'm not sure if there is a better way to do it in C#.
When you press Enter key in the DataGridView (whether in edit mode or not) it goes one cell down by default (in .NET framework 3.5 and 4.0).
Isn't it the functionality you needed in the first place?
Can you not set the focus on the next textbox when the user presses enter?
You can use the TextBox.Focus() method to move the next textbox.
I am creating a small game, the game is printed onto a panel on a windows form. Now i want to capture the keydown event to see if its the arrow keys that has been pressed, the problem however is that i can't seem to capture it.
Let me explain, on the form i have 4 buttons and various other controls and if the user for instance press one of the buttons (to trigger a game event) then the button has focus and i can't capture the movements with the arrow keys.
I tried something like
private void KeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
game.MovePlayer(DonutWarsLibrary.GameObjects.Direction.E);
game.DrawObjects(panel1.CreateGraphics());
}
else if (e.KeyCode == Keys.Right)
{
game.MovePlayer(DonutWarsLibrary.GameObjects.Direction.W);
game.DrawObjects(panel1.CreateGraphics());
}
else if (e.KeyCode == Keys.Up)
{
game.MovePlayer(DonutWarsLibrary.GameObjects.Direction.N);
game.DrawObjects(panel1.CreateGraphics());
}
else if (e.KeyCode == Keys.Down)
{
game.MovePlayer(DonutWarsLibrary.GameObjects.Direction.S);
game.DrawObjects(panel1.CreateGraphics());
}
}
and then when the form key down event was pressed, i used this
private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
KeyDown(e);
}
I also added keydown for the buttons and the various other controls on the windows form, but i am not getting any response back. I have setup a breakpoint inside the function to see if it's being called, but that breakpoint never triggers?
Any ideas?
The most optimal was to have a general KeyDown event that triggers (regardless of what control that currently has focus) and then calls the KeyDown method.
Have you set the KeyPreview property of the form to true? That will cause the form to get a "first look" at key events.
Update: getting this to work properly when a Button has focus seems to be a bit tricky. The Button control intercepts the arrow key presses and moves focus to the next or previous control in the tab order in a manner so that the KeyDown, KeyUp and KeyPress events are not raised. However, the PreviewKeyDown event is raised, so that can be used:
private void Form_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = ProcessKeyDown(e.KeyCode);
}
// event handler for the PreViewKeyDown event for the buttons
private void ArrowButton_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
ProcessKeyDown(e.KeyCode);
}
private bool ProcessKeyDown(Keys keyCode)
{
switch (keyCode)
{
case Keys.Up:
{
// act on up arrow
return true;
}
case Keys.Down:
{
// act on down arrow
return true;
}
case Keys.Left:
{
// act on left arrow
return true;
}
case Keys.Right:
{
// act on right arrow
return true;
}
}
return false;
}
Still, the focus moves around in a rather ugly manner...
I believe the easiest way of solving this problem is through overriding the ProcessCmdKey() method of the form. That way, your key handling logic gets executed no matter what control has focus at the time of keypress. Beside that, you even get to choose whether the focused control gets the key after you processed it (return false) or not (return true).
Your little game example could be rewritten like this:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Left)
{
MoveLeft(); DrawGame(); DoWhatever();
return true; //for the active control to see the keypress, return false
}
else if (keyData == Keys.Right)
{
MoveRight(); DrawGame(); DoWhatever();
return true; //for the active control to see the keypress, return false
}
else if (keyData == Keys.Up)
{
MoveUp(); DrawGame(); DoWhatever();
return true; //for the active control to see the keypress, return false
}
else if (keyData == Keys.Down)
{
MoveDown(); DrawGame(); DoWhatever();
return true; //for the active control to see the keypress, return false
}
else
return base.ProcessCmdKey(ref msg, keyData);
}
Override IsInputKey behaviour
You must override the IsInputKey behavior to inform that you want the Right Arrow key to be treated as an InputKey and not as a special behavior key.
For that you must override the method for each of your controls.
I would advise you to create your won Buttons, let's say MyButton
The class below creates a custom Button that overrides the IsInputKey method so that the right arrow key is not treated as a special key. From there you can easily make it for the other arrow keys or anything else.
public partial class MyButton : Button
{
protected override bool IsInputKey(Keys keyData)
{
if (keyData == Keys.Right)
{
return true;
}
else
{
return base.IsInputKey(keyData);
}
}
}
Afterwards, you can treat your keyDown event event in each different Button or in the form itself:
In the Buttons' KeyDown Method try to set these properties:
private void myButton1_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
//DoSomething();
}
-- OR --
handle the common behaviour in the form: (do not set e.Handled = true; in the buttons)
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
//DoSomething();
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
KeyPreview = true;
KeyDown += new KeyEventHandler(Form1_KeyDown);
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
System.Diagnostics.Debug.Write(e.KeyCode);
}
}