Handling repeat rate on KeyPress

I want to be able to hold down a key or combination of keys and have it fire repeatedly. I've tried keyPressed as well as keyTyped and they only seem to register the initial event. I then tried keyIsDown() and it's very difficult to get a consistent repeat rate contingent upon the current frameRate. I also tried to throttle the repeat rate by only polling it every n frames but this is inconsistent as well. What's the best way to handle this?

keyPressed=function(){

  switch(true){

    /*  Function Keys                                                   */
    case keyCode===KEYCODES.UP:         colUp();      break;

...

  }
Tagged:

Answers

  • edited December 2017

    Edit: Note that this is a p5 Java example -- the API is the same but the event model is actually different in JavaScript (p5.js).

    One approach that I have found generalized and reliable:

    1. set a flag on an initial key(s) pressed event
    2. clear the flag on key(s) up event

    While the flag is set, fire at whatever rate you want.

    One way to efficiently store the data for that approach is

    HashMap<Character,Boolean> keystate = new HashMap<Character,Boolean>();
    

    Then on keyPressed and keyReleased events you can use:

    keystate.put(key, Boolean.TRUE);
    keystate.put(key, Boolean.FALSE);
    

    Now for any combination of keys, you can check the key state.

    Here is an old sketch -- it doesn't use keypressed / keyreleased, but only flips states based on taps (a full release). But it gives you the idea. Try loading it up and pressing "qwerty" all at once as quickly as you can, and you can see it go into the map. You could also do this as a class.

    /**
     * Keypress Flags with HashMap
     * -- press 1, 2, 3 to flip each between true/false.
     * 2016-09-29 Jeremy Douglass
     * Processing 3.2.1
     * https:// forum.processing.org/two/discussion/18261/how-to-choose-a-movie-file-to-play
     */
    
    import java.util.Map;
    HashMap<Character,Boolean> flags = new HashMap<Character,Boolean>();
    String flagList = "";
    String flagState = "";
    
    
    void setup(){
      //// pre-populate hashmap with lower-case alphabet
      for(int i=97; i<123; i++){ // 90/91
        flags.put((char)i, Boolean.FALSE);
        flagList = flagList + (char)i;
      }
      noLoop();
    }
    
    void keyReleased(){
      if(flags.get(key)!=null){
        flags.put(key, !(flags.get(key)));
      } else {
        flags.put(key, Boolean.TRUE);
        flagList = flagList + key;
      }
      redraw();
    }
    
    void draw(){
      flagState = "";
      for (Map.Entry me : flags.entrySet()) {
        if(me.getValue()==Boolean.TRUE){
          flagState = flagState + (char)(me.getKey());
        } else {
          flagState = flagState + '.';
        }
      }
      println(flagList);
      println(flagState);
    }
    
    //// Note testing the value is against Boolean.TRUE
    //// rather than simple "==true" or "!" tests, because
    //// Map.Entry.entrySet().getValue() returns Object, not a boolean.
    
  • Thanks. It's a nice solution (I've used something similar using an array) but it still doesn't answer how to get a consistent keypress rate while polling the frameCount. Unless I'm missing something? I'm using frameCount%5 or some such and it consistently skips or gives me doubles. Any thoughts?

    TIA

  • edited December 2017

    it still doesn't answer how to get a consistent keypress rate while polling the frameCount

    I don't understand what "polling" means. Could you explain more and perhaps share a concrete example of an MCVE showing the problem that you are having?

    It sounds like you are saying that you are doing something like this:

    boolean myKey = true;
    void setup(){
       frameRate(4);
    }
    void draw(){
      if(frameCount%2==0){
        print(frameCount);
        println(":key!");
      }
    }
    

    ...but that your output is dropping frames, and so you are missing hits from e.g. this list?

    2:key!
    4:key!
    6:key!
    8:key!
    10:key!
    12:key!
    14:key!
    

    ...that would be surprising, because as far as I know Processing does not drop frames. Each frame takes as long as it takes, and it then increments by 1 and proceeds to the next one. Also, there isn't a race condition between key events and frames -- all key events are processed before the next frame starts, so if you so any variables updated by key events should be in their new state at the beginning of the next frame.

  • ...scratch ALL of that -- I just realized that this is a p5.js question, not p5 Java. I defer to people more familiar with the JavaScript event model!

  • Answer ✓

    Keyboard repeat rate is not consistent across devices and frame rate is not consistent either; so you might be better off handling repetition yourself. On key down just use a timer to trigger repetition of the key action and stop it on key up.

  • I think you're right. Everything I've read leads me to the same conclusion. Thanks

Sign In or Register to comment.