Java: How to detect if a key has been held down over a certain time without checking every loop
EDIT: Notice to others, premature optimisation is the root of all evil! Had I not been busily attempting to optimise every aspect of my code (it was a phase OK!) I'd have made a nice, readable program that could be maintained and understood by others.
Each loop (1/60th of a second) the Player class checks the Game class to see which key is down, If the player is not facing the right way it turns to face the right way, else it moves in that direction.
Sadly this means that if you want to turn and not move you have to tap the key for less than 2/60ths of a second (two loops). The code for checking which key is held down is below and a link to the full source code and a built example is below that.
public class Game extends RenderableObject
{
public static final byte NONE = 0;
public static final byte UP = 1;
public static final byte RIGHT = 2;
public static final byte DOWN = 3;
public static final byte LEFT = 4;
public byte key = NONE;
public Game()
{
}
@Override
public void keyPressed(KeyEvent ke)
{
if (!paused)
{
if (ke.getKeyCode() == 38) // '^ key'
{
key = UP;
return;
}
else if (ke.getKeyCode() == 40) // 'v key'
{
key = DOWN;
return;
}
if (ke.getKeyCode() == 37) // '< key'
{
key = LEFT;
return;
}
else if (ke.getKeyCode() == 39) // '> key'
{
key = RIGHT;
return;
}
}
}
@Override
public void keyReleased(KeyEvent ke)
{
if (ke.getKeyCode() == 38) // '^ key'
{
if (key == UP)
key = NONE;
return;
}
else if (ke.getKeyCode() == 40) // 'v key'
{
if (key == DOWN)
key = NONE;
return;
}
if (ke.getKeyCode() == 37) // '< key'
{
if (key == LEFT)
key = NONE;
return;
}
else if (ke.getKeyCode() == 39) // '> key'
{
if (key == RIGHT)
key = NONE;
return;
}
}
Link to full source code and built example: (well a link to the page with a link to the download anyway!
WARNING: I am a NOOB, if my naming conventions are off... please don't hurt me!
http://troygametwodee.blogspot.com/2011/09/latest-build-21092011.html
So I have toyed with a few ideas already, but none seem particularly gracefull, and all become very messy very quickly,
I'd ideally like to be able to check some sort of boolean in the game class so the player only moves in that direction when the bool is true, but I failed at that too :(
So if anyon开发者_StackOverflow中文版e has any suggestions I would be very happy :)
Thanks a bunch in advance
Ok, so how I solved the issue while keeping fluid motion possible:
First I added a boolean variable called "held". This on it's own was useless so I also added a numerical variable, called "timeHeld". Each time a key was pressed I reset "timeHeld" to zero and made "held" == false, and put the following code into my loop.
if (timeHeld < 20)
timeHeld++;
else
held = true;
So in my player class, when deciding which way to move it calls "Game.key" to asses which key is down, then if the "held" boolean is true (which it is after one third of a second) the player walks in that direction, Otherwise the player stayed where it is on screen and just changed the direction it was looking.
This worked to an extent, but each time I change direction the "held" variable was set to zero again. This means each time I turned I was delayed by a third of a second, very annoying!
So I did this:
if (ke.getKeyCode() == 38) // '^ key'
{
if (key == NONE)
{
held = false;
timeHeld = 0;
}
key = UP;
return;
}
this means that as long as an arrow key is down "held" remains true, so once the player gets moving there is no delay when turning a corner.
This worked absolutely perfectly as it was, so that is the answer, however I also added another variable. Because I only use my middle finger for both the UP and DOWN key while I play using the arrow or 'wasd' keys, I end up letting go of all of the keys when I switch from moving north to south. In this case with the code as described above I end up stopping for a third of a second SO to fix this I added a variable called "delay"
When a key is let go of, delay is set to 10 (loops), each loop I call:
if (delay > 0)
delay--;
then when a key is pressed I call:
if (ke.getKeyCode() == 38) // '^ key'
{
if (key == NONE && delay == 0)
{
held = false;
timeHeld = 0;
}
key = UP;
return;
}
This means that "held" is only ever set to false when a direction key is pressed and there were no other direction keys down at the time it was pressed AND delay is 0. this gives me a 10 loop delay between letting go of all the arrow keys and pressing another one before "held" is set to false.
Confused? I am! but it works, anyone else who has this problem can either follow the link to my blog, which should have the latest working source, or just ask me to explain the bits you don't get :D
A clearer description of what you are trying to achieve would help.
However, if I understand correctly, you could keep a short history of which key is down, e.g. keep the key value for the last five loops. Then you could execute certain actions if at least a certain number of the previous values match a particular key.
try holding a value object with boolean value for all of your actions/keypresses in it. The boolean will be true for keydown and false for keyup. When the key actions are handled you can then update the value object to true/false for the relevant key only.
Then when your game update loop runs you can check which key(s) are currently down.
精彩评论