Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

In my games update loop, which happens on a fixed timestep of 30 times per second, I am checking the current state of several keys to determine how to move the player. For example:

if(Keyboard.down(KEY_LEFT) { /* move character left */ }

This works fine in situations where I expect the player to hold the key down for extended periods (such as movement), but for things like shooting, where the player just taps the key quickly, it doesn't always pick up the key being down.

This is due to the key being both pressed and released in the gap between polling.

Now obviously I don't want to trigger my shooting code 30 times a second like I do my moving code, so the solution needs to incorporate some kind of buffering/delay mechanism to make sure each shot is still x milliseconds apart, but I need the first one to register immediately and with certainty.

I am using Javascript, so I can easily use event driven functions linked to keypresses (in fact I am doing this already in order to keep track of which keys are down), but I didn't want to have game logic outside of the fixed timestep update.

Can anyone suggest a solution? I'm not looking for code, more an idea of how to design/structure the code around the problem.

share|improve this question
    
This is dependent on your platform/language/API, as most offer buffered input in some form. Could you edit your post to specify what you're using? –  Kylotan Dec 31 '11 at 13:39
1  
You should not polling user input at all! Your main loop is the place where you see what happened. UI is managed by interrupts and events. –  FxIII Dec 31 '11 at 16:46
    
@FxIII Like Kylotan said, it depends on the API. For instance, the standard XNA input API is entirely polled, so not much you can do there (except for using workarounds such as intercepting the window's messages directly, or another API entirely). Also, the main loop is typically divided into the Update phase and Render phase (which may run at different rates). The place where you see what happened is the Render phase, not the entire game loop. In fact, in a game it's a good idea to limit most of the processing to the Update phase. Handling the events directly brings unpredictability. –  David Gouveia Jan 1 '12 at 17:05
    
@DavidGouveia My opinion remains the same: polling the user input is unaffordable solution (for every non trivial projects) –  FxIII Jan 1 '12 at 22:48
    
@FxIII - if the API only offers polled input, then you have no choice, and if it wasn't affordable the API would be useless. (Luckily there is very little difference in terms of performance, because it's usually just a different interface onto the same data these days.) –  Kylotan Jan 2 '12 at 18:48

4 Answers 4

up vote 4 down vote accepted

Use the event driven API already provided for you, but instead of executing the events immediatly, queue and batch them to be executed in the next update iteration.

Here's an example in C# which I suppose you'll be capable of adapting to Javascript. In this case all events will be executed during the next frame after they were created:

var keyEventQueue = new List<Pair<object, KeyEventArgs>>();

private void OnKeyPress(object sender, KeyEventArgs e)
{
    keyEventQueue.Add(new Pair(sender, e));
}

public void Update(float elapsed)
{
    foreach(var evt in keyEventQueue) 
    {
        // Handle evt
    }
    eventQueue.Clear();
}

But if you'd like to ensure that there's at least one frame of delay between each event being handled, you simply need to change the Update method to:

public void Update(float elapsed)
{
    if(keyEventQueue.Count > 0)
    {
        var evt = keyEventQueue[0];
        // Handle evt
        keyEventQueue.RemoveAt(0);
    }
}
share|improve this answer
    
Luckily Javascript can handle this a lot easier. The problem with the keyEventQueue is that you'd have to process those events much more compact maybe causing runtime conditions, and you'd have to deal with other time issues because the bullets would be fired at the same time and not when they happen like in my example. –  Thomas R. Koll Dec 31 '11 at 16:20
    
@ThomasR.Koll Maybe you didn't catch my edit, but it's trivial to change so that only one event is processed per frame (while still doing the detection at an higher resolution which is what he wants). –  David Gouveia Dec 31 '11 at 16:25
    
@ThomasR.Koll Also it has nothing to do with it being Javascript or not. I simply used C# because it's the one whose syntax and API I have clearer in my memory. The idea still remains for Javascript - make a copy of the state of your event and store it to be handled later when it's more appropriate (i.e. in the update portion of the game loop). –  David Gouveia Dec 31 '11 at 16:35
    
I think this is pretty much the answer is was looking for, or something similar anyway. I need to just register the events as they happen, and then deal with them however I like within the loop. –  beeglebug Dec 31 '11 at 19:00

That's the reason why APIs usually export things like KeyDown and KeyUp (or KeyPressed / KeyReleased). That way you don't check if a key is currently pressed, but its derivative. You then bind the action "Fire" to "KeyPressed", and the problem doesn't even exist.

share|improve this answer
    
I understand that, but thought it would be bad to have separate chunks of game logic inside events which could trigger at any time, rather than keeping it all inside the main update function. Different systems trigger events at different speeds, so any logic inside events could happen at unknown intervals, rather than a consistent 30 times per second. –  beeglebug Dec 31 '11 at 14:21
    
@beeglebug: You could, as David says, just set a "key_pressed=true" on the keyPress event, then just handle it in your mainloop. What I meant is that you don't want to know if a key is down, you want to know if it has been pressed. See the difference ? –  Ravachol Dec 31 '11 at 15:48
    
@Ravachol Sorry, I deleted my comment because I thought it needed more explanation. But I don't think you're targeting beeglebug's main question which is about having game logic running outside the fixed timestep update though. –  David Gouveia Dec 31 '11 at 15:57

What's you problem with having that little firing code not being in you timestep update? I'd model my gamestate object to have a firePressed function that I call from the keyPressed event handler, that firePressed will create a new bullet or whatever it is and in the game's timesteop update I'd only do stuff like expiring bullets wether the hit something or went outside the viewfield.

share|improve this answer
    
What you suggest is no different from handling the event directly in the event handler. There's just one extra level of indirection in that method call. And maybe in a simple bullet case there would be no problems with having them spawned outside of the update loop's timeframe, but in general that might not be true. It's a matter of synchronization and predictability, and keeping all game processing inside the game loop. –  David Gouveia Dec 31 '11 at 16:27

to get a pattern like that:

if (keyDown[KEYCODE_ENTER]) ...

you should attach event handlers for keydown and keyup. the event handlers respectively set/unset a property on an object (keyDown in the following example):

var keyDown = {};
document.attachEventHandler('keydown', function(event) {
    keyDown[ev.keyCode || ev.which] = true;
}, false);
document.attachEventHandler('keyup', function(event) {
    keyDown[ev.keyCode || ev.which] = false;
}, false);
share|improve this answer
    
Won't do what he asked. What if the player presses and releases a button twice before the frame is reached? By the time the update runs the only thing it will know is that the key is currently up. It won't tell him how many presses happened. –  David Gouveia Jan 1 '12 at 13:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.