Sign in to follow this  
Gage64

[.net] Keyboard/mouse event handlers - must return quickly?

Recommended Posts

A while ago I downloaded a demo that renders some terrain and allows you to move an FPS camera around it using the keyboard. The handler for the KeyDown event looked something like this:
void MyApp_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.W)
        camera.Walk(0.5f);
    // Handle other keys similarly
}

I compiled and ran the demo but the camera movement was extremely choppy. I then decided to change it to this:

// This is a member of MyApp
bool keys[256];

void MyApp_KeyDown(object sender, KeyEventArgs e)
{
    keys[(int)e.KeyCode] = true;
}

void MyApp_KeyUp(object sender, KeyEventArgs e)
{
    keys[(int)e.KeyCode] = false;
}

// In the Update/Render function:
if (keys[(int)Keys.W])
    camera.Walk(0.5f);
// Handle other keys similarly

After this change the movement was completely smooth. My reasoning for doing this was that the handlers are called each time a key is pressed/released and so they must return very quickly so they can be called for the next key press/release. My question is - am I thinking about this correctly? The reason I ask is: 1) The previous code in the handlers really didn't do much, so I would expect it to be executed fast enough for this not to be a problem. 2) I see many people that use the first method (in tutorials, posts, etc.) and none of them are complaining about choppiness. BTW, the demo used C# and MDX but I guess the question is valid for any Windows Forms application, and also for Win32 applications and the window procedure (maybe especially there because that function is called for many other events as well). Any help is greatly appreciated.

Share this post


Link to post
Share on other sites
KeyDown events don't get sent every frame (I'm making assumptions about your environment/libraries). Typically KeyDown get sent only when the key is initially pressed and then once every so often depending on the key repeat rate. So the reason the old one is choppy is that the event is only getting sent once in a while (maybe 10 times a second or something, based on the repeat rate). The way you've changed it keeps track of the key's state so you can advance the camera every frame.

Share this post


Link to post
Share on other sites
Quote:

Original post by wendigo23
the reason the old one is choppy is that the event is only getting sent once in a while (maybe 10 times a second or something, based on the repeat rate). The way you've changed it keeps track of the key's state so you can advance the camera every frame.


Thanks, that makes sense, though I'm still wondering why it's not a problem for many other people.

Also, I guess this means that using an event-based approach to input in a game is not a good idea because it will not be updated fast enough (though you must still use such an approach when you need to know the exact order of input events, like when typing a name to be entered into a high-scores list)?

Share this post


Link to post
Share on other sites
Quote:
Also, I guess this means that using an event-based approach to input in a game is not a good idea because it will not be updated fast enough (though you must still use such an approach when you need to know the exact order of input events, like when typing a name to be entered into a high-scores list)?


I use the mousedown event and mouseup to tell my system when the mouse is up or down, then in connection, the mousemove event moves my camera when it knows the mouse is down and does it quite well. Never miss a frame and is never choppy for me.

Your code might be faster if it didn't scan an array ever time it renders the loop. Perhaps cash to a bool value or something? I'm speaking of course of:


// In the Update/Render function:
if (keys[(int)Keys.W])
camera.Walk(0.5f);
// Handle other keys similarly



At least that would be my guess :)

HTH,

-Devin

Share this post


Link to post
Share on other sites
Instead of guessing why it's slowing down, measure it. Using Stopwatch.ElapsedMilliseconds should give you an idea of where the slowdown is occurring if it's within camera.Walk. Otherwise I would measure the frequency of MyApp_KeyDown as wendigo23 suggests, and also check if your render loop is rendering when keys are held down.

Share this post


Link to post
Share on other sites
mutex,

I wasn't suggesting that a [begin edit] very small [end edit] slowdown was causing the jitter. That speedup idea was just a sugestion. I wasn't really guessing, scanning an array every frame WILL cause some slow down. Do it once and OK, no big deal, but do that too much and soon it can become a problem, thus the reason for the suggestion. Sorry for the confusion.

-Devin

Share this post


Link to post
Share on other sites
I use the event style input (bound to delegates rather than hardcoded, so if anything mine should be worse) and don't have any problem really. Though IIRC the auto-repeat does require that sort of 'on key held down' event to be a WalkStart/WalkStop sort of pair to keep it smooth since the auto-repeat only kicks in after enough time has gone past.

Share this post


Link to post
Share on other sites
Quote:

Original post by devronious
I use the mousedown event and mouseup to tell my system when the mouse is up or down, then in connection, the mousemove event moves my camera when it knows the mouse is down and does it quite well. Never miss a frame and is never choppy for me.


Any idea why that is? From what wendigo23 and Telastyn say I would think you should have the same choppiness problem?

Quote:
Your code might be faster if it didn't scan an array ever time it renders the loop. Perhaps cash to a bool value or something?


What do you mean by "scan an array"? I simply index the array for the keys I want to handle. I highly doubt that that can cause a slowdown (and note that I said that with this code the movement is completely smooth).

Quote:

Original post by mutex
Instead of guessing why it's slowing down, measure it. Using Stopwatch.ElapsedMilliseconds should give you an idea of where the slowdown is occurring if it's within camera.Walk. Otherwise I would measure the frequency of MyApp_KeyDown as wendigo23 suggests, and also check if your render loop is rendering when keys are held down.


I wouldn't call it slowing down. I didn't have a frame-rate display, but I'm guessing that if I did it would always be high. Rather, it seemed like the camera is only being moved on some frames, which causes the choppiness.

Thanks to everyone for their comments so far. I understand it better now but I still wonder why it works for some of you.

Share this post


Link to post
Share on other sites
I just did a little experiment. I added two variables to the demo - keyDown and mouseMoving. At the beginning of the main loop, I set them both to false. In the KeyDown handler, I set keyDown to true (but I do not set it to false in the KeyUp handler), and in the MouseMove handler I set mouseMoving to true. Then I print them both every frame.

When I run the program and move the mouse, it always prints "mouseMoving = true", but if I press a key and keep it pressed, the printed value of keyDown sort of switches between true and false (though it's hard to see at what rate), which indeed confirms that it is not called frequently enough (i.e., every frame) to allow for smooth movement.

But then why does the MouseMove handler gets called frequently enough (this also explains why devronious' code is not choppy)? Is it hardware related, i.e., whatever "detects" mouse movement does it better then whatever "detects" that a key is pressed?

Share this post


Link to post
Share on other sites
Mouse movement events get sent constantly while the mouse is moving. Constantly is a lie. There is probably some sample rate going on behind the scenes inside windows and/or the mouse itself. A quick googling reveals numbers in the 100 to 200 samples per second range.

Key up/down events are designed for typing. That's where the repate rate comes in. I don't know how fast a keyboard could be polled, but if the OS sent out a keydown event at maximum poll frequency notepad would fill with letters really fast.

So the difference is really just because of how the different pieces of hardware are used. Keyboard is used for a typed character now and then, mouse is used for a smoothly moving cursor.

Share this post


Link to post
Share on other sites
Gage64,

The MouseMove event is usually used for panning and rotating your view, you know, pitch, yaw and such. The reason that the MouseMove event works so well is that it is fired every time the mouse is moved from one screen pixel to the next. Not until such time though, so there's no unnecessary firing until the mouse is actually moved. In other words, if you keep the mouse still, it does not fire. It's smooth because it is fired on each frame that actually counts, or in other words, each frame when the mouse is actually moved and the user would see the view change.

The scan array was just an optimization suggestion. At the end of the programming phase I usually perform all of my optimization checking, to see if there's anyway that I could possible speed things up. The speed up in your code would probably not be noticable for this instance. But what I meant was that if this sort of thing was repeated too much in the render loop you might see performance degrade some.

HTH,

Devin

Share this post


Link to post
Share on other sites
I would suggest using polling for the mouse instead of events, it should cut down on some of the overhead a bit. For XNA which can be locked at 60 FPS, this is more than enough for smooth mouse tracking, not to mention the mouse cursor if being rendered wont be rendered faster than 60 fps anyways. Plug in some math to smooth the movement and handle acceleration and it should work well enough for you.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this