I'm writing a simple game in Java and I need to handle KeyEvents. Unfortunately my Java knowledge is considerably low and I can't get it to work. What am I doing wrong here? I also tried to implement KeyListener
while extending from JPanel
just to see if it will work, I even overridden addNotify()
to requestFocus()
and nothing. Everything else works fine.
The following is the class that should handle the events:
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
public class BasicRobot extends JComponent{
public static enum FACING{
NORTH, WEST, SOUTH, EAST
}
int xLoc, yLoc;
FACING facing;
int beepers;
public BasicRobot(int xLoc, int yLoc, FACING facing, int beepers){
this.xLoc = xLoc;
this.yLoc = yLoc;
this.facing = facing;
this.beepers = beepers;
World w = new World(this);
addKeyListener();
setFocusable(true);
requestFocusInWindow();
}
public void move(){
switch(facing){
case NORTH:
yLoc++;
break;
case WEST:
xLoc--;
break;
case SOUTH:
yLoc--;
break;
case EAST:
xLoc++;
break;
}
}
public void turnLeft(){
switch(facing){
case NORTH:
facing=FACING.WEST;
break;
case WEST:
facing=FACING.SOUTH;
break;
case SOUTH:
facing=FACING.EAST;
break;
case EAST:
facing=FACING.NORTH;
break;
}
}
void addKeyListener(){
getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "move");
getActionMap().put("move", new AbstractAction(){
public void actionPerformed(ActionEvent e) {
move();
}
});
}
}
This is the World
class(not sure how relevant it is but maybe I have done something really stupid so here it is):
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.border.LineBorder;
public class World extends JPanel implements Runnable{
private final int ROWS=30, COLS=30, BLOCK_SCALE=24, WALL_WIDTH=BLOCK_SCALE/3;
private final Dimension DIMS = new Dimension(COLS*BLOCK_SCALE, ROWS*BLOCK_SCALE);;
private final JFrame frame = new JFrame("World");
private final Polygon NORTH_TRIANGLE = new Polygon(new int[]{5,BLOCK_SCALE/2,BLOCK_SCALE-5}, new int[]{BLOCK_SCALE-3,2,BLOCK_SCALE-3}, 3);
private final Polygon WEST_TRIANGLE = new Polygon(new int[]{1,BLOCK_SCALE-4,BLOCK_SCALE-4}, new int[]{BLOCK_SCALE/2,5,BLOCK_SCALE-5}, 3);
private final Polygon SOUTH_TRIANGLE = new Polygon(new int[]{5,BLOCK_SCALE/2,BLOCK_SCALE-5}, new int[]{3,BLOCK_SCALE-2,3}, 3);
private final Polygon EAST_TRIANGLE = new Polygon(new int[]{4,4,BLOCK_SCALE-1}, new int[]{BLOCK_SCALE-5,5,BLOCK_SCALE/2}, 3);
private final int obsticleMap[][] = new int[COLS][ROWS];
private final Wall obsticles[] = {
new Wall(1, 1, 5, WALL_WIDTH, Wall.ALIGNMENT_HORIZONTAL),
new Wall(1, 1, 5, WALL_WIDTH, Wall.ALIGNMENT_VERTICAL),
new Wall(1, 5, 3, WALL_WIDTH, Wall.ALIGNMENT_HORIZONTAL)
}; //The Wall class is just data nothing of interest there
BasicRobot robot;
public World(BasicRobot robot){
super(null);
this.robot = robot;
initThis();
initFrame();
initObsticleMap();
}
private void initThis(){
setBackground(Color.white);
setSize(DIMS);
setLocation(1, 1);
setBorder(new LineBorder(Color.black));
Thread t = new Thread(this);
t.start();
}
private void initFrame(){
frame.setContentPane(new Container(){
private static final long serialVersionUID = 1L;
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHints(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON));
g2d.drawRect(0, 0, DIMS.width+1, DIMS.height+1);
g2d.drawLine(0, DIMS.height+2, DIMS.width+1, DIMS.height+2);
g2d.setFont(new Font("Serif", Font.BOLD, 18));
g2d.drawString(String.format("Loc X = %d", robot.xLoc), 20, DIMS.height+22);
g2d.drawString(String.format("Loc Y = %d", robot.yLoc), 20, DIMS.height+42);
g2d.drawString(String.format("Beepers in bag = %d", robot.beepers), 150, DIMS.height+22);
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setPreferredSize(new Dimension(DIMS.width+2,DIMS.height+50));
frame.setResizable(false);
frame.getContentPane().add(this);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void initObsticleMap(){
for(int i=0; i<obsticles.length; i++){
switch(obsticles[i].alignment){
case Wall.ALIGNMENT_HORIZONTAL:
for(int j=0; j<obsticles[i].span; j++){
obsticleMap[obsticles[i].xLoc+j][obsticles[i].yLoc] = 1;
}
break;
case Wall.ALIGNMENT_VERTICAL:
for(int j=0; j<obsticles[i].span; j++){
obsticleMap[obsticles[i].xLoc][obsticles[i].yLoc+j] = 1;
}
break;
}
}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
drawGrid(g2d);
drawObsticles(g2d);
drawRobot(g2d);
}
private void drawGrid(Graphics2D g){
for(int i=0; i<COLS; i++){
for(int j=0; j<ROWS; j++){
g.drawRect(i*BLOCK_SCALE, j*BLOCK_SCALE, BLOCK_SCALE-1, BLOCK_SCALE-1);
}
}
}
private void drawObsticles(Graphics2D g2d){
for(int i=0; i<obsticles.length; i++){
switch(obsticles[i].alignment){
case Wall.ALIGNMENT_HORIZONTAL:
g2d.fillRoundRect(obsticles[i].xLoc*BLOCK_SCALE-BLOCK_SCALE/2, (ROWS-obsticles[i].yLoc)*BLOCK_SCALE-obsticles[i].width/2, BLOCK_SCALE*obsticles[i].span, obsticles[i].width, 2, 2);
break;
case Wall.ALIGNMENT_VERTICAL:
g2d.fillRoundRect(obsticles[i].xLoc*BLOCK_SCALE-obsticles[i].width/2, (ROWS-obsticles[i].yLoc-obsticles[i].span+1)*BLOCK_SCALE-BLOCK_SCALE/2, obsticles[i].width, BLOCK_SCALE*obsticles[i].span, 2, 2);
break;
}
}
}
private void drawRobot(Graphics2D g){
Polygon translated;
switch(robot.facing){
case NORTH:
translated = new Polygon(NORTH_TRIANGLE.xpoints, NORTH_TRIANGLE.ypoints, 3);
break;
case WEST:
translated = new Polygon(WEST_TRIANGLE.xpoints, WEST_TRIANGLE.ypoints, 3);
break;
case SOUTH:
translated = new Polygon(SOUTH_TRIANGLE.xpoints, SOUTH_TRIANGLE.ypoints, 3);
break;
case EAST:
translated = new Polygon(EAST_TRIANGLE.xpoints, EAST_TRIANGLE.ypoints, 3);
break;
default:
translated = new Polygon(EAST_TRIANGLE.xpoints, EAST_TRIANGLE.ypoints, 3);
}
translated.translate(robot.xLoc*BLOCK_SCALE-BLOCK_SCALE/2, (ROWS-robot.yLoc)*BLOCK_SCALE-BLOCK_SCALE/2);
g.setColor(Color.yellow);
g.fillPolygon(translated);
g.setColor(Color.black);
g.drawPolygon(translated);
}
public void run() {
for(;;){
try{
frame.repaint();
Thread.sleep(40);
}catch(Exception e){e.toString();}
}
}
}
PS: The preferred method is to use Key Bindings. Thank you!
getInputMap(WHEN_IN_FOCUSED_WINDOW)
to remove any focus related issues...addKeyListener()
so that the key bindings are registered.Thread.sleep
from within the Event Dispatching ThreadIn fact, this looks suspicious...
Seen as you're not actually updating the state of anything, all it is doing is sucking up CPU cycles which could be put to better use...
Additional...
Because you never add
BasicRobot
to anything that is displayed (like a window), it never registers it's listeners into the Event Queue, meaning, the key bindings will never work.Instead of extending
BasicRobot
fromJComponent
, you should simply not extend it from anything, there is no benifit been gained.Instead, register the key binding to the
World
and callBasicRobot#move
from thereFor example...sorry, I might have your code backwards a little, but the basic idea is there..
Updated...
This is a personal thing, but I prefer to use the
KeyEvent
virtual key codes instead...Otherwise I think you might need to use (but I'm not 100% sure)