james_lohr's picture

What happened to max_key in the Key Enum?

It's no big deal, but I had hastily replaced occurences of (int)Key.max_key with Enum.GetValues(typeof(Key)).Length. Well, guess what happened in the following code:

for(int n=0;n< Enum.GetValues(typeof(Key)).Length;n++){
...
}

What I had down as a totally negligible enumeration actually bombed out my fps. Obviously it's not hard to store the length of the enumeration somewhere, but max_key was so much more convenient.

[edit]

I'm glad to see that MouseButton.LastButton is still there. :P


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
objarni's picture

May I ask why you are looping through all keys?

james_lohr's picture

Yes sure. Perhaps it's a bit of a bad habit, but I've always used a sort of polling technique for key presses / releases in my games for the sake of convenience. So, for example, my logic code may look like:

updateLogic(){
 
    polldKeys.Update();
 
.
.
.
 
    if(polledKeys[Key.Space] == keyState.Pressed){
        //this will always gets called exactly once upon space being pressed
        doSomething1();
    }
 
.
.
.
 
    if(polledKeys[Key.Space] == keyState.Released){
        //this will always get called exactly once upon release
        doSomething2();
    }
 
.
.
.
 
    if(polledKeys[Key.Space == keyState.Held){
        //Always called at least once when a key is pressed, also called every frame while the key is held
       doSomething3();
    }
 
 
}

Perhaps calling them "polled" is misleading, because I most certainly don't poll the Keyboard device. It's still driven by the usual KeyDown, KeyUp events. All that polledKeys.Update() does is switch a value between pressed, held and released so that I get the desired behaviour (pressed/released state for exactly one frame of the logic loop) without ever having to deal with events. What's also nice is that the actual KeyDown/KeyUP events themselves never get bloated because they're just updating a single value (which is what gets polled in the Update() call).

Obviously you'd want to stick to events for any sort of forms type application, but for your average game, I find this to be far more convenient.

objarni's picture

Are you using GameWindow or GLControl?

If you are using GameWindow, you could use the Keyboard[Key.Space] which is what you looking for, I guess?

In the GLControl case, I'm with you.

the Fiddler's picture

The keyboard interface is undergoing some changes right now. The current interface works but it has a couple of problems: it is not thread-safe, it doesn't support multiple keyboards and it is bound to the GameWindow. The new interface will be completely polled (no events) and will look very similar to your code snippet:

KeyboardState state = Keyboard.GetState();
if (state.IsKeyDown(Key.Space)) { doSomething(); }

You can detect held keys trivially by comparing the previous KeyboardState with the current one.

Multiple keyboards are trivial to support like this:

KeyboardState keyboard1 = Keyboard.GetState(1);
KeyboardState keyboard2 = Keyboard.GetState(2);
 
if (keyboard1.IsConnected) { ... }
if (keyboard2.IsConnected) { ... }

Finally, this can be implemented in a thread-safe way - in other words, you'll be able to call GetState() from any thread and receive correct results.

The MaxKeys value was removed because it's considered a C-ism that is not very common in C# code. However, I can still see its appeal (for example, you might wish to loop and check if at least one key is pressed). It is not something that I feel strongly about either way: we can readd the value and document that the Key values are consecutive.

james_lohr's picture

Fiddler: Although I do advocate a thread-safe way of dealing with input (and multiple keyboards is cool too), I'm a bit concerned about getting rid of events.

I'll illustrate my point with an example: At present I have my game logic in a separate thread to the GameWindow thread (mainly because of client-server synchronization which doesn't like the fact that the GameWindow thread freezes when moving the window). For controls to work in my game logic thread, I need to set values in the GameWindow thread (via the current events you have provided), and then share these values (via locking) with my game logic thread.

Now, let's say that the data being shared is just a boolean value which indicates whether or not a key is currently down, and let's say we leave it as the game logic thread's own responsibility to translate this boolean "on / off" value into a "pressed - once per frame". "released - once per frame".

This is normally fine, but consider the following case:

What if a key is pressed for a duration shorter than the rate at which the thread is being polled? - the thread then fails to ever see the key as "on", and therefore the key press doesn't register. *

There is also another issue with using polling instead of events: When typing quickly, it's critical that the key presses are registered in the correct order. I would imagine that typing quickly soon becomes a horrible experience if the rate drops below 60Hz.

Now, I have no idea how OpenTK is currently generating events from keypresses, but the only reason I can see for eliminating events alltogether is if the events themselves originate from polling or need to be processed via polling anyway. (which may very well be the case, as I don't know much about events in C# and I may be confusing them with good old hardware interrupts in C :P)

the Fiddler's picture

Right now, OpenTK is using events for keyboard/mouse input and polling for joysticks. This is an implementation detail that may change between platforms and releases.

The way I see it, there are two distinct use-cases:

  1. typing text or navigating a UI, which tends to work better using events
  2. navigating the game environment, which tends to work better with a polled model

Events always run on the GUI thread and do not play nicely with multiple threads, which is why you need such a roundabout way for checking input (you have actually built a thread-safe polled interface on top of OpenTK's event-based interface). A thread-safe, polled interface would have made this workaround redundant, but you'd have to maintain a high polling rate to avoid dropped keys.

In other words, it's a trade-off. Ultimately, I think it's much easier to keep the polling rate high enough than working around threading issues.

Now, typing text is a whole other beast: polled interfaces are not meant for text input, which involves dead keys, uppercase/lowercase modifiers etc. I think the best solution is to add WinForms-like events to the GameWindow specifically for text input. The implementation is not trivial but, fortunately, Mono's WinForms implemetation is open source and offers everything we need.

On another note, the current KeyboardDevice/MouseDevice/JoystickDevice interfaces will remain to maintain compatibility with existing applications, so no need to worry.

james_lohr's picture

I agree 100% on your views of the two distinct use cases, and once again you've reassured me that you're fully in touch with the ideals of games development whilst also maintaining a sleek C# feel to the library. I just wanted to be sure that I would still be able to utilize the current KeyboardDevice events if I wanted to support cleaner-feeling text input for player-player chat, for example, which you have confirmed.

Once again, thanks for clearing this up.

Entropy's picture
the Fiddler wrote:
KeyboardState state = Keyboard.GetState();
if (state.IsKeyDown(Key.Space)) { doSomething(); }

I like this. Another move closer to XNA's design model for easier migration to OpenTK, and much simpler to check for new keypresses.