I've been trying to write a small side-scroller game, but ran into an issue with repaint() not repainting. The key listener itself works, it increments and decrements the x and y values when the directional keys are pressed. Just the repaint() doesn't seem to be repainting.
public class SideScroller extends JPanel implements KeyListener{
private Random random = new Random();
private int r,g,b;
private int x = 0, y = 0;
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == e.VK_UP) {
y -= 5;
} else if(e.getKeyCode() == e.VK_DOWN) {
y += 5;
} else if(e.getKeyCode() == e.VK_LEFT) {
x -= 5;
} else if(e.getKeyCode() == e.VK_RIGHT) {
x += 5;
}
repaint();
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void paint(Graphics gg) {
gg.setColor(new Color(r, g, b));
gg.fillRect (x, y, 50, 50);
}
public static void main(String[] args) {
SideScroller ss = new SideScroller();
JFrame f = new JFrame();
f.add(new SideScroller());
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.addKeyListener(new SideScroller());
}
You should be overriding paintComponent
and calling super.paintComponent
, not paint
.
protected void paintComponent(Graphics gg) {
super.paintComponent(gg);
gg.setColor(new Color(r, g, b));
gg.fillRect (x, y, 50, 50);
}
Also I'd recommend looking into using Key Bindings instead of KeyListener
,
Also you should be running Swing apps on the Event Dispacth Thread You can d so by wrapping the code in the main
in a SwingUtilities.invokeLater(...)
Here are all the fixes mentioned above
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class SideScroller extends JPanel {
private Random random = new Random();
private int r, g, b;
private int x = 0, y = 0;
public SideScroller() {
InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke("UP"), "upaction");
am.put("upaction", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
y -= 5;
repaint();
}
});
im.put(KeyStroke.getKeyStroke("DOWN"), "downaction");
am.put("downaction", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
y += 5;
repaint();
}
});
im.put(KeyStroke.getKeyStroke("LEFT"), "leftaction");
am.put("leftaction", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
x -= 5;
repaint();
}
});
im.put(KeyStroke.getKeyStroke("RIGHT"), "rightaction");
am.put("rightaction", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
x += 5;
repaint();
}
});
}
protected void paintComponent(Graphics gg) {
super.paintComponent(gg);
gg.setColor(new Color(r, g, b));
gg.fillRect(x, y, 50, 50);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.add(new SideScroller());
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
For the original poster and for Boann: Key Binding with a Swing Timer example:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class GamePanel extends JPanel {
private static final int ANIMATION_DELAY = 15;
private final int HEIGHT = 400;
private final int WIDTH = 600;
private Square square;
private EnumMap<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);
private Map<Integer, Direction> keyToDir = new HashMap<>();
private Timer animationTimer;
public GamePanel() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
keyToDir.put(KeyEvent.VK_UP, Direction.UP);
keyToDir.put(KeyEvent.VK_DOWN, Direction.DOWN);
keyToDir.put(KeyEvent.VK_LEFT, Direction.LEFT);
keyToDir.put(KeyEvent.VK_RIGHT, Direction.RIGHT);
setKeyBindings();
setBackground(Color.white);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
square = new Square();
animationTimer = new Timer(ANIMATION_DELAY, new AnimationListener());
animationTimer.start();
}
private void setKeyBindings() {
int condition = WHEN_IN_FOCUSED_WINDOW;
final InputMap inputMap = getInputMap(condition);
final ActionMap actionMap = getActionMap();
boolean[] keyPressed = { true, false };
for (Integer keyCode : keyToDir.keySet()) {
Direction dir = keyToDir.get(keyCode);
for (boolean onKeyPress : keyPressed) {
boolean onKeyRelease = !onKeyPress;
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, 0,
onKeyRelease);
Object key = keyStroke.toString();
inputMap.put(keyStroke, key);
actionMap.put(key, new KeyBindingsAction(dir, onKeyPress));
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
square.display(g);
}
private class AnimationListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent evt) {
boolean repaint = false;
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
square.move(dir);
repaint = true;
}
}
if (repaint) {
repaint();
}
}
}
private class KeyBindingsAction extends AbstractAction {
private Direction dir;
boolean pressed;
public KeyBindingsAction(Direction dir, boolean pressed) {
this.dir = dir;
this.pressed = pressed;
}
@Override
public void actionPerformed(ActionEvent evt) {
dirMap.put(dir, pressed);
}
}
private static void createAndShowGUI() {
GamePanel gamePanel = new GamePanel();
JFrame frame = new JFrame("GamePanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
gamePanel.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
enum Direction {
UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);
private int incrX;
private int incrY;
private Direction(int incrX, int incrY) {
this.incrX = incrX;
this.incrY = incrY;
}
public int getIncrX() {
return incrX;
}
public int getIncrY() {
return incrY;
}
}
class Square {
private int x = 0;
private int y = 0;
private int w = 20;
private int h = w;
private int step = 1;
private Color color = Color.red;
private Color fillColor = new Color(255, 150, 150);
private Stroke stroke = new BasicStroke(3f, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
public void display(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(fillColor);
g2d.fillRect(x, y, w, h);
g2d.setStroke(stroke);
g2d.setColor(color);
g2d.drawRect(x, y, w, h);
g2d.dispose();
}
public void setStep(int step) {
this.step = step;
}
public void move(Direction dir) {
x += step * dir.getIncrX();
y += step * dir.getIncrY();
}
}
The problem here is two things. You are adding the key listener to a new SideScroller - not the same one you are rendering, and you're not overwriting the correct painting method.
I suggest you add addKeyListener(this);
to the SideScroller's constructor and removing f.addKeyListener(new SideScroller());
, test that is getting called then testthe paint method which should be paintComponent(Graphics g)
.
I hope you understand how to do this and why keys are being recognised but 'not' rendering, I don't believe providing code to beginners is a good idea.
You are creating three different SideScroller objects. One unused, a second which you add to the frame and is displayed, and the third which you use as a KeyListener only (which is the one the repaint() method is called on). The SideScroller that you put into the ss
variable is the one you should also add to the frame and be using as the KeyListener.