Can't repaint my JFrame/JPanel - java

I have created a program that just moves a ball across a screen. I used to have it all in one class, but decided that it looked too messy so I split it up into three different classes: Main... initializes everything, Game... which paints everything and is a JPanel, and AL which is a KeyListener (which is also where the problem is). The problem is that I can't get the program to repaint from my AL class no matter what I try to pass into it. Can anyone help with this? Here are my three classes:
import java.awt.Color;
import javax.swing.JFrame;
public class Main {
static Game game;
static JFrame frame;
public static void main(String[] args) {
game = new Game();
frame = new JFrame();
frame.getContentPane().add(game);
frame.addKeyListener(new AL(game, frame));
frame.setTitle("Game");
frame.setSize(500, 500);
frame.setResizable(true);
frame.setVisible(true);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
-
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable {
int x, y, xCoord, yCoord;
private Image dbImage;
private Graphics dbg;
JFrame frame;
public void changeCoord() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
}
public static void main(String[] args) {
Game game = new Game();
Thread t = new Thread(game);
t.start();
}
public Game() {
x = 250;
y = 250;
}
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
#Override
public void run() {
try {
while (true) {
changeCoord();
Thread.sleep(30);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
-
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class AL extends KeyAdapter {
Game game;
JFrame frame;
public AL(Game game, JFrame frame) {
this.game = game;
this.frame = frame;
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
game.setXCoord(-1);
}
if (keyCode == e.VK_RIGHT) {
game.setXCoord(+1);
}
if (keyCode == e.VK_UP) {
game.setYCoord(-1);
}
if (keyCode == e.VK_DOWN) {
game.setYCoord(+1);
}
game.repaint();
}
#Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
game.setXCoord(0);
}
if (keyCode == e.VK_RIGHT) {
game.setXCoord(0);
}
if (keyCode == e.VK_UP) {
game.setYCoord(0);
}
if (keyCode == e.VK_DOWN) {
game.setYCoord(0);
}
game.repaint();
}
}

Let's start with the obvious....
This is problematic...
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
There's no need to implement double buffering in Swing components, they already are. Also, you're breaking the painting contract, by not call the paint methods super methods
The whole thing should be...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
See Painting in AWT and Swing and Performing Custom Painting for more details
KeyListener is well known for been problematic. It will only raise key events if the component it registered to is focuable AND has keyboard focus. A JPanel by default, is not focusable. Before you run of and try and make it focusable (and get bitterly disappointed), you should be using the Key Bindings API instead, which was designed to over come the limitations of KeyListener
As a basic example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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 Game());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Game extends JPanel {
int x, y, xCoord, yCoord;
public Game() {
x = 250;
y = 250;
addKeyBinding(KeyEvent.VK_LEFT, "move.left", new MoveAction(this, -1, 0));
addKeyBinding(KeyEvent.VK_RIGHT, "move.right", new MoveAction(this, 1, 0));
addKeyBinding(KeyEvent.VK_UP, "move.up", new MoveAction(this, 0, -1));
addKeyBinding(KeyEvent.VK_DOWN, "move.down", new MoveAction(this, 0, 1));
}
protected void addKeyBinding(int keyCode, String name, Action action) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0), name, action);
}
protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(keyStroke, name);
actionMap.put(name, action);
}
public void changeCoord() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
repaint();
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
changeCoord();
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
changeCoord();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(480, 480);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
}
public class MoveAction extends AbstractAction {
private int xDelta;
private int yDelta;
// I'd prefer an interface with just the "move" methods, but
// that's more time I don't have
private Game game;
public MoveAction(Game game, int xDelta, int yDelta) {
this.xDelta = xDelta;
this.yDelta = yDelta;
this.game = game;
}
#Override
public void actionPerformed(ActionEvent e) {
game.setXCoord(xDelta);
game.setYCoord(yDelta);
}
}
}
But, wait, that isn't exactly what you want (trust me, I'm an anoymouse person on the Internet ;)), a better example might be...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
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 Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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 Game());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Direction {
UP,
LEFT,
DOWN,
RIGHT;
}
public class Game extends JPanel {
int x, y, xCoord, yCoord;
private Set<Direction> movement;
public Game() {
x = 250;
y = 250;
movement = new HashSet<>(4);
addKeyPressedBinding(KeyEvent.VK_LEFT, "left.pressed", new MoveAction(movement, Direction.LEFT, true));
addKeyReleasedBinding(KeyEvent.VK_LEFT, "left.released", new MoveAction(movement, Direction.LEFT, false));
addKeyPressedBinding(KeyEvent.VK_RIGHT, "right.pressed", new MoveAction(movement, Direction.RIGHT, true));
addKeyReleasedBinding(KeyEvent.VK_RIGHT, "right.released", new MoveAction(movement, Direction.RIGHT, false));
addKeyPressedBinding(KeyEvent.VK_UP, "up.pressed", new MoveAction(movement, Direction.UP, true));
addKeyReleasedBinding(KeyEvent.VK_UP, "up.released", new MoveAction(movement, Direction.UP, false));
addKeyPressedBinding(KeyEvent.VK_DOWN, "down.pressed", new MoveAction(movement, Direction.DOWN, true));
addKeyReleasedBinding(KeyEvent.VK_DOWN, "down.released", new MoveAction(movement, Direction.DOWN, false));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
changeCoord();
}
});
timer.start();
}
protected void addKeyBinding(int keyCode, String name, Action action) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0), name, action);
}
protected void addKeyPressedBinding(int keyCode, String name, Action action) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, false), name, action);
}
protected void addKeyReleasedBinding(int keyCode, String name, Action action) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, true), name, action);
}
protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(keyStroke, name);
actionMap.put(name, action);
}
public void changeCoord() {
if (movement.contains(Direction.UP)) {
y--;
} else if (movement.contains(Direction.DOWN)) {
y++;
}
if (movement.contains(Direction.LEFT)) {
x--;
} else if (movement.contains(Direction.RIGHT)) {
x++;
}
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
repaint();
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
changeCoord();
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
changeCoord();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(480, 480);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
}
public class MoveAction extends AbstractAction {
private Set<Direction> movement;
private Direction direction;
private boolean pressed;
public MoveAction(Set<Direction> movement, Direction direction, boolean pressed) {
this.movement = movement;
this.direction = direction;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
if (pressed) {
movement.add(direction);
} else {
movement.remove(direction);
}
}
}
}
What this does is simply activates a flag when a key is pressed (and deactivates it when it's released), then in a Swing Timer, we check which keys are "active" and update the location of the ball.
What this does is, eliminates the key "stutter" which is caused by the OS when a key is first pressed and held. A delay is inserted between the first key and the repeated key events. Instead, we just turn the flag on and off as we like.
It also allows you to move in two directions at the same time (horizontally and vertically)
Have a look at Concurrency in Swing and How to use Swing Timers for more details

Related

repaint(); not Updating

I'm new to java, and as a challenge I'm following some tutorials on how to create a small game. I've managed to make a nice keyboard input reader but sadly I don't know why my repaint(); isn't working... The point is to change the ints Py and Px, they are responsible in changing the position of the player!
This is the class f2, a JPanel that is added to a Jframe in another file.
import java.awt.*;
import java.awt.event.*;
public class f2 extends JPanel implements Runnable{
keys keyh = new keys();
Thread gameThreadMain;
//background color
Color bg2 = new Color(81,89,98);
Color txt1 = new Color(237,237,237);
//width control
final int w = 700, h = 700;
int wp = 64, hp = 64;
//spawn cords
//
int spawn1x = (w / 2) - (wp / 2);
int spawn1y = (h / 2) - (hp / 2);
//Player Speed
int Px = spawn1x;
int Py = spawn1y;
int Ps = 4;
//FPS
int fps = 60;
public f2(){
/* this.addKet*/
this.setBackground(bg2);
this.setPreferredSize(new Dimension(w,h));
this.setDoubleBuffered(true);
/*this.setLayout();*/
}
public void startGameThreadMain(){
gameThreadMain = new Thread(this);
gameThreadMain.start();
}
#Override
public void run(){
double drawInterval = 1000000000/fps;
double nextDrawTime = System.nanoTime() + drawInterval;
while(gameThreadMain != null){
update();
repaint();
/*System.out.print("check");*/
try{
double remainingTime = nextDrawTime - System.nanoTime();
remainingTime = remainingTime / 1000000;
if (remainingTime < 0) {
remainingTime = 0;
}
Thread.sleep((long) remainingTime);
nextDrawTime += drawInterval;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void update(){
//Keyboard
if(keyh.UpPressed == true) {
Py -= Ps;
}
else if(keyh.DownPressed == true) {
Py += Ps;
}
else if(keyh.LeftPressed == true) {
Px -= Ps;
}
else if(keyh.RightPressed == true) {
Px += Ps;
}else{
//
}
}
//player painter (this is the part where the 'repaint();' isn't working
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D player = (Graphics2D)g;
player.setColor(txt1);
player.fillRect(Px,Py,wp,hp);
System.out.println("repainted");
}
}```
//Thanks for reading!
startGameThreadMain is never called.
Swing is not thread AND is single threaded. You should never update the UI, or some state the UI depends on, from outside the context of the Event Dispatching Thread.
See Concurrency in Swing for more details and How to Use Swing Timers for one possible solution.
You should also be aware of:
How to Use Key Bindings
Painting in AWT and Swing
Performing Custom Painting
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;
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;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Player {
private int x;
private int y;
private Color backgroundColor = new Color(237, 237, 237);
private Shape shape;
public Player(int x, int y) {
this.x = x;
this.y = y;
this.shape = new Rectangle2D.Double(0, 0, getWidth(), getHeight());
}
public void setLocation(int x, int y) {
setX(x);
setY(y);
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return 64;
}
public int getHeight() {
return 64;
}
public void paint(Graphics2D g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(backgroundColor);
g2d.translate(getX(), getY());
g2d.fill(shape);
g2d.dispose();
}
}
public enum Movement {
UP, DOWN, LEFT, RIGHT;
}
public interface MovementObserver {
public void movementDidActiviated(Object source, Movement movement);
public void movementWasDeactiviated(Object source, Movement movement);
}
public class TestPane extends JPanel {
private Timer timer;
private Player player;
private Set<Movement> movements = new HashSet<>();
public TestPane() {
player = new Player(0, 0);
int spawnY = (getPreferredSize().width - player.getWidth()) / 2;
int spawnX = (getPreferredSize().height - player.getHeight()) / 2;
player.setLocation(spawnX, spawnY);
setBackground(new Color(81, 89, 98));
timer = new Timer(15, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (movements.contains(Movement.UP)) {
player.setY(player.getY() - 1);
} else if (movements.contains(Movement.DOWN)) {
player.setY(player.getY() + 1);
}
if (movements.contains(Movement.LEFT)) {
player.setX(player.getX() - 1);
} else if (movements.contains(Movement.RIGHT)) {
player.setX(player.getX() + 1);
}
repaint();
}
});
MovementObserver observer = new MovementObserver() {
#Override
public void movementDidActiviated(Object source, Movement movement) {
movements.add(movement);
}
#Override
public void movementWasDeactiviated(Object source, Movement movement) {
movements.remove(movement);
}
};
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Up.pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Down.pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Up.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Down.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.released");
am.put("Up.pressed", new MovementAction(Movement.UP, observer, true));
am.put("Down.pressed", new MovementAction(Movement.DOWN, observer, true));
am.put("Left.pressed", new MovementAction(Movement.LEFT, observer, true));
am.put("Right.pressed", new MovementAction(Movement.RIGHT, observer, true));
am.put("Up.released", new MovementAction(Movement.UP, observer, false));
am.put("Down.released", new MovementAction(Movement.DOWN, observer, false));
am.put("Left.released", new MovementAction(Movement.LEFT, observer, false));
am.put("Right.released", new MovementAction(Movement.RIGHT, observer, false));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(700, 700);
}
#Override
public void addNotify() {
super.addNotify();
if (timer == null) {
return;
}
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer == null) {
return;
}
timer.stop();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (player == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
player.paint(g2d);
g2d.dispose();
}
}
public class MovementAction extends AbstractAction {
private Movement movement;
private MovementObserver observer;
private boolean activate;
public MovementAction(Movement movement, MovementObserver observer, boolean active) {
this.movement = movement;
this.observer = observer;
this.activate = active;
}
public Movement getMovement() {
return movement;
}
public MovementObserver getObserver() {
return observer;
}
public boolean doesActivate() {
return activate;
}
#Override
public void actionPerformed(ActionEvent e) {
if (doesActivate()) {
getObserver().movementDidActiviated(this, getMovement());
} else {
getObserver().movementWasDeactiviated(this, getMovement());
}
}
}
}

Not seeing rectangle Player class in JFrame

I'm new to java and trying yo create a basic game and right now and trying to program the player class. However only the JFRame appears when i run the game. These are my thress classes and they show no errors.
import java.awt.Color;
import javax.swing.JFrame;
public class Game extends JFrame {
public
final static int WIDTH = 700, HEIGHT = 450;
public
GamePanel panel;
public Game() {
setSize(WIDTH, HEIGHT);
setTitle("Game");
setBackground(Color.WHITE);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new GamePanel(this);
add(panel);
}
public GamePanel getPanel() {
return panel;
}
public static void main(String[] args) {
new Game();
}
}
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.JPanel;
public class GamePanel extends JPanel implements ActionListener, KeyListener {
public
Game game;
public
Player player;
public GamePanel(Game game) {
setBackground(Color.GREEN);
this.game = game;
player = new Player(game, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, game.getWidth() - 36);
addKeyListener(this);
setFocusable(true);
}
public
void update() {
player.update();
}
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
public Player getPlayer(int playerNo) {
return player;
}
public void keyPressed(KeyEvent e) {
player.pressed(e.getKeyCode());
}
public void keyReleased(KeyEvent e) {
player.released(e.getKeyCode());
}
public void keyTyped(KeyEvent e) {
;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
player.paint(g);
}
}
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Color;
public class Player {
public
static final int WIDTH = 50, HEIGHT = 50;
public
Game game;
public
int left, right;
public
int y;
public
int x, xa;
public Player(Game game, int left, int right, int x) {
this.game = game;
this.x = x;
y = game.getHeight() - 20;
this.left = left;
this.right = right;
this.y = y;
x = game.getWidth() - 36;
}
public void update() {
if (x > 0 && x < game.getWidth() - WIDTH - 36)
x += xa;
else if (x == 0)
x++;
else if (x == game.getWidth() - WIDTH - 36)
x--;
}
public void pressed(int keyCode) {
if (keyCode == left)
xa = -1;
else if (keyCode == right)
xa = 1;
}
public void released(int keyCode) {
if (keyCode == left || keyCode == right)
xa = 0;
}
public Rectangle getBounds() {
return new Rectangle(x, y, WIDTH, HEIGHT);
}
public void paint(Graphics g) {
g.fillRect(x, y, WIDTH, HEIGHT);
g.setColor(Color.ORANGE);
}
}
Take setVisible(true); and move it it to the last of the Game constructor. Also you should make sure you're working within the context of the EDT when building your UI's, see Initial Threads for more details
import java.awt.Color;
import java.awt.EventQueue;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Game extends JFrame {
public final static int WIDTH = 700, HEIGHT = 450;
public GamePanel panel;
public Game() {
setTitle("Game");
setBackground(Color.WHITE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new GamePanel(this);
add(panel);
setResizable(false);
setSize(WIDTH, HEIGHT);
setVisible(true);
}
public GamePanel getPanel() {
return panel;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new Game();
}
});
}
}
Basically, Swing's layout management is lazy, it won't try to update the container hierarchy until you tell it (revalidate) or it becomes realised or resized, this is a good thing, as the operation can be expensive.
Next, take a look at player = new Player(game, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, game.getWidth() - 36);
You're setting the position of the player to be 36 pixels less than the components width, but when this is called, the width will be 0.
Basically, you need to allow the UI to "settle" before making this types of calls. This is actually not that easy. You could use a ComponentListener and monitor for the componentResized event, but a window can be resized a number of times when it's first initialized, to this end, you need to "wait" until the size is "settled", for example...
public Game() {
addComponentListener(new ComponentAdapter() {
private boolean initalised = false;
private Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
initalised = true;
panel.init();
timer.stop();
}
});
#Override
public void componentResized(ComponentEvent e) {
if (!initalised) {
timer.restart();
}
}
});
Then you would need to update GamePanel to provide an init method...
public class GamePanel extends JPanel implements ActionListener, KeyListener {
public Game game;
public Player player;
public GamePanel(Game game) {
setBackground(Color.GREEN);
this.game = game;
addKeyListener(this);
setFocusable(true);
}
public void init() {
player = new Player(game, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, game.getWidth() - 36);
repaint();
}
//...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (player != null) {
player.paint(g);
}
}
}
You have a wide range of "magic" numbers, which don't equate to what you're actually trying to do, for example...
player = new Player(game, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, game.getWidth() - 36);
shouldn't that be game.getWidth() - Player.WIDTH?
And then in Player...
public Player(Game game, int left, int right, int x) {
this.game = game;
this.x = x;
y = game.getHeight() - 20;
//...
this.y = y;
x = game.getWidth() - 36;
}
You assign the parameter x to the field x, but then change it at the end of the constructor? Also, this.y = y makes no sense!?
Now, let's talk about what a bad idea setSize(WIDTH, HEIGHT);, which relates to why your player doesn't appear where you want it to...
Windows have decorations, so your viewable area will ALWAYS be smaller than the actual window size, what's worse, the size of the decorations are variable. Instead, you should use JFrame#pack to pack the window around it's contents and have the contents provide hints about big it wants to be.
Have a look at How can I set in the midst? for more details.
Instead of setting the size of the frame, you should be using pack...
public class Game extends JFrame {
public GamePanel panel;
public Game() {
addComponentListener(new ComponentAdapter() {
private boolean initalised = false;
private Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
initalised = true;
panel.init();
timer.stop();
}
});
#Override
public void componentResized(ComponentEvent e) {
if (!initalised) {
timer.restart();
}
}
});
setTitle("Game");
setBackground(Color.WHITE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new GamePanel(this);
add(panel);
setResizable(false);
pack();
setVisible(true);
}
And allowing GamePanel to make decisions about how big it wants to be...
public static class GamePanel extends JPanel implements ActionListener, KeyListener {
public final static int WIDTH = 700, HEIGHT = 450;
public Game game;
public Player player;
public GamePanel(Game game) {
setBackground(Color.GREEN);
this.game = game;
addKeyListener(this);
setFocusable(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
This also means that instead of passing Game to your objects, you should be passing GamePanel to them and using its dimensions, for example...
public static class Player {
//...
public Player(GamePanel game, int left, int right, int x) {
this.game = game;
this.left = left;
this.right = right;
this.x = x;
y = game.getHeight() - HEIGHT;
}
You should also not be using KeyListener and instead should be using the Key Bindings API instead, which will solve the focus related issue and make it easier to configure the key strokes
Basically, you're missing some core understandings about how the Swing framework works, which is causing no end of issues, I'd take the time to learn more about how the framework works generally before diving into something as complex as game development
Runnable example...
Now, because I've basically butchered your code to get it to work...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Game extends JFrame {
public GamePanel panel;
public Game() {
addComponentListener(new ComponentAdapter() {
private boolean initalised = false;
private Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
initalised = true;
panel.init();
timer.stop();
}
});
#Override
public void componentResized(ComponentEvent e) {
if (!initalised) {
timer.restart();
}
}
});
setTitle("Game");
setBackground(Color.WHITE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new GamePanel(this);
add(panel);
setResizable(false);
pack();
setVisible(true);
}
public GamePanel getPanel() {
return panel;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new Game();
}
});
}
public static class GamePanel extends JPanel implements ActionListener, KeyListener {
public final static int WIDTH = 700, HEIGHT = 450;
public Game game;
public Player player;
public GamePanel(Game game) {
setBackground(Color.GREEN);
this.game = game;
addKeyListener(this);
setFocusable(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
public void init() {
System.out.println("!!");
player = new Player(this, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, getWidth() - 50);
repaint();
}
public void update() {
player.update();
}
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
public Player getPlayer(int playerNo) {
return player;
}
public void keyPressed(KeyEvent e) {
player.pressed(e.getKeyCode());
}
public void keyReleased(KeyEvent e) {
player.released(e.getKeyCode());
}
public void keyTyped(KeyEvent e) {
;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (player != null) {
player.paint(g);
}
}
}
public static class Player {
public static final int WIDTH = 50, HEIGHT = 50;
public GamePanel game;
public int left, right;
public int y;
public int x, xa;
public Player(GamePanel game, int left, int right, int x) {
this.game = game;
this.left = left;
this.right = right;
this.x = x;
y = game.getHeight() - HEIGHT;
}
public void update() {
if (x > 0 && x < game.getWidth() - WIDTH - 36) {
x += xa;
} else if (x == 0) {
x++;
} else if (x == game.getWidth() - WIDTH - 36) {
x--;
}
}
public void pressed(int keyCode) {
if (keyCode == left) {
xa = -1;
} else if (keyCode == right) {
xa = 1;
}
}
public void released(int keyCode) {
if (keyCode == left || keyCode == right) {
xa = 0;
}
}
public Rectangle getBounds() {
return new Rectangle(x, y, WIDTH, HEIGHT);
}
public void paint(Graphics g) {
System.out.println(x + "x" + y);
g.fillRect(x, y, WIDTH, HEIGHT);
g.setColor(Color.ORANGE);
}
}
}

Graphics Object not displaying to screen

So the answer to this might be completely obvious, but I just can't see it. Why is my Graphics object not displaying what I tell it to? I'm fairly certain I'm honoring Swing concurrency and such but perhaps not. Here's my JPanel code:
package com.kraken.towerdefense.graphics;
import com.kraken.towerdefense.TowerDefense;
import com.kraken.towerdefense.listener.KeyHandler;
import com.kraken.towerdefense.listener.MouseMotionHandler;
import com.kraken.towerdefense.scene.Scene;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
private int FPS = 0;
public Scene scene;
TowerDefense tD;
private boolean running = false;
public RoundRectangle2D.Float playGame, quitGame;
public boolean playGameHighlighted, quitGameHighlighted;
#Override
public void run() {
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
while (running) {
repaint();
frames++;
if (System.currentTimeMillis() - 1000 >= lastFrame) {
FPS = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
}
System.exit(0);
}
public Screen(TowerDefense tD) {
thread.start();
addKeyListener(new KeyHandler(this));
addMouseMotionListener(new MouseMotionHandler(this));
this.tD = tD;
scene = Scene.MENU;
}
#Override
public void paintComponent(Graphics g2) {
super.paintComponent(g2);
playGame = new RoundRectangle2D.Float((getWidth() / 2) - 200, (getHeight() / 2) - 100, 400, 100, 10, 10);
quitGame = new RoundRectangle2D.Float((getWidth() / 2) - 200, (getHeight() / 2) + 20, 400, 100, 10, 10);
Graphics2D g = (Graphics2D) g2.create();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.clearRect(0, 0, getWidth(), getHeight());
g.drawString("FPS: " + FPS, 10, 10);
if (scene == Scene.MENU) {
if (playGameHighlighted) {
g.setColor(new Color(255, 152, 56));
} else {
g.setColor(new Color(4, 47, 61));
}
g.fill(playGame);
if (quitGameHighlighted) {
g.setColor(new Color(255, 152, 56));
} else {
g.setColor(new Color(4, 47, 61));
}
g.fill(quitGame);
g.setColor(Color.WHITE);
g.setFont(new Font("Gisha", Font.PLAIN, 20));
g.drawString("Play", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Play") / 2), (getHeight() / 2) - 45);
g.drawString("Quit", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Quit") / 2), (getHeight() / 2) + 75);
}
}
public class KeyTyped {
public void keyESC() {
running = false;
}
}
}
And here's my Scene Enum:
package com.kraken.towerdefense.scene;
public enum Scene {
MENU,
GAME
}
I'm pretty sure I don't need to supply the JFrame code, but if necessary I will. Any other solutions to problems in my code you could give would be greatly appreciated. Thanks!
EDIT 1
Here's my MouseMotionListener class:
package com.kraken.towerdefense.listener;
import com.kraken.towerdefense.graphics.Screen;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class MouseMotionHandler implements MouseMotionListener {
Screen screen;
public MouseMotionHandler(Screen screen) {
this.screen = screen;
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if (screen.playGame.contains(e.getPoint())) {
screen.playGameHighlighted = true;
} else {
screen.playGameHighlighted = false;
}
if (screen.quitGame.contains(e.getPoint())) {
screen.quitGameHighlighted = true;
} else {
screen.playGameHighlighted = false;
}
}
}
Here's my JFrame code:
package com.kraken.towerdefense;
import com.kraken.towerdefense.graphics.Screen;
import javax.swing.*;
public class TowerDefense extends JFrame {
public static void main(String[] args) {
new TowerDefense();
}
public TowerDefense() {
setExtendedState(MAXIMIZED_BOTH);
setUndecorated(true);
setTitle("Tower Defense");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
Screen screen = new Screen(this);
this.add(screen);
setVisible(true);
}
}
And here's my KeyListener code:
package com.kraken.towerdefense.listener;
import com.kraken.towerdefense.graphics.Screen;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class KeyHandler implements KeyListener {
private Screen screen;
private Screen.KeyTyped keyTyped;
public KeyHandler(Screen screen) {
this.screen = screen;
keyTyped = screen.new KeyTyped();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 27) {
keyTyped.keyESC();
}
}
#Override
public void keyReleased(KeyEvent e) {
}
}
So those are all my classes, I hope that helps
So, I gutted your code to make it run and was capable of displaying...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GraphicsTest {
public static void main(String[] args) {
new GraphicsTest();
}
public GraphicsTest() {
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 Screen());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Scene {
MENU,
GAME
}
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
private int FPS = 0;
public Scene scene;
private boolean running = false;
#Override
public void run() {
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
scene = Scene.MENU;
while (running) {
repaint();
frames++;
if (System.currentTimeMillis() - 1000 >= lastFrame) {
FPS = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
}
System.exit(0);
}
public Screen() {
thread.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.clearRect(0, 0, getWidth(), getHeight());
g.drawString("FPS: " + FPS, 10, 10);
if (scene == Scene.MENU) {
g.setColor(Color.BLACK);
g.fillRoundRect((getWidth() / 2) - 100, (getHeight() / 2) - 50, 200, 100, 25, 25);
}
}
}
}
So that suggests that the problem you're describing is some where else.
To me this;
g.clearRect(0, 0, tD.getWidth(), tD.getHeight());
looks suspicious, as you're relying on the TowerDefense properties when you should relying on the components own width/height properties (apart from clearRect actually not been required in this context).
This further makes me suspicious that you're not actually adding the Screen component to anything that is displayable
The other possible problem is you're making use of an appropriate layout manager, but since your Screen class doesn't supply any preferredSize hints, this would be an additional issue which you're not demonstrating
Updated based on changes to the original question
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import static java.awt.Frame.MAXIMIZED_BOTH;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import javafx.scene.Scene;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
public class TowerDefense extends JFrame {
public static void main(String[] args) {
new TowerDefense();
}
public TowerDefense() {
// setExtendedState(MAXIMIZED_BOTH);
// setUndecorated(true);
setTitle("Tower Defense");
setDefaultCloseOperation(EXIT_ON_CLOSE);
// setResizable(false);
Screen screen = new Screen(this);
this.add(screen);
pack();
setVisible(true);
}
public enum Scene {
MENU,
GAME
}
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
private int FPS = 0;
public Scene scene;
TowerDefense tD;
private boolean running = false;
public RoundRectangle2D.Float playGame, quitGame;
public boolean playGameHighlighted, quitGameHighlighted;
#Override
public void run() {
// long lastFrame = System.currentTimeMillis();
// int frames = 0;
//
// running = true;
//
// while (running) {
// repaint();
//
// frames++;
//
// if (System.currentTimeMillis() - 1000 >= lastFrame) {
// FPS = frames;
// frames = 0;
//
// lastFrame = System.currentTimeMillis();
// }
// }
//
// System.exit(0);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public Screen(TowerDefense tD) {
thread.start();
addKeyListener(new KeyHandler(this));
addMouseMotionListener(
new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
playGameHighlighted = playGame.contains(e.getPoint());
quitGameHighlighted = quitGame.contains(e.getPoint());
repaint();
}
});
this.tD = tD;
scene = Scene.MENU;
}
#Override
public void paintComponent(Graphics g2) {
super.paintComponent(g2);
playGame = new RoundRectangle2D.Float((getWidth() / 2) - 200, (getHeight() / 2) - 100, 400, 100, 10, 10);
quitGame = new RoundRectangle2D.Float((getWidth() / 2) - 200, (getHeight() / 2) + 20, 400, 100, 10, 10);
Graphics2D g = (Graphics2D) g2.create();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.clearRect(0, 0, getWidth(), getHeight());
g.drawString("FPS: " + FPS, 10, 10);
if (scene == Scene.MENU) {
if (playGameHighlighted) {
g.setColor(new Color(255, 152, 56));
} else {
g.setColor(new Color(4, 47, 61));
}
g.draw(playGame);
if (quitGameHighlighted) {
g.setColor(new Color(255, 152, 56));
} else {
g.setColor(new Color(4, 47, 61));
}
g.draw(quitGame);
g.setColor(Color.WHITE);
g.setFont(new Font("Gisha", Font.PLAIN, 20));
g.drawString("Play", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Play") / 2), (getHeight() / 2) - 45);
g.drawString("Quit", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Quit") / 2), (getHeight() / 2) + 75);
}
}
public class KeyTyped {
public void keyESC() {
running = false;
}
}
}
public class KeyHandler implements KeyListener {
private Screen screen;
private Screen.KeyTyped keyTyped;
public KeyHandler(Screen screen) {
this.screen = screen;
keyTyped = screen.new KeyTyped();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 27) {
keyTyped.keyESC();
}
}
#Override
public void keyReleased(KeyEvent e) {
}
}
}

mouseMoved Event not working

I've been trying to work this code, it's like when you hover over the start button it should change its color to gray, but whenever i hover over it. nothing happens, can somebody tell me why? i didn't get any error and it seems like my mousemoved listener isn't recognized by the compiler, sorry for my english. I haven't finish it yet but here is the code:
class Contents extends JFrame implements Runnable {
private Image dbi;
private Graphics dbg;
private boolean isStarted, isHovered;
private int x,y,xDir,yDir,bx,by,timer,life,my,mx,mhx,mhy;
private Rectangle startgame = new Rectangle(80,100,150,40);
Contents()
{
super();
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
if(isStarted)
setSize(600,600);
else
{
setSize(300,300);
setBackground(Color.BLUE);
}
setLocationRelativeTo(null);
isStarted = false;
isHovered = false;
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
mx = e.getX();
my = e.getY();
if(mx > startgame.x && mx < startgame.x+startgame.width &&
my > startgame.y && my < startgame.y+startgame.height)
{
isStarted = true;
}
}
public void mouseMoved(MouseEvent e)
{
mhx = e.getX();
mhy = e.getY();
if(mhx > startgame.x && mhx < startgame.x+startgame.width &&
mhy > startgame.y && mhy < startgame.y+startgame.height)
isHovered = true;
else
isHovered = false;
}
});
}
public void paint(Graphics g)
{
dbi = createImage(getWidth(), getHeight());
dbg = dbi.getGraphics();
draw(dbg);
g.drawImage(dbi,0,0,this);
repaint();
}
public void draw(Graphics g)
{
if(!isStarted)
{
if(!isHovered)
g.setColor(Color.GRAY);
else
g.setColor(Color.GREEN);
g.fillRect(startgame.x, startgame.y, startgame.width, startgame.height);
g.setFont(new Font("Serif",Font.BOLD,24));
g.setColor(Color.WHITE);
g.drawString("Start game", startgame.x+20, startgame.y+25);
g.drawString(String.format("hoverx: %d hovery: %d",mhx,mhy), 50,200);
}
else
{
}
}
public void run()
{
} }
public class Game {
public static void main(String[] args)
{
Contents c = new Contents();
} }
Just use Rectangle.contains(Point) to check if the point from the MouseEvent is inside the Rectangle. Here is an example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PaintedButton extends JPanel {
private static final Color HOVER_COLOR = Color.BLUE;
private static final Color NON_HOVER_COLOR = Color.GREEN;
private static final Rectangle2D RECTANGLE = new Rectangle2D.Double(50, 50,
200, 100);
private Color color = NON_HOVER_COLOR;
public PaintedButton() {
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
if (RECTANGLE.contains(p)) {
color = HOVER_COLOR;
} else {
color = NON_HOVER_COLOR;
}
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(color);
g2.fill(RECTANGLE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PaintedButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Detect overlapping objects in java Swing

I am trying to make a program that has a moving ball and a platform that it will sit on. I am new too java and I cant figure out how to detect when 2 swing objects are overlapping. My code it below and I am wondering what the best way is to detect overlapping objects.
KeyDemo.java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class KeyDemo
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
JPanel panel = new JPanel();
LayoutManager overlay = new OverlayLayout(panel);
panel.setLayout(overlay);
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("Move the Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final WallComponent wc1 = new WallComponent(400, 400);
final BallComponent bc = new BallComponent(400, 300);
panel.add(wc1);
panel.add(bc);
frame.add(panel);
KeyboardController kc = new KeyboardController(bc);
frame.addKeyListener(kc);
frame.setVisible(true);
class AnimationListener implements ActionListener{
public void actionPerformed(ActionEvent event){
bc.tick();
//wc1.checkOverlap(bc);
}
}
ActionListener aListener = new AnimationListener();
final Timer timer = new Timer(1, aListener);
timer.start();
}
}
KeyboardController.java:
import java.awt.*;
import java.awt.event.*;
public class KeyboardController implements KeyListener
{
BallComponent bComp;
public KeyboardController(BallComponent t)
{
bComp = t;
}
/** Handle the key pressed event from the text field. */
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == 38)
{
System.out.println("Pressed Up!");
bComp.moveUp();
}
if(keyCode == 37)
{
System.out.println("Pressed Left!");
bComp.moveLeft();
}
if(keyCode == 39)
{
System.out.println("Pressed Right!");
bComp.moveRight();
}
if(keyCode == 40)
{
System.out.println("Pressed Down!");
bComp.moveDown();
}
}
/** Handle the key released event from the text field. */
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == 38)
{
System.out.println("Released Up!");
bComp.stopY();
}
if(keyCode == 37)
{
System.out.println("Released Left!");
bComp.stopX();
}
if(keyCode == 39)
{
System.out.println("Released Right!");
bComp.stopX();
}
if(keyCode == 40)
{
System.out.println("Pressed Down!");
bComp.stopY();
}
}
public void keyTyped(KeyEvent e) {
}
}
BallComponent.java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class BallComponent extends JComponent
{
int xSpeed;
int ySpeed;
int x;
int y;
public BallComponent(int x, int y)
{
super();
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
Ellipse2D.Double ball = new Ellipse2D.Double(x-10,y-10,10,10);
g2.setColor(Color.RED);
g2.fill(ball);
g2.draw(ball);
}
public void moveLeft()
{
xSpeed=-1;
}
public void moveRight()
{
xSpeed=1;
}
public void moveUp()
{
ySpeed=-1;
}
public void moveDown()
{
ySpeed=1;
}
public void tick()
{
x=x+xSpeed;
y=y+ySpeed;
repaint();
}
public void stopY()
{
ySpeed=0;
}
public void stopX()
{
xSpeed=0;
}
}
WallComponent.java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class WallComponent extends JComponent
{
int x;
int y;
public WallComponent(int x, int y)
{
super();
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
Rectangle wall = new Rectangle(x-40,y-40,40,40);
g2.setColor(Color.YELLOW);
g2.fill(wall);
g2.draw(wall);
}
public void checkOverlap(BallComponent bc){
if (this.contains(bc.getLocation())){
bc.stopY();
bc.stopX();
}
}
}
All Swing components have a concept of "bounds". This is a rectangular area within which they are "drawn".
If you are controlling the size and position correctly, you should be able to use the contains method of the Rectangle which is returned from calling Component#getBounds
So you checkOverlap method could look like...
public void checkOverlap(BallComponent bc){
if (getBounds().intersects(bc.getBounds())){
bc.stopY();
bc.stopX();
}
}
You will also want to make sure that you are calling super.paintComponent before performing any custom painting, espeically when using components that extend from JComponent. This will ensure that the Graphics context is prepared for painting correctly...
Updated
There's a cascade of issues. Basically, instead of positing the components within the parent container yourself (which is how I thought you had done it), you've laid each component out to fill the parent container and only "painted" the objects...This makes life more difficult
Instead, if you are going to use components, I would use a null layout (or even possibly use a JLayeredPane as the parent container).
I would then change the physical position of the components, for example...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.OverlayLayout;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestGame {
public static void main(String[] args) {
new TestGame();
}
public TestGame() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(null);
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("Move the Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final WallComponent wc1 = new WallComponent(400, 400);
final BallComponent bc = new BallComponent(400, 300);
panel.add(wc1);
panel.add(bc);
frame.add(panel);
KeyboardController kc = new KeyboardController(bc);
frame.addKeyListener(kc);
frame.setVisible(true);
class AnimationListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
bc.tick();
wc1.checkOverlap(bc);
}
}
ActionListener aListener = new AnimationListener();
final Timer timer = new Timer(1, aListener);
timer.start();
}
});
}
public class KeyboardController implements KeyListener {
BallComponent bComp;
public KeyboardController(BallComponent t) {
bComp = t;
}
/**
* Handle the key pressed event from the text field.
*/
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == 38) {
System.out.println("Pressed Up!");
bComp.moveUp();
}
if (keyCode == 37) {
System.out.println("Pressed Left!");
bComp.moveLeft();
}
if (keyCode == 39) {
System.out.println("Pressed Right!");
bComp.moveRight();
}
if (keyCode == 40) {
System.out.println("Pressed Down!");
bComp.moveDown();
}
}
/**
* Handle the key released event from the text field.
*/
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == 38) {
System.out.println("Released Up!");
bComp.stopY();
}
if (keyCode == 37) {
System.out.println("Released Left!");
bComp.stopX();
}
if (keyCode == 39) {
System.out.println("Released Right!");
bComp.stopX();
}
if (keyCode == 40) {
System.out.println("Pressed Down!");
bComp.stopY();
}
}
public void keyTyped(KeyEvent e) {
}
}
public class BallComponent extends JComponent {
int xSpeed;
int ySpeed;
public BallComponent(int x, int y) {
super();
setBounds(x, y, 10, 10);
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Ellipse2D.Double ball = new Ellipse2D.Double(0, 0, 9, 9);
g2.setColor(Color.RED);
g2.fill(ball);
g2.draw(ball);
}
public void moveLeft() {
xSpeed = -1;
}
public void moveRight() {
xSpeed = 1;
}
public void moveUp() {
ySpeed = -1;
}
public void moveDown() {
ySpeed = 1;
}
public void tick() {
int x = getX() + xSpeed;
int y = getY() + ySpeed;
setLocation(x, y);
repaint();
}
public void stopY() {
ySpeed = 0;
}
public void stopX() {
xSpeed = 0;
}
}
public class WallComponent extends JComponent {
public WallComponent(int x, int y) {
super();
setBounds(x, y, 40, 40);
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Rectangle wall = new Rectangle(0, 0, 40, 40);
g2.setColor(Color.YELLOW);
g2.fill(wall);
g2.draw(wall);
}
public void checkOverlap(BallComponent bc) {
System.out.println(" me: " + getBounds());
System.out.println("you: " + bc.getBounds());
if (getBounds().intersects(bc.getBounds())) {
bc.stopY();
bc.stopX();
}
}
}
}
Now, you could use "painted" objects, but in that case I would have a virtual concept of a Ball and Wall which you could paint within a single component. These objects would need to provide information about there position and size, which you could, again, check using Rectangle#intersects...
Generally, just try to make a "bounding box" for your objects. This will be the invisible rectangle that goes with the object. Then just do
if(rectangle1.intersects(rectangle2)) ...
The intersects method only works with rectangles, and that's why you need a bounding box.

Categories

Resources