I am making a game and I ran into a key ghosting problem (where the program only detects one keypress at a time, so the player can't go diagonally). I was watching this tutorial: https://www.youtube.com/watch?v=5UaEUrbpDPE
I followed everything they said and it still only detects one key at a time.
Main:
public class Main extends JApplet implements Runnable, KeyListener {
private static final long serialVersionUID = 1L;
public static int width = 900;
public static int height = 600;
public static int fps = 60;
public static Main instance;
public static Ailoid ailoid = new Ailoid();
public static Player player = new Player();
// Initialize
public void init() {
setSize(width, height);
setBackground(Color.white);
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
requestFocus();
instance = this;
ailoid.setLocation(new Location(100, 100));
AlienManager.registerAlien(ailoid);
player.setLocation(new Location(400, 400));
}
// Paint graphics
public void paint(Graphics g) {
super.paint(g);
paintComponent(g);
}
public void paintComponent(Graphics g) {
for (Alien alien : AlienManager.getAliens()) {
Location loc = alien.getLocation();
g.setColor(Color.GREEN);
g.fillRect(loc.getX(), loc.getY(), 10, 25);
}
g.setColor(Color.BLUE);
Location loc = Main.player.getLocation();
g.fillRect(loc.getX(), loc.getY(), 10, 25);
}
// Thread start
#Override
public void start() {
Thread thread = new Thread(this);
thread.start();
}
// Thread stop
#Override
public void destroy() {
}
// Thread run
#Override
public void run() {
Thread thread = new Thread(this);
while (thread != null) {
Updater.run();
repaint();
try {
// 1000 divided by fps to get frames per second
Thread.sleep(1000 / fps);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void keyPressed(KeyEvent evt) {
if (!KeysDown.get().contains(evt.getKeyCode()))
KeysDown.add(new Integer(evt.getKeyCode()));
KeyPress.run(evt);
}
#Override
public void keyReleased(KeyEvent evt) {
KeysDown.remove(new Integer(evt.getKeyCode()));
}
#Override
public void keyTyped(KeyEvent evt) {
}
}
KeysDown:
public class KeysDown {
private static ArrayList<Integer> keysDown = new ArrayList<Integer>();
public static ArrayList<Integer> get() {
return keysDown;
}
public static void add(Integer key) {
keysDown.add(key);
}
public static void remove(Integer key) {
keysDown.remove(key);
}
}
KeyPress:
public class KeyPress {
public static void run(KeyEvent evt) {
if (KeysDown.get().contains(KeyEvent.VK_RIGHT)) {
Main.player.moveRight();
}
else if (KeysDown.get().contains(KeyEvent.VK_LEFT)) {
Main.player.moveLeft();
}
else if (KeysDown.get().contains(KeyEvent.VK_DOWN)) {
Main.player.moveDown();
}
else if (KeysDown.get().contains(KeyEvent.VK_UP)) {
Main.player.moveUp();
}
}
}
Thank you!
Again as I have mentioned in previous comments:
Don't draw directly on the JApplet or in any top-level window.
If you give your applet a paintComponent method, it won't override any applet methods and won't gain the benefit of double buffering.
Instead draw in the paintComponent method of a JPanel, an thereby gain the benefit of double buffering.
Use Key Bindings not a KeyListener.
Also, I like to use a Swing Timer for simple game loops.
In the code below, I use an enum called Direction to encapsulate the idea of directions on the screen.
I then put the for Directions together with Boolean.FALSE in a Map<Direction, Boolean> called dirMap.
I run a Swing Timer continuously, polling the state of this Map and move a sprite based on the state of Booleans held by the dirMap.
I change the state of the map held values in my Key Binding Actions. So if the down key is pressed, its action will change the Direction.DOWN associated value in the Map to true, and when released, the Direction.DOWN associated value will be changed back to false.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.EnumMap;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class AnimateExample extends JPanel {
public static final String DUKE_IMG_PATH = // https://duke.kenai.com/iconSized/duke.gif
"https://duke.kenai.com/iconSized/duke4.gif";
private static final int PREF_W = 800;
private static final int PREF_H = 800;
private static final int TIMER_DELAY = 20;
private static final String KEY_DOWN = "key down";
private static final String KEY_RELEASE = "key release";
public static final int TRANSLATE_SCALE = 3;
private static final String BACKGROUND_STRING = "Use Arrow Keys to Move Image";
private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 32);
private EnumMap<Direction, Boolean> dirMap =
new EnumMap<AnimateExample.Direction, Boolean>(Direction.class);
private BufferedImage image = null;
private int imgX = 0;
private int imgY = 0;
private int bgStringX;
private int bgStringY;
public AnimateExample() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
try {
URL imgUrl = new URL(DUKE_IMG_PATH);
image = ImageIO.read(imgUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
new Timer(TIMER_DELAY, new TimerListener()).start();
// here we set up our key bindings
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
for (final Direction dir : Direction.values()) {
// for the key down key stroke
KeyStroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0,
false);
inputMap.put(keyStroke, dir.name() + KEY_DOWN);
actionMap.put(dir.name() + KEY_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, true);
}
});
// for the key release key stroke
keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true);
inputMap.put(keyStroke, dir.name() + KEY_RELEASE);
actionMap.put(dir.name() + KEY_RELEASE, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, false);
}
});
}
FontMetrics fontMetrics = getFontMetrics(BG_STRING_FONT);
int w = fontMetrics.stringWidth(BACKGROUND_STRING);
int h = fontMetrics.getHeight();
bgStringX = (PREF_W - w) / 2;
bgStringY = (PREF_H - h) / 2;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setFont(BG_STRING_FONT);
g.setColor(Color.LIGHT_GRAY);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.drawString(BACKGROUND_STRING, bgStringX, bgStringY);
if (image != null) {
g.drawImage(image, imgX, imgY, this);
}
}
private class TimerListener implements ActionListener {
public void actionPerformed(java.awt.event.ActionEvent e) {
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
imgX += dir.getX() * TRANSLATE_SCALE;
imgY += dir.getY() * TRANSLATE_SCALE;
}
}
repaint();
};
}
enum Direction {
Up(KeyEvent.VK_UP, 0, -1), Down(KeyEvent.VK_DOWN, 0, 1), Left(
KeyEvent.VK_LEFT, -1, 0), Right(KeyEvent.VK_RIGHT, 1, 0);
private int keyCode;
private int x;
private int y;
private Direction(int keyCode, int x, int y) {
this.keyCode = keyCode;
this.x = x;
this.y = y;
}
public int getKeyCode() {
return keyCode;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
private static void createAndShowGui() {
AnimateExample mainPanel = new AnimateExample();
JFrame frame = new JFrame("Animate Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
For more on Key Bindings, please check out the informative tutorial which you can find here.
Related
I have a problem with repaint() method in my Java code. I want to call it in another class but I can't, something doesn't work at all. I've searched on forums, but nothing was able to help me out.
My Main class:
public class Main {
public static Main main;
public static JFrame f;
public Main(){
}
public static void main(String[] args) {
main = new Main();
f = new JFrame();
Ball b = new Ball();
f.getContentPane().setBackground(Color.GRAY);
f.add(b);
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setTitle("Test");
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.addMouseMotionListener(b);
f.addKeyListener(new Key());
}
}
Ball class where I created 2DGraphics for moving shapes:
public class Ball extends JLabel implements MouseMotionListener{
public Ball(){
}
public static double x = 10;
public static double y = 10;
public static double width = 40;
public static double height = 40;
String nick;
boolean isEllipse = true;
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if(isEllipse){
Ellipse2D e2d = new Ellipse2D.Double(x, y, width, height);
g2d.setColor(Color.RED);
g2d.fill(e2d);
}
else{
Rectangle2D r2d = new Rectangle2D.Double(x, y, width, height);
g2d.setColor(Color.GREEN);
g2d.fill(r2d);
}
}
#Override
public void mouseDragged(MouseEvent e) {
isEllipse = false;
x = e.getX() - 30;
y = e.getY() - 40;
this.repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
x = e.getX() - 30;
y = e.getY() - 40;
isEllipse = true;
this.repaint();
}
}
And Key class where I put KeyListener for move the shapes by key pressing:
public class Key extends Ball implements KeyListener {
public Key() {
}
#SuppressWarnings("static-access")
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
super.x += 10;
super.repaint();
System.out.println("x: " + super.x);
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
But something is wrong with this code: super method doesn't work for Key class. Everything in Ball class is working well. Where is the problem?
Super works fine, but your interpretation of what it does is wrong. Your problem is that you're trying to use inheritance to solve a problem that isn't solved with inheritance. You need to call repaint() on the actual visualized and used Ball instance, not on an instance of some completely different class, Key, that inappropriately extends from Ball. First off, make Key not extend Ball, pass in a true Ball reference into Key and the solution will fall from that.
Perhaps do something like this:
f.addKeyListener(new Key(b));
and
public class Key implements KeyListener {
private Ball ball;
public Key(Ball ball) {
this.ball = ball;
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
b.incrX(10); // give Ball a public method for this
b.repaint();
// System.out.println("x: " + super.x);
}
}
// .... etc...
Note, myself, I'd use Key Bindings for this, not a KeyListener, since then I wouldn't have to futz with keyboard focus.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.*;
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 MoveBall {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static void createAndShowGui() {
BallPanel ballPanel = new BallPanel(PREF_W, PREF_H);
MyMouse myMouse = new MyMouse(ballPanel);
ballPanel.addMouseListener(myMouse);
ballPanel.addMouseMotionListener(myMouse);
new CreateKeyBindings(ballPanel);
JFrame frame = new JFrame("MoveBall");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(ballPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
#SuppressWarnings("serial")
class BallPanel extends JPanel {
private static final Color ELLIPSE_COLOR = Color.RED;
private static final Color SQUARE_COLOR = Color.GREEN;
private static final int BALL_WIDTH = 40;
private int prefW;
private int prefH;
private boolean isEllipse = true;
private int ballX;
private int ballY;
public BallPanel(int prefW, int prefH) {
this.prefW = prefW;
this.prefH = prefH;
}
public boolean isEllipse() {
return isEllipse;
}
public void setEllipse(boolean isEllipse) {
this.isEllipse = isEllipse;
}
public int getBallX() {
return ballX;
}
public void setBallX(int ballX) {
this.ballX = ballX;
}
public void setXY(int x, int y) {
ballX = x;
ballY = y;
repaint();
}
public void setXYCenter(int x, int y) {
ballX = x - BALL_WIDTH / 2;
ballY = y - BALL_WIDTH / 2;
repaint();
}
public void setXYCenter(Point p) {
setXYCenter(p.x, p.y);
}
public int getBallY() {
return ballY;
}
public void setBallY(int ballY) {
this.ballY = ballY;
}
public void incrementBallX(int x) {
ballX += x;
repaint();
}
public void incrementBallY(int y) {
ballY += y;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (isEllipse) {
g2.setColor(ELLIPSE_COLOR);
g2.fillOval(ballX, ballY, BALL_WIDTH, BALL_WIDTH);
} else {
g2.setColor(SQUARE_COLOR);
g2.fillOval(ballX, ballY, BALL_WIDTH, BALL_WIDTH);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(prefW, prefH);
}
}
class MyMouse extends MouseAdapter {
private BallPanel ballPanel;
public MyMouse(BallPanel ballPanel) {
this.ballPanel = ballPanel;
}
#Override
public void mousePressed(MouseEvent e) {
ballPanel.setXYCenter(e.getPoint());
}
#Override
public void mouseDragged(MouseEvent e) {
ballPanel.setXYCenter(e.getPoint());
}
#Override
public void mouseReleased(MouseEvent e) {
ballPanel.setXYCenter(e.getPoint());
}
}
enum Direction {
UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(KeyEvent.VK_RIGHT);
private int key;
private Direction(int key) {
this.key = key;
}
public int getKey() {
return key;
}
}
// Actions for the key binding
#SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
private static final int STEP_DISTANCE = 5;
private BallPanel ballPanel;
private Direction direction;
public MyKeyAction(BallPanel ballPanel, Direction direction) {
this.ballPanel = ballPanel;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
ballPanel.incrementBallY(-STEP_DISTANCE);
break;
case DOWN:
ballPanel.incrementBallY(STEP_DISTANCE);
break;
case LEFT:
ballPanel.incrementBallX(-STEP_DISTANCE);
break;
case RIGHT:
ballPanel.incrementBallX(STEP_DISTANCE);
break;
default:
break;
}
}
}
class CreateKeyBindings {
private BallPanel ballPanel;
public CreateKeyBindings(BallPanel ballPanel) {
this.ballPanel = ballPanel;
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = ballPanel.getInputMap(condition);
ActionMap actionMap = ballPanel.getActionMap();
for (Direction direction : Direction.values()) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(direction.getKey(), 0);
String keyString = keyStroke.toString();
inputMap.put(keyStroke, keyString);
actionMap.put(keyString, new MyKeyAction(ballPanel, direction));
}
}
}
When I create a Board instance from my Square instance, I try to assign the size of the window to the integers x and y. I fail to do this because it seems like on start the size is 0. In the constructor in Board.java, x and y shouldn't be -50 like they end up now.
Square.java:
package Square;
import javax.swing.*;
public class Square extends JFrame {
public Square(){
add(new Board());
setSize(800, 800);
setVisible(true);
}
public static void main(String[] args){
new Square();
}
}
Board.java
package Square;
import javax.swing.*;
import java.awt.*;
public class Board extends JPanel{
int x,y;
public Board(){
x = width-50;
y = height-50;
}
public int width = (int) getSize().getWidth();
public int height = (int) getSize().getHeight();
public void paintComponent(Graphics g){
super.paintComponent(g);
g.fillRect(x,y, 100, 100);
}
}
Full Code for clarification:
Square.java
package Square;
import javax.swing.*;
public class Square extends JFrame {
public Square(){
Board board = new Board();
board.start();
add(board);
setTitle("Square");
setSize(800, 800);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args){
Square square = new Square();
square.setVisible(true);
square.setLocation(2000, 150);
}
}
Board.java
package Square;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Board extends JPanel implements ActionListener{
Timer timer;
int x, y;
int velX = 0;
int velY = 0;
public Board(){
setFocusable(true);
timer = new Timer(1, this);
addKeyListener(new TAdapter());
}
class TAdapter extends KeyAdapter{
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch(keyCode){
case KeyEvent.VK_ESCAPE: x = width()/2-50; y = height()/2-50; break;
case KeyEvent.VK_RIGHT: velX = 1; break;
case KeyEvent.VK_DOWN: velY = 1; break;
case KeyEvent.VK_LEFT: velX = -1; break;
case KeyEvent.VK_UP: velY = -1; break;
}
}
public void keyReleased(KeyEvent e){
velX = 0;
velY = 0;
}
}
public int width(){ return (int) getSize().getWidth();}
public int height(){ return (int) getSize().getHeight();}
public void start(){
timer.setInitialDelay(100);
timer.start();
x = width()/2-50;
y = height()/2-50;
}
#Override
public void actionPerformed(ActionEvent e) {
x += velX;
y += velY;
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x,y, 100, 100);
}
}
Put the calculation into your paintComponent method. In general you want to avoid having code in paintComponent that will slow it down, but these calls shouldn't give too much penalty. Also, if your program is re-sizable, you'll need to be able to re-calculate the sizes of things on the go like this:
public void paintComponent(Graphics g){
super.paintComponent(g);
int x = getWidth() - 50;
int y = getHeight() - 50;
g.fillRect(x, y, 100, 100);
}
but of course in your real program, you will want to avoid "magic" numbers
Another issue: don't call setSize() on your JFrame. Instead, if you want to specify a hard size, do so in the JPanel by overriding its getPreferredSize() method. This will also then give you the suggested parameters that can be used for your calculations.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawRect extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private static final int DELTA = 50;
private static final Color RECT_COLOR = Color.red;
private static final int RECT_WIDTH = 100;
private static final int TIMER_DELAY = 15;
private int rectX = PREF_W - DELTA;
private int rectY = PREF_H - DELTA;
public DrawRect() {
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(RECT_COLOR);
g.fillRect(rectX, rectY, RECT_WIDTH, RECT_WIDTH);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
rectX--;
rectY--;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawRect());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Also, check out the key bindings animation code from this answer of mine.
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 Circle circle;
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);
// !! addKeyListener(new DirectionListener());
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; // to make it clear how bindings work
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();
}
}
Hey your codestyle is horrible, but i try to help :). You can't get a size of an undrawn window. First Things first, your Constructor is wrong, you add the Board that actually create the Board Obj. Calling the Constructor of Board, which has no drawn parent yet and no x,y set. Try to initialize your variables in the Constructor. So just use width and height and fill the values in the constructor. Next, just tell your board its creation size by passing its parent size trough constructor variables.
I think you try to learn java and this is much more elegant. Furthermore, try to do all parent modification before adding some to it. So first setSize, add some Layout (Border/Flow/whatuwish) then get the frames ContentPane and add your Board component. To make things clear, you can't get e.g. the parent and parent size in Contructor because your board Obj isn't created and added yet. If you wish to getParent() and its size, create the Object add it to JFrame and than you can call getParent().getSize(). You get 0 because your JPanel isn't drawn at this time (before creation). If you wish to get the Parent Size just pass the JFrame Ref to Constructor or its size. Another Advise, don't create things in things in things, keep in mind with your code you create your JPanel as first Obj... Here is some example code:
Square:
public class Square extends JFrame {
public static void main(String[] args){
Square square = new Square();
square.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension d = new Dimension(800,800);
square.setPreferredSize(d);
square.setSize(d);
//too much, every Jframe has BorderLayout enabled
square.getContentPane().setLayout(new BorderLayout());
square.getContentPane().add(new Board(square), BorderLayout.CENTER);
square.pack();
square.setVisible(true);
}
}
Board:
public class Board extends JPanel{
int x,y;
JFrame parent;
public Board(JFrame parent){
int width = parent.getPreferredSize().width;
int height = parent.getPreferredSize().height;
x = width-50;
y = height-50;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.fillRect(x,y, 100, 100);
}
}
You can take x and y values after the panel has become visible, in the next EDT cycle, by using SwingUtilities.invokeLater, for example.
I'm trying to make Boy1 move but in the second class I'm getting redlines under setX. Anyone know what's wrong?
First Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class MyGame extends JPanel implements ActionListener, KeyListener {
Timer t = new Timer(5, this);
int x = 0, y = 0, velx =0, vely =0;
public MyGame() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x,y,50,30);
}
public void actionPerformed(ActionEvent e) {
if(x < 0)
{
velx=0;
x = 0;
}
if(x > 530)
{
velx=0;
x = 530;
}
if(y < 0)
{
vely=0;
y = 0;
}
if(y > 330)
{
vely=0;
y = 330;
}
x += velx;
y += vely;
repaint();
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_DOWN){
vely = 1;
velx = 0;
}
if (code == KeyEvent.VK_UP){
vely = -1;
velx = 0;
}
if (code == KeyEvent.VK_LEFT){
vely = 0;
velx = -1;
}
if (code == KeyEvent.VK_RIGHT){
vely = 0;
velx = 1;
}
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {
velx=0;
vely=0;
}
public static void main (String arge[]){
JFrame f = new JFrame();
MyGame s = new MyGame();
f.add(s);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(600,400);
f.setVisible(true);
}
}
Problem is with this last class. Perhaps it needs to be KeyAdapter? But I tried that and seems like that totally doesn't work.
The compiler is flagging you for a reason -- you're trying to call methods on a variable as if it were a variable that the class contains when it isn't -- it's held by a different class. And this isn't Kosher in Java.
What you should do is give the class that holds the variable public methods that outside classes can call and that will allow outside classes to be able to move the label. Then give your control object (the listener) an instance of the class that has these methods.
As an aside, you're usually better off using Key Bindings and not using a KeyListener.
I can give you an example of code that uses Key Bindings to move a JLabel around a JPanel:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.EnumMap;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class AnimateExample extends JPanel {
public static final String DUKE_IMG_PATH = // https://duke.kenai.com/iconSized/duke.gif
"https://duke.kenai.com/iconSized/duke4.gif";
private static final int PREF_W = 800;
private static final int PREF_H = 800;
private static final int TIMER_DELAY = 20;
private static final String KEY_DOWN = "key down";
private static final String KEY_RELEASE = "key release";
public static final int TRANSLATE_SCALE = 3;
private static final String BACKGROUND_STRING = "Use Arrow Keys to Move Image";
private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 32);
private EnumMap<Direction, Boolean> dirMap =
new EnumMap<AnimateExample.Direction, Boolean>(Direction.class);
private BufferedImage image = null;
private int imgX = 0;
private int imgY = 0;
private int bgStringX;
private int bgStringY;
public AnimateExample() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
try {
URL imgUrl = new URL(DUKE_IMG_PATH);
image = ImageIO.read(imgUrl);
Icon icon = new ImageIcon(image);
JOptionPane.showMessageDialog(null, icon);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
new Timer(TIMER_DELAY, new TimerListener()).start();
// here we set up our key bindings
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
for (final Direction dir : Direction.values()) {
// for the key down key stroke
KeyStroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0,
false);
inputMap.put(keyStroke, dir.name() + KEY_DOWN);
actionMap.put(dir.name() + KEY_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, true);
}
});
// for the key release key stroke
keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true);
inputMap.put(keyStroke, dir.name() + KEY_RELEASE);
actionMap.put(dir.name() + KEY_RELEASE, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, false);
}
});
}
FontMetrics fontMetrics = getFontMetrics(BG_STRING_FONT);
int w = fontMetrics.stringWidth(BACKGROUND_STRING);
int h = fontMetrics.getHeight();
bgStringX = (PREF_W - w) / 2;
bgStringY = (PREF_H - h) / 2;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setFont(BG_STRING_FONT);
g.setColor(Color.LIGHT_GRAY);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.drawString(BACKGROUND_STRING, bgStringX, bgStringY);
if (image != null) {
g.drawImage(image, imgX, imgY, this);
}
}
private class TimerListener implements ActionListener {
public void actionPerformed(java.awt.event.ActionEvent e) {
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
imgX += dir.getX() * TRANSLATE_SCALE;
imgY += dir.getY() * TRANSLATE_SCALE;
}
}
repaint();
};
}
enum Direction {
Up(KeyEvent.VK_UP, 0, -1), Down(KeyEvent.VK_DOWN, 0, 1), Left(
KeyEvent.VK_LEFT, -1, 0), Right(KeyEvent.VK_RIGHT, 1, 0);
private int keyCode;
private int x;
private int y;
private Direction(int keyCode, int x, int y) {
this.keyCode = keyCode;
this.x = x;
this.y = y;
}
public int getKeyCode() {
return keyCode;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
private static void createAndShowGui() {
AnimateExample mainPanel = new AnimateExample();
JFrame frame = new JFrame("Animate Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I'm creating a Frogger-like game and my sprite(frog)(image), if you will, is created but does not move on my keypress, and I believe the error is occurring somewhere in the draw() method for the actual frog.java class. This is my own project and I'm trying to incorporate polymorphism and inheritance. Is there a way to move my sprite using the draw and move methods that I have, else what would be the correct way to do so. This is being done in Netbeans unfortunately.
import java.awt.*;
import java.awt.event.*;
public class FroggerForm extends Frame implements ActionListener
{
private Frog frog;
private javax.swing.Timer moveTimer = new javax.swing.Timer(500, this);
/**
Creates new form FroggerForm
*/
public FroggerForm()
{
initComponents();
this.setSize(400, 400);
frog = new Frog(froggerPanel);
moveTimer.start();
}
/**
This method is called from within the constructor to initialize the form.
WARNING: Do NOT modify this code. The content of this method is always
regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents()
{
froggerPanel = new java.awt.Panel();
setMinimumSize(new java.awt.Dimension(500, 500));
addWindowListener(new java.awt.event.WindowAdapter()
{
public void windowClosing(java.awt.event.WindowEvent evt)
{
exitForm(evt);
}
});
setLayout(null);
froggerPanel.setBackground(new java.awt.Color(190, 190, 190));
froggerPanel.setPreferredSize(new java.awt.Dimension(400, 400));
froggerPanel.addKeyListener(new java.awt.event.KeyAdapter()
{
public void keyPressed(java.awt.event.KeyEvent evt)
{
keyAdapter(evt);
}
});
add(froggerPanel);
froggerPanel.setBounds(100, 0, 400, 400);
pack();
}// </editor-fold>
/**
Exit the Application
*/
private void exitForm(java.awt.event.WindowEvent evt) {
System.exit(0);
}
private void keyAdapter(java.awt.event.KeyEvent evt)
{
frog.hide();
if(evt.getSource() == KeyEvent.VK_UP)
frog.move(0, -10);
else if(evt.getSource() == KeyEvent.VK_DOWN)
frog.move(0, 10);
else if(evt.getSource() == KeyEvent.VK_RIGHT)
frog.move(10, 0);
else if(evt.getSource() == KeyEvent.VK_LEFT)
frog.move(-10 , 0);
}
/**
#param args the command line arguments
*/
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
new FroggerForm().setVisible(true);
}
});
}
// Variables declaration - do not modify
private java.awt.Panel froggerPanel;
// End of variables declaration
#Override
public void actionPerformed(ActionEvent ae)
{
frog.draw();
frog.move();
}
}
public abstract class PFigure implements Comparable
{
protected int x, y; // Current position of the figure
protected int width, height; // Drawn (displayed) this size
protected int priority; // Can use to determine "winner"
protected Panel panel; // Panel the figure lives on
public PFigure ( int startX, int startY, int _width, int _height,
int pr, Panel p )
{
x = startX;
y = startY;
width = _width;
height = _height;
priority = pr;
panel = p;
}
// Can use this in "battles", which figures is "greater"
public int compareTo(Object o)
{
if( o instanceof PFigure )
return priority - ((PFigure)o).priority;
return Integer.MAX_VALUE;
}
// Has "this" figure collided with p?
public boolean collidedWith ( PFigure p )
{
if ( p == null )
return false;
return ( x + width ) >= p.x && ( p.x + p.width ) >= x &&
( y + height ) >= p.y && ( p.y + p.height ) >= y;
}
// Can be used for moving by keyboard or mouse
public void move ( int deltaX, int deltaY )
{
x = x + deltaX;
y = y + deltaY;
}
public void hide()
{
Graphics g = panel.getGraphics();
Color oldColor = g.getColor();
g.setColor(panel.getBackground() );
g.fillRect(x, y, width, height);
g.setColor(oldColor);
}
// Can be automatic move, for example, called based on timer
public void move()
{
}
// Draw the figure.
// Each derived class will write their own drawing method.
// The first line should be:
// Graphics g = panel.getGraphics();
abstract public void draw();
}
import java.awt.*;
import java.io.File;
import javax.imageio.ImageIO;
public class Frog extends PFigure
{
private Image frogImg;
public Frog(Panel p)
{
super(200, 350, 30, 30, 0, p);
try
{
File file = new File("frogger.png");
frogImg = ImageIO.read(file);
}
catch ( Exception e )
{
System.out.println("Crashing: " + e);
}
}
#Override
public void draw()
{
if(frogImg != null)
{
Graphics g = panel.getGraphics();
g.drawImage(frogImg, x, y, width, height, null);
}
}
#Override
public void move(int deltaX, int deltaY)
{
if ( x <= 0)
x = 1;
else if(x >= panel.getSize().width)
x = panel.getSize().width - 1;
else if(y >= panel.getSize().height)
y = panel.getSize().height - 1;
x += deltaX;
y += deltaY;
}
}
I believe your first error is in the use of KeyListener.
KeyListener will only respond to key strokes when the component it is registered to has focus AND is focusable.
Use Key Bindings instead, they allow you to define the focus level where key events will be generated
Updated
As an example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class WizardFireball {
public static void main(String[] args) {
new WizardFireball();
}
public WizardFireball() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
private GameModel model;
public GamePane() {
try {
model = new DefaultModel();
ActionMap am = getActionMap();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "up-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "down-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "left-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "up-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "down-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "left-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "right-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "fire");
am.put("up-pressed", new KeyPressedAction(getModel(), Key.UP));
am.put("down-pressed", new KeyPressedAction(getModel(), Key.DOWN));
am.put("left-pressed", new KeyPressedAction(getModel(), Key.LEFT));
am.put("right-pressed", new KeyPressedAction(getModel(), Key.RIGHT));
am.put("up-released", new KeyReleasedAction(getModel(), Key.UP));
am.put("down-released", new KeyReleasedAction(getModel(), Key.DOWN));
am.put("left-released", new KeyReleasedAction(getModel(), Key.LEFT));
am.put("right-released", new KeyReleasedAction(getModel(), Key.RIGHT));
am.put("fire", new FireBallAction(getModel()));
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Asset asset : getModel().getAssets()) {
asset.update(GamePane.this);
}
repaint();
}
});
timer.start();
}
public GameModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Asset asset : getModel().getAssets()) {
asset.paint(this, g2d);
}
g2d.dispose();
}
}
public enum Key {
LEFT,
RIGHT,
UP,
DOWN
}
public interface GameModel {
public void keyPressed(Key key);
public void keyReleased(Key key);
public boolean isKeyPressed(Key key);
public void addFireball();
public void remove(Asset asset);
public Wizard getWizard();
public Iterable<Asset> getAssets();
}
public class DefaultModel implements GameModel {
private Set<Key> keys;
private Wizard wizard;
private List<Asset> assets;
public DefaultModel() throws IOException {
keys = new HashSet<>(25);
assets = new ArrayList<>(25);
wizard = new Wizard();
}
#Override
public void keyPressed(Key key) {
keys.add(key);
}
#Override
public void keyReleased(Key key) {
keys.remove(key);
}
#Override
public boolean isKeyPressed(Key key) {
return keys.contains(key);
}
#Override
public Wizard getWizard() {
return wizard;
}
#Override
public void addFireball() {
try {
assets.add(new Fireball(getWizard()));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public void remove(Asset asset) {
assets.remove(asset);
}
#Override
public Iterable<Asset> getAssets() {
List<Asset> proxy = new ArrayList<>(assets);
proxy.add(wizard);
return proxy;
}
}
public abstract class AbstractGameAction extends AbstractAction {
private GameModel model;
public AbstractGameAction(GameModel model) {
this.model = model;
}
public GameModel getModel() {
return model;
}
}
public abstract class AbstractKeyAction extends AbstractGameAction {
private Key key;
public AbstractKeyAction(GameModel model, Key key) {
super(model);
this.key = key;
}
public Key getKey() {
return key;
}
}
public class KeyPressedAction extends AbstractKeyAction {
public KeyPressedAction(GameModel model, Key key) {
super(model, key);
}
#Override
public void actionPerformed(ActionEvent e) {
getModel().keyPressed(getKey());
}
}
public class KeyReleasedAction extends AbstractKeyAction {
public KeyReleasedAction(GameModel model, Key key) {
super(model, key);
}
#Override
public void actionPerformed(ActionEvent e) {
getModel().keyReleased(getKey());
}
}
public class FireBallAction extends AbstractGameAction {
public FireBallAction(GameModel model) {
super(model);
}
#Override
public void actionPerformed(ActionEvent e) {
getModel().addFireball();
}
}
public interface Asset {
public void paint(GamePane surface, Graphics2D g2d);
public void update(GamePane surface);
public Rectangle getBounds();
}
public class Fireball implements Asset {
private final BufferedImage fireball;
private final Rectangle bounds;
public Fireball(Wizard wizard) throws IOException {
fireball = ImageIO.read(getClass().getResource("/Fireball.png"));
bounds = new Rectangle();
bounds.x = wizard.getBounds().x + wizard.getBounds().width;
bounds.y = wizard.getBounds().y + (wizard.getBounds().height - fireball.getHeight());
bounds.setSize(fireball.getWidth(), fireball.getHeight());
}
#Override
public Rectangle getBounds() {
return bounds;
}
#Override
public void paint(GamePane surface, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.drawImage(fireball, bounds.x, bounds.y, surface);
}
#Override
public void update(GamePane surface) {
Rectangle bounds = getBounds();
bounds.x += 8;
if (bounds.x > surface.getWidth()) {
surface.getModel().remove(this);
}
}
}
public class Wizard implements Asset {
private final BufferedImage wizard;
private final Rectangle bounds;
private boolean initialised = false;
public Wizard() throws IOException {
wizard = ImageIO.read(getClass().getResource("/Wizard.png"));
bounds = new Rectangle(wizard.getWidth(), wizard.getHeight());
}
#Override
public Rectangle getBounds() {
return bounds;
}
#Override
public void paint(GamePane surface, Graphics2D g2d) {
Rectangle bounds = getBounds();
Point point = bounds.getLocation();
if (!initialised) {
point = new Point(5, (surface.getHeight() - bounds.height) / 2);
bounds.setLocation(point);
initialised = true;
}
g2d.drawImage(wizard, point.x, point.y, surface);
}
#Override
public void update(GamePane surface) {
Rectangle bounds = getBounds();
Point point = bounds.getLocation();
if (initialised) {
GameModel model = surface.getModel();
if (model.isKeyPressed(Key.UP)) {
point.y -= 4;
} else if (model.isKeyPressed(Key.DOWN)) {
point.y += 4;
}
if (point.y < 0) {
point.y = 0;
} else if (point.y + bounds.height > surface.getHeight()) {
point.y = surface.getHeight() - bounds.height;
}
bounds.setLocation(point);
}
}
}
}
I have a piece of code in which a KeyEvent is triggered when any of the arrow keys are pressed. This in turn causes a square to move across the screen.
Now I've noticed that when I press and hold the key, the square moves but the time after the initial movement to the subsequent movements is quite long if you understand what I mean? How would I go about diminishing this time?
Thanks!
import java.awt.*;
import javax.swing.*;
import squarequest.sprites.*;
import java.awt.event.*;
public class GamePanel extends JPanel{
private final int HEIGHT = 400;
private final int WIDTH = 600;
private Square square;
private Circle circle;
public GamePanel(){
addKeyListener(new DirectionListener());
setBackground (Color.white);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
square = new Square();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
square.display(g);
}
public class DirectionListener implements KeyListener{
#Override
public void keyReleased(KeyEvent event) {}
#Override
public void keyTyped(KeyEvent event) {}
#Override
public void keyPressed(KeyEvent event) {
switch(event.getKeyCode()){
case KeyEvent.VK_UP:
square.moveUp();
break;
case KeyEvent.VK_DOWN:
square.moveDown();
break;
case KeyEvent.VK_LEFT:
square.moveLeft();
break;
case KeyEvent.VK_RIGHT:
square.moveRight();
break;
}
repaint();
}
}
}
Use a Swing Timer for the actual movement.
Start the Timer on key press
Stop it on key release.
Or better still, keep the timer always running, but only moving the sprite based on the state of a map, one that holds a Direction enum key and a Boolean value.
Better to use key bindings rather than KeyListeners.
e.g.,
import java.awt.*;
import javax.swing.*;
//!! import squarequest.sprites.*;
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 Circle circle;
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);
// !! addKeyListener(new DirectionListener());
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; // to make it clear how bindings work
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);
}
// public class DirectionListener implements KeyListener {
// #Override
// public void keyReleased(KeyEvent event) {
// int keyCode = event.getKeyCode();
// if (keyToDir.keySet().contains(keyCode)) {
// Direction dir = keyToDir.get(keyCode);
// dirMap.put(dir, false);
// }
// }
//
// #Override
// public void keyTyped(KeyEvent event) {
// }
//
// #Override
// public void keyPressed(KeyEvent event) {
// int keyCode = event.getKeyCode();
// if (keyToDir.keySet().contains(keyCode)) {
// Direction dir = keyToDir.get(keyCode);
// dirMap.put(dir, true);
// }
// }
// }
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();
}
// public void moveRight() {
// x++;
// }
//
// public void moveLeft() {
// x--;
// }
//
// public void moveUp() {
// y--;
// }
//
// public void moveDown() {
// y++;
// }
}