Hello,

I'm creating a game and in this game if you press two particular buttons at once, a particular move is done. How do I go about doing this with KeyListener? Is there a way to get it to wait for a fraction of a second to see if there's another button press?

Recommended Answers

All 13 Replies

Well, it won't wait but you can keep track of it for yourself with something like this

// class level
long lastKeyPress = System.currentTimeMillis();
int lastKey=0;
// whichever keys you need
final static int KEY_1 = KeyEvent.VK_A;  
final static int KEY_2 = KeyEvent.VK_B;
// whatever interval you need for "at the same time"
// 50ms seems to work ok
final static int TIME_LIMIT = 50;

// wherever you set up the KeyListener
component.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e){
                if (System.currentTimeMillis() - lastKeyPress < TIME_LIMIT && ((lastKey|e.getKeyCode())==(KEY_2|KEY_1)) ){
                    // within time limit
                    doSpecial();                        
                }

                lastKeyPress = System.currentTimeMillis();
                lastKey = e.getKeyCode();
            }        
        });

Well that kind of does the job, but there's one issue. So lets say a and b are the keys I'm looking at. The issue comes in when each button has a job. I want each to do their job when individually pressed, but when pressed together do a different job without ever doing the individual ones. So if a is pressed before b but within the given time, I don't want job 'a' to take place before 'a'+'b' , but at same time I want a to take place if 'a'+'b' was NOT pressed in time.

Let's say I create a class called ButtonInterpretation and it recieves the key press and time. I want something where when a is pressed it will suspend for the given time and if b is pressed in the given time it will interupt the suspend and return a+b otherwise after the given time a will be returned and it will suspend the next a or b and repeat process.

So my question is there a suspend function or way to suspend that has a way to be interrupted and does not suspend the entire program.

The way it's going to work is the KeyListener will send the key and time to my Character class that has a KeyInterpreter in it. The information will be sent to KeyInterpreter. I want to avoid preventing any method in my character class that doesn't deal with KeyInterpreter to continue functioning. Do I have to deal with threads?

I'd say yes, you're probably going to have to deal with threads for that. It sounds like you have a decent handle on how to proceed with it via the KeyInterpreter. If you queue the received keys and process them in a timer event you can dispatch to the appropriate actions. Of course, tuning that dispatch to prevent lagging of the individual A and B actions or allowing the transitions such as A to AB or B to AB may be the tricky part.

It sounds like the transition states are what you are wanting to avoid, so I would think if you keep the timer event short enough you can dispatch to the appropriate method based on the queued keys.

Well acctualy I'm clueless with threads, I just happen to be using one off a tutorial for my animation. I guess at this point I just have to do some research on threads. By the way do you think 50 is faster than humanly possible to double tap a key?

If you have the time, mind answering this question...

I was following this example: ( http://www.javaworld.com/jw-03-1996/animation/Example1Applet.html ) and what confuses me is the start function. Being that the new thread is "this" and in the start function, start is called in the new thread; how's that not a looping effect?

Well acctualy I'm clueless with threads, I just happen to be using one off a tutorial for my animation. I guess at this point I just have to do some research on threads. By the way do you think 50 is faster than humanly possible to double tap a key?

Not sure, System.out.println(System.currentTimeMillis() - lastTime) is a decent way to find out though.

If you have the time, mind answering this question...

I was following this example: ( http://www.javaworld.com/jw-03-1996/animation/Example1Applet.html ) and what confuses me is the start function. Being that the new thread is "this" and in the start function, start is called in the new thread; how's that not a looping effect?

Yes,I can see where it would be a bit confusing in that both threads and applets have start() methods. In the applet start() method, they are creating a new Thread with the constructor Thread(Runnable target) . Because they have defined the applet to implement the Runnable interface and provided a run() method in the class, they can pass the class object "this" to the Thread constructor. They then start that thread running with the animator.start() call. The fact that it was done in the applet start() method does make it a bit awkward to read if you aren't used to threads.

I was playing around with a timer-based key processor and I think you are going to have issues with key listener. Here is the processor I wrote

class KeyProcessor extends javax.swing.Timer {
    List keyQueue = new ArrayList();

    final static int KEY_1 = KeyEvent.VK_A;
    final static int KEY_2 = KeyEvent.VK_B;

    public KeyProcessor(int delay, ActionListener listener) {
        super(delay, null);
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                if (keyQueue.contains(KEY_1) && keyQueue.contains(KEY_2))
                    lblState.setText("State A+B");
                else if (keyQueue.contains(KEY_1))
                    lblState.setText("State A");
                else if (keyQueue.contains(KEY_2))
                    lblState.setText("State B");
                keyQueue.clear();
            }
        });
    }

I then started that up and attached a KeyListener to feed the key queue

keyProcessor = new KeyProcessor(200,null);
txtInput.addKeyListener(new KeyAdapter() {
    public void keyPressed(KeyEvent e){
        keyProcessor.queueKey(e.getKeyCode());
    }
});
keyProcessor.start();

It will process the A+B state fine just once if the key combo comes in within the time slice, but if the keys are held down then only the last key pressed gets repeated, so the state will change either A or B. Now, I am running this on a Windows XP machine so maybe it's a system specific thing with how repeated keypresses are handled, but it is obviously an issue on Windows.

I haven't had to do anything like this with KeyListeners before, so maybe there is some other technique to listen for both of those keys, but keyPressed() isn't receiving both key code.

It will process the A+B state fine just once if the key combo comes in within the time slice, but if the keys are held down then only the last key pressed gets repeated, so the state will change either A or B. Now, I am running this on a Windows XP machine so maybe it's a system specific thing with how repeated keypresses are handled, but it is obviously an issue on Windows.

I haven't had to do anything like this with KeyListeners before, so maybe there is some other technique to listen for both of those keys, but keyPressed() isn't receiving both key code.

How about release? I think I may have a solution if it reports release for both buttons (doesn't matter if it is or not at the same time).

How about if you double tap a b? Does it still detect a b at some point of time for both taps?

By the way I'm still reading up on threads.

hey Ezzaral if I call a method of a thread while it's sleeping, will it still function?

In other words if my KeyInterpreter object is sleeping can I still call a method of that particular object.

What am I doing wrong.... shouldn't main be on a different thread? It stops at "Sleeping", Shouldn't it continue.

public class Dem extends Thread
{
private int a;
public Dem(int in)
{
a = in;
}
public void run()
{
try
{
System.out.println("sleeping");
sleep(100000);
}
catch (InterruptedException e)
{
System.out.println("interupt");
}
System.out.println("done with Dem");
}
public int geta()
{
return a;
}
public void seta(int in)
{
a = in;
}
}


public class ThTstser
{

public static void main(String[] args)
{
Dem v = new Dem(4);
Thread f = new Thread(v);
System.out.println(v.geta());
f.run();
System.out.println("running");
System.out.println(v.geta());
System.out.println(v.geta());
System.out.println(v.geta());
v.seta(5);
System.out.println(v.geta());
System.out.println(v.geta());
System.out.println(v.geta());
System.out.println("tst done");
}
}

You will want to call the start() method to begin thread execution instead of calling run() directly. Start() sets up other necessary things and then calls run(), which is why the Runnable interface requires you to override run() to provide the actual "what am I supposed to do" part of the thread.

I did some more playing around with it this morning and this seems to work

class KeyProcessor extends javax.swing.Timer {
    
    final static int KEY_1 = KeyEvent.VK_A;
    final static int KEY_2 = KeyEvent.VK_B;
    
    Map<Integer,Boolean> keystate = new Hashtable<Integer,Boolean>();

    public KeyProcessor(int delay, ActionListener listener) {
        super(delay, null);
        keystate.put (KEY_1,false);
        keystate.put(KEY_2,false);
        
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                if (keystate.get(KEY_1)&&keystate.get(KEY_2))
                    lblState.setText("State A+B");
                else if (keystate.get(KEY_1))
                    lblState.setText("State A");
                else if (keystate.get(KEY_2))
                    lblState.setText("State B");
                else
                    lblState.setText("none");
            }
        });
    }
    
    public void setKeystate(int keycode, boolean pressed){
        keystate.put(keycode, pressed);
    }
    
}

// This part wherever you are setting up your key listener.
// 200ms seemed to work fine as a time interval
// but tune as you like
keyProcessor = new KeyProcessor(200,null);
txtInput.addKeyListener(new KeyAdapter() {
    public void keyPressed(KeyEvent e){
        keyProcessor.setKeystate(e.getKeyCode(),true);
    }
    public void keyReleased(KeyEvent e){
        keyProcessor.setKeystate(e.getKeyCode(),false);
    }
});
keyProcessor.start();

I got a quick question, if a thread finishes do I call start to repeat the thread?

Also how do I go about suspending a thread and then resuming it again, but doesn't involve time so sleep won't do the job. I noticed a lot of methods that look like they'll do the job are deprecated, so how do I go about it?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.