can I go without events?

Started by
9 comments, last by DracoLacertae 10 years, 1 month ago


i wonder if in winapi win32 game programming I can go without events

May I ask: Why would you do that?

Im not sure if i would or not, just wonder - in some cases polling

could be easier than event servicing (when you poll all you polling routines are independant each another, when you service an event you must wrote some call trees or lists to service them)

Advertisement

The problem with polling is you can miss something. If the user presses and releases a key before you get around to polling again, you will completely miss it. Mouse clicks are fast, and if your frame rate stalls for whatever reason (not even your program's fault; maybe the dreaded McAfee just started to do an update in the background...) and you're out of luck.

I get that you want to just poll to check if a key is pressed in your code. I like to do that too. But it's easy to do with events: (Rough pseudocode; haven't done win32 events in a while:


bool keystate[MAX_KEY];
bool mousebutton[MAX_MOUSEBUTTON];
int  mousex, mousey;

#define MOUSE_LEFT 0
#define MOUSE_RIGHT 1


eventhandler(...) 
{


  while (event = getnextevent(...)) 
  {

    if(event==KEY_DOWN && keycode < MAX_KEY)
        keystate[keycode] = 1;

    if(event==KEY_UP && keycode < MAX_KEY)
        keystate[keycode] = 0;

    if (event==L_MOUSE_DOWN)
         mousebutton[MOUSE_LEFT] = 1;
    
    if (event==L_MOUSE_UP)
         mousebutton[MOUSE_LEFT] = 0;

    if (event==MOUSE_MOVE)
    {  
        mousex = event_mouse_x;
        mousey = event_mouse_y;
    }
  }
}

Then, in your code you can do if(keystate[KEY_L_CTRL]) all you want. Or sprite.draw(mousex, mousey).

The above could still suffer from missing presses. If the system slows down, and you call the event loop once per frame, if a keydown AND a keyup press bunch up against each other, then in one frame's processing of events you would set, and unset a single key in the same call to the loop. One way to do that is handle the player's inputs in the event handler itself:


   if(event==KEY_DOWN && keycode < MAX_KEY)
   {
        keystate[keycode] = 1;
  
        if (keycode == playerconfig.firekey)
            player.fire=1;

        if (keycode == playerconfig.jump)
            player.jump= 1;
   }

This way the event loop is looking for particular events, and sets those flags in the player object. Each frame, after the event handler is run, when the player's control code is run, it checks it's action flags and does the appropriate action. This way you can't miss a jump or fire event, even if the next event is the queue is releasing the button.

You can abstract that completely and do this:



while (event = getnextevent(...))
{
    memset(keypressed, 0, sizeof(keypressed)); //clear pressed events

    if(event==KEY_DOWN && keycode < MAX_KEY)
    {
        keystate[keycode] = 1;
        keypressed[keycode] = 1;
    }

    if(event==KEY_UP && keycode < MAX_KEY)
        keystate[keycode] = 0;

}

Now, you can't miss an event. If the key was pressed at all since the last frame, keypressed will be '1'. And if its still pressed keystate will be '1'. So now anywhere in your game loop you can do this:


if (keypressed[ player_config.fire_key] || ( keystate[player_config.fire_key] && (this_frame - last_fire_frame >= 20)) 
{
    last_fire_frame = this_frame;
    fire_a_bullet();
}

What this does is fire a bullet every time the player presses the fire key OR if the player holds down the fire key, one bullet every 20 frames.

Also this is all assume a single main loop thread, that looks something like this:


while(not_quitting)
{
   handle_window_events();
   increment_game_logic();
   draw();
}




so say i got a 5 seconds lag in my app and user types the tekst

(say 30 keystrokes) will it be queued in windows and then flushed as a set of 30 keydowns ?
Yes, even a bit more than that. You will get roughly 30 key down and key up events, plus roughly 30 char events.

By counting down and up events, you know which keys are still down (for things like game controls). By handling character events, you know what the user has typed (in terms of "text").

I used the wording "roughly" because one key press does not necessarily correspond to a virtual key (usually it does, but the weird AltGr key to the right of the space key for example sends two key-downs and two key-ups for every press). Further, keys and characters are different things. Several keys pressed can result in a single character, for example Shift+a will result in A, and ´+e will result in é whereas Shift+´+e will result in è, but Shift+´+Shift+e will give È.

This is extremely complicated to a point where it gets annoying and unmanageable (especially in some non-roman languages where ligatures are mandatory and completely different glyphs). The nice thing is, you don't need to care because what comes out on the WM_CHAR end is already correct, human-readable characters in whatever language the user has configured (which you maybe don't even can't pronounce).

Using WM_CHAR will save you a lot of trouble. If you are going to interpret the keystrokes as text (player is entering their name), you should use WM_CHAR. For player actions, use WM_KEYDOWN/UP. Pressing, holding and releasing a key will generate many events. If you press and hold shift-A you will get this:

shift keydown

a keydown

capital A char (repeated several times)

capital A char (repeated several times)

capital A char (repeated several times)

a key up

shift key up

This topic is closed to new replies.

Advertisement