0

So I have a key listener in Java that moves a rectangle on a JFrame either up, down, left, or right for which I am using the W, A, S, and D keys. Usually, and especially in games, pressing 2 keys should yeild diagonal movement. An example would be you press W and D at the same time, and the expected behavior is to move diagonally to the right upwards. However, the key listener only detects one key at a time. I know I can do something where I detect multiple keys at a time and then write new code as if it were a new key but is there a way to execute two pieces of code under two different keys at the same time? If not, how can I test multiple keys at a time?

Here is a minimal reproducible that sets up a rectangle that moves with the W, A, S, and D keys.

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class Test extends JPanel implements KeyListener {
    
    private int x = 200, y = 200;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new Test().create();
    }
    
    public void paintComponent(Graphics tool) {
        super.paintComponent(tool);
        tool.drawRect(x,  y, 50, 50);
    }
    
    public void create() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 500);
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        
        frame.add(this);
        frame.addKeyListener(this);
        
        frame.setVisible(true);
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // TODO Auto-generated method stub
        switch(e.getKeyCode()) {
        case KeyEvent.VK_W:
            y -= 3;
            break;
        case KeyEvent.VK_S:
            y += 3;
            break;
        case KeyEvent.VK_A:
            x -= 3;
            break;
        case KeyEvent.VK_D:
            x += 3;
            break;
        }
        repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub
        
    }

}

LuckyBandit74
  • 417
  • 1
  • 4
  • 10
  • Well, if a key gets pressed and not released it's still pressed, right? Just keep a bit of state. – Maarten Bodewes Apr 12 '21 at 22:37
  • @Maarten Bodewes Thanks for the reply. I am new to programming in general. How would I go about doing that? – LuckyBandit74 Apr 12 '21 at 22:38
  • Create a `Set` of `KeyEvent` integers, add an event when pressed, remove when released. And test for it when needed. – Maarten Bodewes Apr 12 '21 at 22:44
  • Does [this](https://stackoverflow.com/questions/18037576/how-do-i-check-if-the-user-is-pressing-a-key) maybe solve your problem? – Maarten Bodewes Apr 12 '21 at 22:55
  • @Maarten Bodewes I think so. So now that I can keep track of which keys I have, can I execute keys within my code? Much like how JButton.doClick() works. – LuckyBandit74 Apr 12 '21 at 23:00
  • A Java enum / EnumSet would be another option. It allows for easier debugging as you could just print the set and get the enum strings. – Maarten Bodewes Apr 12 '21 at 23:00
  • Well, a key press in itself doesn't do anything, you can of course programmatically add and remove keys. But that's probably not what you want. Maybe you should convert the key presses to specific *actions* (e.g. going up) using a key -> action map. Then you can programmatically perform actions. – Maarten Bodewes Apr 12 '21 at 23:01
  • @Maarten Bodewes Alright I will try that. Thanks. – LuckyBandit74 Apr 12 '21 at 23:07
  • Good because otherwise you get: computer presses key, user presses key, computer releases key, user asks himself why they are not moving forward while holding the forward key pressed :) – Maarten Bodewes Apr 12 '21 at 23:09
  • See the `Keyboard Animation` example from [Motion Using the Keyboard](https://tips4java.wordpress.com/2013/06/09/motion-using-the-keyboard/). – camickr Apr 13 '21 at 01:53
  • @camickr Hey sorry for a very late response as I had just started working on this again. Your example show how to move an icon around but I want to move a painted component around. I know the basics of moving around a painted rectangle per se, but I can't really figure out how to implement painting into the Keyboard Animation example, could you lead me in the right direction? – LuckyBandit74 Apr 18 '21 at 22:25
  • @camickr If you see this, I asked a follow up question if you want to look there instead: https://stackoverflow.com/questions/67154074/keybinding-multiple-keys-pressed-not-working – LuckyBandit74 Apr 18 '21 at 22:53
  • @LuckyBandit74 - why would you use that link and not the `Motion Using the Keyboard` link I provided? – camickr Apr 19 '21 at 00:16

1 Answers1

0

So after some of @Maarten Bodewes 's advice, I came up with this solution:

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;

public class Test extends JPanel implements KeyListener {
    
    private int x = 200, y = 200;
    
    Set<Integer> keys = new HashSet<Integer>();

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new Test().create();
    }
    
    public void paintComponent(Graphics tool) {
        super.paintComponent(tool);
        tool.drawRect(x,  y, 50, 50);
    }
    
    public void create() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 500);
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        
        frame.add(this);
        frame.addKeyListener(this);
        
        frame.setVisible(true);
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // TODO Auto-generated method stub
        keys.add(e.getKeyCode());
        multiKeys();
        repaint();
    }
    
    public void multiKeys() {
        System.out.println(keys.size());
        for(Integer key: keys) {
            System.out.println(key);
            switch(key) {
            case KeyEvent.VK_W:
                y -= 3;
                break;
            case KeyEvent.VK_S:
                y += 3;
                break;
            case KeyEvent.VK_A:
                x -= 3;
                break;
            case KeyEvent.VK_D:
                x += 3;
                break;
            }
        }
    }


    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub
        keys.remove(e.getKeyCode());
        
    }

}

Now when I press say W and D, the rectangle moved diagonally as intended. This is due to the fact that a Set called keys stores the keys pressed, and then executes those keys' codes through a separate method called multiKeys() which loops keys. And of course when a key is no longer pressed, remove it from the keys Set via the KeyListener keyReleased() method.

LuckyBandit74
  • 417
  • 1
  • 4
  • 10