I'm working on a Pong game and right now I'm working on being able to register multiple KeyDown presses simulatenously. I used the Win32 native method GetKeyBoardState
first to loop through all of the virtual keys and see if any of the keys matched the game input keys. Now this worked just fine but in the interest of optimization I figured that it would be quicker to use GetKeyState
since there are only 4 valid input keys and then simply check if any of those keys are pressed down.
Surprisingly it is incredibly slow and choppy, a lot worse than GetKeyBoardState
so I'm wondering if there's something in my code that's slowing it down. Here's the complete implementation
NativeMethods.cs
internal static class NativeMethods
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
internal static extern short GetKeyState(int nVirtKey);
}
Keyboard.cs
static class Keyboard
{
public static bool GetKeyState(Keys vKey)
{
short results = NativeMethods.GetKeyState((int)vKey);
// We're only going to use this to look for arrow keys so
// No need to differntiate results based on low or high order bits
switch (results)
{
case 0: //KEY STATE: NOT PRESSED
return false;
case -127: //KEY STATE: PRESSED
return true;
}
return false;
}
GameForm.cs
private void GameForm_KeyDown(object sender, KeyEventArgs e)
{
var movementData = new Tuple<int, Move>(-1, Pong.Move.Down);
//Check how many keys that are pressed down (for example both players might be moving their paddles simulatenously)
var pressedKeys = inputKeys.Where(key => Keyboard.GetKeyState(key));
//Check if pressed key equals any of the player assigned keys.. If so call MovePaddle()
foreach (Keys key in pressedKeys)
{
switch (key)
{
case Keys.Up:
movementData = Tuple.Create(0, Pong.Move.Up);
break;
case Keys.Down:
movementData = Tuple.Create(0, Pong.Move.Down);
break;
case Keys.W:
movementData = Tuple.Create(1, Pong.Move.Up);
break;
case Keys.S:
movementData = Tuple.Create(1, Pong.Move.Down);
break;
}
gameController.MovePaddle(movementData.Item1, movementData.Item2);
}
}
2 Answers 2
Your bool GetKeyState
is incorrect. The reason for this is that the comment
No need to differntiate results based on low or high order bits
is wrong. The low bit will alternate between 0 and 1 for all keys, not just caps-lock/num-lock/scroll-lock. So the method will return false
for every second key-press.
Replace it by:
public static bool GetKeyState(Keys vKey)
{
short state = NativeMethods.GetKeyState((int)vKey);
return (state & 0x80) != 0;
}
What is impacting you performance so heavily is the creation of Tuples everytime you recieve a keypressed event.
creating objects is slow. What you should do instead is, directly apply the changes to the moving objects (your two pong-thingies).
private void GameForm_KeyDown(object sender, KeyEventArgs e)
{
var pressedKeys = inputKeys.Where(key => Keyboard.GetKeyState(key));
foreach(Keys key in pressedKeys)
switch(key){
case Keys.Up:
leftPong.Positon += FIXEDVALUE;
break;
case Keys.Down:
leftPong.Position -= FIXEDVALUE;
break;
//continue...
-
\$\begingroup\$ Are you saying that there's a big difference in performance inbetween creating one instance of an integer, one instance of an enum and create one instance of a tuple to hold aforementioned instances? I must admit I didn't think that tuples would take up a lot of resources. \$\endgroup\$Overly Excessive– Overly Excessive2014年04月18日 09:46:20 +00:00Commented Apr 18, 2014 at 9:46
-
\$\begingroup\$ @Overly It's not the creation in itself, but the interval in which it's perfomed \$\endgroup\$Vogel612– Vogel6122014年04月18日 10:09:44 +00:00Commented Apr 18, 2014 at 10:09
-
1\$\begingroup\$ Sounds unlikely. Creating objects only matters if you do it millions of times per second. \$\endgroup\$CodesInChaos– CodesInChaos2014年04月20日 19:12:23 +00:00Commented Apr 20, 2014 at 19:12