Results 1 to 15 of 15
Like Tree1Likes
  • 1 Post By pbrockway2

Thread: Moving animage; KeyListener simple question

  1. #1
    CuppaCoffee's Avatar
    CuppaCoffee is offline Member
    Join Date
    Jan 2012
    Location
    Canada
    Posts
    80
    Rep Power
    0

    Default Moving animage; KeyListener simple question

    Just as the title says, I'm working on a program that moves an image with KeyListener. It moves left and it moves right (VK_LEFT) and (VK_RIGHT). I'm sure you're all familiar with listeners, but when i move left or right, it moves once, for a second, then continues to move flatout when I hold the button. (Just like if you're typing and hold a letter, it will type it out once for a brief second, then continue to type it multiple times if you hold it long enough). I want it to be that I don't have to wait that brief second so it can move continuously the moment I hold the key. Anyone know how I can do this?

  2. #2
    DarrylBurke's Avatar
    DarrylBurke is offline Member
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,189
    Rep Power
    19

    Default Re: Moving animage; KeyListener simple question

    You could start an animation loop/Timer in keyPressed and stop it in keyReleased. But be aware that the sequence of key events when a key is held down is platform dependent and not the same in Windows and Linux.

    And since this pertains to AWT/Swing, I'm moving it to the correct place.

    db
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  3. #3
    CuppaCoffee's Avatar
    CuppaCoffee is offline Member
    Join Date
    Jan 2012
    Location
    Canada
    Posts
    80
    Rep Power
    0

    Default Re: Moving animage; KeyListener simple question

    Whenever I use a loop in keyPressed and try to move on the actual program, the whole thing just freezes... I've tried for loops and while loops, for example;

    Java Code:
    private boolean press = false;
    
    //......
    
    public void keyPressed (KeyEvent e)
      {
        press = true;
        while (press = true)
        {
        if (e.getKeyCode() == KeyEvent.VK_RIGHT)
        {
          p1.moveHorizontal(10);
        }
          
        else if (e.getKeyCode() == KeyEvent.VK_LEFT)
        {
          p1.moveVertical(-10);  
        }
        }
        
      }
      
      public void keyTyped (KeyEvent e) {}
      public void keyReleased (KeyEvent e) 
      {
        press = false;
      }
    Or am I doing it incorrectly? I'm currently using Windows 7.. If what you said about the differentiation with Windows/Linux is true, how could I make this work on Windows?

  4. #4
    pbrockway2 is offline Moderator
    Join Date
    Feb 2009
    Location
    New Zealand
    Posts
    4,565
    Rep Power
    12

    Default Re: Moving animage; KeyListener simple question

    The problem is that keyPressed() will be called on the event dispatch thread. Think of this as a thread where the runtime system is constantly shouting things to your program and giving you a chance to respond: "the key just went down", "the mouse was moved 1pixel to the left", "the window is about to be closed", "the mouse is being dragged now"... The important thing is that these things and your responses happen one at a time. So keyPressed() is called and you won't get another message until keyPressed() has completely finished.

    It is very important, therefore, that events like keyPressed() execute quickly and return control to avoid the freezing that you're seeing. (It appears visually as freezing because no painting will be done while your keyPressed() is still hogging the event dispatch thread.)

    You might be wondering how anything continuous can be done this way. That's why DarrylBurke mentioned using a timer.

    The idea is this:

    * You respond to keyPressed() by starting a timer. That's all: just start it and return. The timer will now fire its own events on the event dispatch thread at a rate you predetermine
    * You respond to timer events by doing one frame of your animation
    * You respond to keyReleased() by stopping the timer

    Each response occurs on this event dispatch thread, each is very quick. Continuous animation appears as a result, but it's something of an illusion because between each timer event there will still be time for the mouse to move and for the system (and your program) to respond to other things.

    -----

    Oracle's Tutorial has a discussion of an example on the How to Use Swing Timers page.

  5. #5
    CuppaCoffee's Avatar
    CuppaCoffee is offline Member
    Join Date
    Jan 2012
    Location
    Canada
    Posts
    80
    Rep Power
    0

    Default Re: Moving animage; KeyListener simple question

    A timer works with an actionPerformed method, right? Is there any way I can make more than one 'actionPerformed', if I have more than one timer that both have different tasks?
    Last edited by CuppaCoffee; 01-11-2012 at 11:09 PM.

  6. #6
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    25

  7. #7
    pbrockway2 is offline Moderator
    Join Date
    Feb 2009
    Location
    New Zealand
    Posts
    4,565
    Rep Power
    12

    Default Re: Moving animage; KeyListener simple question

    Quote Originally Posted by CuppaCoffee View Post
    A timer works with an actionPerformed method, right? Is there any way I can make more than one 'actionPerformed', if I have more than one timer that both have different tasks?
    Yes and yes.

    The example in the Tutorial is a bit "lazy" in that it makes the TumbleItem applet implement ActionListener and just uses its actionPerformed() to do the animation. It is often more convenient to define the ActionListener instance as an instance variable. This way you can have numerous action listeners each doing their own thing: multiple animations or whatever.

    Java Code:
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    
    
    public class MultipleTimers extends JPanel {
        
        public MultipleTimers() {
            add(new JLabel("This panel intentionally left blank"));
            Timer timer = new Timer(300, fastAnimator);
            timer.start();
            timer = new Timer(1000, slowAnimator);
            timer.start();
        }
        
        private ActionListener fastAnimator = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                System.out.println("FAST animation here");
            }
        };
        
        private ActionListener slowAnimator = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                System.out.println(">>> SLOW animation here");
            }
        };
        
        public static void main(String[] args) {
                // here we also create the gui on the event dispatch thread
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame("Timer eg");
                    frame.add(new MultipleTimers());
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
    }

  8. #8
    CuppaCoffee's Avatar
    CuppaCoffee is offline Member
    Join Date
    Jan 2012
    Location
    Canada
    Posts
    80
    Rep Power
    0

    Default Re: Moving animage; KeyListener simple question

    Thanks for the example, I know how to use more than one timer now. The final problem remaining is how to get keyPressed, keyTyped, and keyReleased methods to work inside those ActionListeners..

  9. #9
    pbrockway2 is offline Moderator
    Join Date
    Feb 2009
    Location
    New Zealand
    Posts
    4,565
    Rep Power
    12

    Default Re: Moving animage; KeyListener simple question

    No keyPressed() and keyReleased() aren't within those action listeners. They are methods of a distinct KeyListener that responds to key events. keyPressed() should (maybe create and) start the timer, keyReleased() should stop it.

  10. #10
    CuppaCoffee's Avatar
    CuppaCoffee is offline Member
    Join Date
    Jan 2012
    Location
    Canada
    Posts
    80
    Rep Power
    0

    Default Re: Moving animage; KeyListener simple question

    Ah, you're right! I seem to have gotten it to work, with a little alterations. What you said was correct; keyPressed() is only meant to start the timer. And that specific timer is supposed to do the duties. The thing is though, one timer can't move left and right at the same time, and I can't put if statements for e.KeyCode() inside of it. So, my conclusion was two timers. One for moving left and one for moving right, and it works great.

    Here's a snip of the final code;

    Java Code:
    private Timer t2, t3;
    
    //...
    
    public ~~~()
    {
    t2 = new Timer(20, right);
    t3 = new Timer(20, left);
    }
    
    public void keyPressed (KeyEvent e)
      {
        
        if (e.getKeyCode() == KeyEvent.VK_RIGHT)
        {
          t2.start();
          
        }
          
        else if (e.getKeyCode() == KeyEvent.VK_LEFT)
        {
          t3.start();
            
        }
        
      }
      
      public void keyTyped (KeyEvent e) {}
      public void keyReleased (KeyEvent e)
      {
        t2.stop();
        t3.stop();
      }
      
      private ActionListener right = new ActionListener()
      {
        public void actionPerformed (ActionEvent e)
        {
          p1.moveHorizontal(10); // calling the class that moves object right
        }
      };
      
      private ActionListener left = new ActionListener()
      {
        public void actionPerformed (ActionEvent e)
        {
          p1.moveVertical(-10); // calling the class that moves object left
        }
      };
    Thank you very much for all the help. My only issue (not so much an issue but a concern) is that when I'm holding the left key the object moves right, but I have to completely let go of the key and then press the right key to instantly move right continuously. When playing a game, for example, users don't really concentrate on this, and would press left when wanting to go left, then just press right (while still holding left, for the moment), and the object will dominantly move right. Is there a solution to that issue?

  11. #11
    pbrockway2 is offline Moderator
    Join Date
    Feb 2009
    Location
    New Zealand
    Posts
    4,565
    Rep Power
    12

    Default Re: Moving animage; KeyListener simple question

    The way you want things there is really only one animation going on, which is either left or right. I never quite know what to call these instances of anonymous inner classes (as Fubarable described them). Above I called them fastAnimator and slowAnimator because I had in my mind that the ActionListener actually does more than listen: in some sense it is the animator.

    A bit of a preamble... but what I am getting at is that since you only have one animation, you only need one animator: one anonymous inner class instance. Modify the logic just slightly:

    * keyPressed() looks at what key was pressed and updates some instance variables so that the animator will later know what to do. Then it starts the timer (I'm going with the idea that there is only one timer).
    * actionPerformed() in the ActionListener (aka animator) looks at the instance variables to figure out what to do. And does it
    * keyReleased() looks at what key was pressed, updates the instance variables, and *possibly* turns off the timer

    I am pretty sure you can start a timer multiple times with no ill effects - ie starting it a second time does nothing. But you might like to check the API docs.

    I don't play games, but I would guess that most game players "just do it". You, as programmer, have to step back and determine the precise semantics of key presses and key releases. With just two possibilities (left/right) the following elaboration of the above might be what you want. With more possibilities things get rather more complex:

    * have 3 instance variables: leftDown, rightDown, and currentDirection. The first two might be booleans, the last one -1,0,1 == left,stopped,right
    * keyPressed() sets the appropriate xxxxDown to true and sets the value of currentDirection (then starts the timer)
    * actionPerformed() just checks currentDirection and does the right thing.
    * keyReleased() sets the appropriate xxxxDown to false. Then it checks the value of the other xxxxDown. If that other one is true, then currentDirection is updated and it is done. If the other one is false then currentDirection is set to zero/stopped and the timer is turned off.

    A variation on this is to have actionPerformed() itself be responsible for turning the timer off when it is called and finds that currentDirection is "stopped".

    The intention of the above is for the most recent "key down" to win. Autorepeating keys may screw things up however by sending the program repeating left/right/left/right/... messages. In that case it might be enough for keyPressed() to do absolutely nothing if the corresponding xxxxDown is already true.

    (I hope the above makes some sort of sense.)
    Last edited by pbrockway2; 01-12-2012 at 02:15 AM.

  12. #12
    CuppaCoffee's Avatar
    CuppaCoffee is offline Member
    Join Date
    Jan 2012
    Location
    Canada
    Posts
    80
    Rep Power
    0

    Default Re: Moving animage; KeyListener simple question

    Quote Originally Posted by pbrockway2 View Post
    I don't play games, but I would guess that most game players "just do it". You, as programmer, have to step back and determine the precise semantics of key presses and key releases. With just two possibilities (left/right) the following elaboration of the above might be what you want. With more possibilities things get rather more complex:

    * have 3 instance variables: leftDown, rightDown, and currentDirection. The first two might be booleans, the last one -1,0,1 == left,stopped,right
    * keyPressed() sets the appropriate xxxxDown to true and sets the value of currentDirection (then starts the timer)
    * actionPerformed() just checks currentDirection and does the right thing.
    * keyReleased() sets the appropriate xxxxDown to false. Then it checks the value of the other xxxxDown. If that other one is true, then currentDirection is updated and it is done. If the other one is false then currentDirection is set to zero/stopped and the timer is turned off.

    A variation on this is to have actionPerformed() itself be responsible for turning the timer off when it is called and finds that currentDirection is "stopped".

    The intention of the above is for the most recent "key down" to win. Autorepeating keys may screw things up however by sending the program repeating left/right/left/right/... messages. In that case it might be enough for keyPressed() to do absolutely nothing if the corresponding xxxxDown is already true.

    (I hope the above makes some sort of sense.)
    I don't quite understand what you mean by this.. Could you make an example out of my code or could you provide a short example to help me get an idea of what you're explaining? (also, sorry for the late reply)

  13. #13
    pbrockway2 is offline Moderator
    Join Date
    Feb 2009
    Location
    New Zealand
    Posts
    4,565
    Rep Power
    12

    Default Re: Moving animage; KeyListener simple question

    Well there were a lot of if's and maybe's in my post - because you have to decide exactly what you want key presses/releases to do. But here's a component that implements a simple last-of-two-keys-wins strategy for deciding which direction a small object should move:

    Java Code:
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    
    
    public class LeftRight extends JComponent {
        
            /** Current x-position of the dot. */
        private int position = 130;
            /** Current x-direction of the dot. TODO: enum? */
        private int direction;
            /** True iff the left key is currently depressed. */
        private boolean leftDown;
            /** True iff the right key is currently depressed. */
        private boolean rightDown;
        
        private Timer timer;
        
        
        public LeftRight() {
            setFocusable(true);
            addKeyListener(animator);
            timer = new Timer(50, ticker);
        }
        
        private KeyListener animator = new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent ke) {
                    // update key state variables
                switch(ke.getKeyCode()) {
                    case KeyEvent.VK_LEFT:
                        leftDown = false;
                        break;
                    case KeyEvent.VK_RIGHT:
                        rightDown = false;
                        break;
                    // up/down etc
                    default:
                        return;
                }
                    // update direction: nb at most one key is pressed at this point
                    // (this is the bit that gets tricky when >2 keys might be in play)
                if(leftDown) {
                    direction = -1;
                } else if(rightDown) {
                    direction = 1;
                } else {
                    direction = 0;
                }
            }
            
            @Override
            public void keyPressed(KeyEvent ke) {
                int code = ke.getKeyCode();
                if(code != KeyEvent.VK_LEFT && code != KeyEvent.VK_RIGHT) {
                    return;
                }
                    // deal with keyboard repeats by ignoring them
                if((code == KeyEvent.VK_LEFT && leftDown) || (code == KeyEvent.VK_RIGHT && rightDown)) {
                    return;
                }
                //**/System.out.println("Pressed: " + code);
                    // update down/direction instance variables
                    // this is the code that allows the last depressed key to win
                if(code == KeyEvent.VK_LEFT) {
                    leftDown = true;
                    direction = -1;
                } else if(code == KeyEvent.VK_RIGHT) {
                    rightDown = true;
                    direction = 1;
                }
                    // (re)start the timer
                timer.start();
            }
        };
        
        private ActionListener ticker = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                //**/System.out.println("tick! direction=" + direction);
                switch(direction) {
                    case 1: 
                        if(++position > 200) {
                            position = 200;
                        }
                        break;
                    case -1: 
                        if(--position < 60) {
                            position = 60;
                        }
                        break;
                    default:
                        //**/System.out.println("Stopping timer");
                        timer.stop();
                        break;
                }
                repaint();
            }
        };
        
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawOval(position, 70, 20, 20);
        }
        
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame("Left, right animator");
                    frame.add(new LeftRight());
                    frame.setSize(300, 200);
                    frame.setLocationRelativeTo(null);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setVisible(true);
                }
            });
        }
    }
    [Edit] I've gone off the idea of regarding the key listener as an animator. That's more what the timer's event handler does. The key listener is more of a "direction decider". So I would probably change the variable names for these.
    Last edited by pbrockway2; 01-15-2012 at 02:09 AM.
    CuppaCoffee likes this.

  14. #14
    CuppaCoffee's Avatar
    CuppaCoffee is offline Member
    Join Date
    Jan 2012
    Location
    Canada
    Posts
    80
    Rep Power
    0

    Default Re: Moving animage; KeyListener simple question

    Ah, that's a great example. I followed something like your code and I got my program to work just the way I want it to. Though the code is a bit different from what I'm familiar with, I generally understand how to get it working. I've also changed it to using one timer for left/right and using those booleans to allow the program to figure out which way I'm going. Thanks a great bunch for the help!

  15. #15
    pbrockway2 is offline Moderator
    Join Date
    Feb 2009
    Location
    New Zealand
    Posts
    4,565
    Rep Power
    12

    Default Re: Moving animage; KeyListener simple question

    You're welcome.

    If you've manage to replace the two timers with one, then you've probably understood the logic of my example. The rest is just practice to gain familiarity with the "pattern" of the logic - which is to use the state of the instance (the booleans etc) as a way of communicating things to the timer so it does the right thing when the runtime calls it.

Similar Threads

  1. Replies: 4
    Last Post: 12-03-2011, 04:38 PM
  2. A simple question
    By chris.bos in forum New To Java
    Replies: 11
    Last Post: 12-02-2011, 05:37 PM
  3. Simple question...
    By Onyx in forum New To Java
    Replies: 10
    Last Post: 08-10-2011, 10:44 PM
  4. Simple Question
    By barusk in forum Networking
    Replies: 13
    Last Post: 03-04-2009, 07:33 PM
  5. Probably a really simple question...
    By ibanez270dx in forum New To Java
    Replies: 0
    Last Post: 11-16-2007, 01:27 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •