Java: Repaint() method not calling paintComponent - java

I'm making a brick breaker game and I'm stuck on getting the repaint() method working. I've used the debugger and it's not calling the paintComponent method. I am trying to get the paddle to move left or right when the left/right arrow keys are pressed. Hence I am trying to repaint the graphics, but have no luck in it working and can't figure out what I'm doing wrong.
My main class:
public class BrickBreakerGameApp {
public static void main(String[] args) {
int pw = 500;
int ph = 900;
GamePanel gPanel = new GamePanel(pw,ph);
GameFrame gFrame = new GameFrame();
gFrame.getContentPane().add(gPanel); //add game panel to frame
gFrame.addKeyListener(new GamePanel(pw,ph)); //adds the key listener for the game panel frame
gFrame.pack();
gFrame.setVisible(true); //make frame visible
}
}
My frame class:
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
//Global Variables
public int frameWidth = 500;
public int frameHeight = 800;
//create constructor
GameFrame () {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //so app closes properly
this.setPreferredSize(new Dimension(frameWidth, frameHeight)); //set frame size
this.setTitle("Brick Breaker Game");
ImageIcon icon = new ImageIcon("res/brick-breaker-logo.jpg"); //create image icon
this.setIconImage(icon.getImage()); //update frame icon
this.pack();
}
}
My panel class:
import java.awt.Color;
import java.awt.event.*;
import javax.swing.JPanel;
import java.awt.*;
/**
* This class handles the game panel as a whole
*/
public class GamePanel extends JPanel implements ActionListener, KeyListener {
// Global Variables
private Ball ball;
private Paddle paddle;
private GameFrame gameFrame;
private int width;
private int height;
// create a constructor
GamePanel (int gamePanelWidth, int gamePanelHeight) {
this.setWidth(gamePanelWidth);
this.setHeight(gamePanelHeight);
initialiseGame();
this.isVisible();
}
private void initialiseGame() {
ball = new Ball(10, 520, 30, 30); //create the ball object
paddle = new Paddle(this, 50, 550, 100, 10); //creates paddle object
}
// paint all the elements in
public void paintComponent(Graphics g) {
super.paintComponent(g);
// background
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
// paddle
g.setColor(Color.CYAN);
g.fillRect(paddle.getPaddleX(), paddle.getPaddleY(), paddle.getPaddleWidth(), paddle.getPaddleHeight());
// ball
g.setColor(Color.MAGENTA);
g.fillOval(ball.getBallX(), ball.getBallY(), ball.getBallWidth(), ball.getBallHeight());
}
//add any unimplemented methods because we are using an interface
#Override
public void actionPerformed(ActionEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT) {
paddle.moveLeft();
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
paddle.moveRight();
}
this.repaint();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
//create get and set methods for all paddle attributes
public int getWidth() {
return width;
}
public void setWidth(int pWidth) {
this.width = pWidth;
}
public int getHeight() {
return height;
}
public void setHeight(int pHeight) {
this.height = pHeight;
}
}
My Paddle class:
public class Paddle {
//Global Variables
private int paddleWidth;
private int paddleHeight;
private int paddleX; //paddle x position
private int paddleY;
private GamePanel gamePanel;
//create paddle constructor
Paddle() {
}
//create a paddle constructor based off parameters
Paddle(GamePanel gPanel, int pX, int pY, int pWidth, int pHeight) {
//set the values
this.setPaddleWidth(pWidth);
this.setPaddleHeight(pHeight);
this.setPaddleX(pX);
this.setPaddleY(pY);
this.setGamePanel(gPanel);
}
//create get and set methods for all paddle attributes
public int getPaddleWidth() {
return paddleWidth;
}
public void setPaddleWidth(int pWidth) {
this.paddleWidth = pWidth;
}
public int getPaddleHeight() {
return paddleHeight;
}
public void setPaddleHeight(int pHeight) {
this.paddleHeight = pHeight;
}
public int getPaddleX() {
return paddleX;
}
public void setPaddleX(int pX) {
this.paddleX = pX;
}
public int getPaddleY() {
return paddleY;
}
public void setPaddleY(int pY) {
this.paddleY = pY;
}
public GamePanel getGamePanel() {
return gamePanel;
}
public void setGamePanel(GamePanel gPanel) {
this.gamePanel = gPanel;
}
//move the paddle left if it is not already positoned at 0 (far left)
public void moveLeft() {
try {
if(getPaddleX() <= 0) {
setPaddleX(0);
System.out.println("less than 0, x: " + getPaddleX());
}
else if (getPaddleX() > 0) {
setPaddleX(getPaddleX()-10); //to move paddle left -10
// gamePanel.repaint(getPaddleX()+10, getPaddleY(), getPaddleWidth(), getPaddleHeight()); //repaint old position
// gamePanel.repaint(getPaddleX(), getPaddleY(), getPaddleWidth(), getPaddleHeight()); //repaint new position
System.out.println("left, x: " + getPaddleX());
}
}
catch (Exception e) {
}
}
//move the paddle right if it is not already positioned to the far right of the panel
public void moveRight() {
if(getPaddleX() >= gamePanel.getWidth() - getPaddleWidth()) { //dont move the paddle if it is on the right edge of the panel
setPaddleX(gamePanel.getWidth() - getPaddleWidth());
System.out.println("right1, x:" + getPaddleX());
}
else if ((getPaddleX()+getPaddleWidth()) <= gamePanel.getWidth()){ //if the paddle is within the panel bounds
setPaddleX(getPaddleX() + 10); //to move paddle right +10
System.out.println("right, x:" + getPaddleX());
}
}
}

After a little bite of debugging/testing I found out that in your main method:
public class BrickBreakerGameApp {
public static void main(String[] args) {
int pw = 500;
int ph = 900;
GamePanel gPanel = new GamePanel(pw,ph);
GameFrame gFrame = new GameFrame();
gFrame.getContentPane().add(gPanel); //add game panel to frame
gFrame.addKeyListener(new GamePanel(pw,ph)); //adds the key listener for the game panel frame
gFrame.pack();
gFrame.setVisible(true); //make frame visible
}
}
to the gFrame.addKeyListener method call do not add a new GamePanel but the one that you created already, namely:
gFrame.addKeyListener(gPanel); //adds the key listener for the game panel frame

Related

How to use Key Bindings instead of Key Listener?

So I am creating a brick breaker game. I got the paddle moving using key listeners, however I got some advice to use key bindings instead. I've been reading and looking everywhere on this and I think I've kinda got the idea but the implementation is confusing me a bit.
For my paddle, I created it with the paint component. Hence this is what I want to be able to move left and right with the arrow keys. However, all the key bindings explanations I find seem to use JComponent which the paddle is not. Is there a way around it or will I have to make my paddle a JComponent image icon instead? If so would I just load the image into my Paddle class?
Also what's the best way of structuring my code to contain the key bindings? For instance am I better off creating a whole new class for it, or putting it in e.g. my GamePanel class
Any tips?
Here is some of my code so you can get an idea:
Main Class:
public class BrickBreakerGameApp {
public static void main(String[] args) {
int pw = 500;
int ph = 900;
GamePanel gPanel = new GamePanel(pw,ph);
GameFrame gFrame = new GameFrame();
gFrame.getContentPane().add(gPanel); //add game panel to frame
// gFrame.addKeyListener(gPanel); //adds the key listener for the game panel frame
gFrame.pack();
gFrame.setVisible(true); //make frame visible
}
}
GameFrame class:
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
//Global Variables
public int frameWidth = 500;
public int frameHeight = 800;
//create constructor
GameFrame () {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //so app closes properly
this.setPreferredSize(new Dimension(frameWidth, frameHeight)); //set frame size
this.setTitle("Brick Breaker Game");
ImageIcon icon = new ImageIcon("res/brick-breaker-logo.jpg"); //create image icon
this.setIconImage(icon.getImage()); //update frame icon
this.pack();
}
}
GamePanel Class
import java.awt.Color;
import java.awt.event.*;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import java.awt.*;
/**
* This class handles the game panel as a whole
*/
public class GamePanel extends JPanel {
// Global Variables
private Ball ball;
private Paddle paddle;
private Bricks bricks;
private GameFrame gameFrame;
private int width;
private int height;
private int[] bArray = new int[20];
// create a constructor
GamePanel (int gamePanelWidth, int gamePanelHeight) {
this.setWidth(gamePanelWidth);
this.setHeight(gamePanelHeight);
initialiseGame();
this.isVisible();
}
private void initialiseGame() {
ball = new Ball(10, 520, 30, 30); //create the ball object
paddle = new Paddle(this, 50, 700, 100, 10); //creates paddle object
bricks = new Bricks(10, 10, 60, 30, bArray); //creates the bricks object
//for key binding initialisation
MotionAction motion = new MotionAction(paddle, 24);
}
// paint all the elements in
public void paintComponent(Graphics g) {
super.paintComponent(g);
// background
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
// paddle
// g.setColor(Color.CYAN);
// g.fillRect(paddle.getPaddleX(), paddle.getPaddleY(), paddle.getPaddleWidth(), paddle.getPaddleHeight());
// ball
g.setColor(Color.MAGENTA);
g.fillOval(ball.getBallX(), ball.getBallY(), ball.getBallWidth(), ball.getBallHeight());
// brick
g.setColor(Color.GREEN);
g.fillRect(bricks.getBrickX(), bricks.getBrickY(), bricks.getBrickWidth(), bricks.getBrickHeight());
}
//add any unimplemented methods because we are using an interface
/* #Override
public void actionPerformed(ActionEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT) {
paddle.moveLeft();
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
paddle.moveRight();
}
this.repaint();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
} */
//create get and set methods for all panel attributes
public int getWidth() {
return width;
}
public void setWidth(int pWidth) {
this.width = pWidth;
}
public int getHeight() {
return height;
}
public void setHeight(int pHeight) {
this.height = pHeight;
}
}
Paddle class:
import javax.swing.AbstractAction;
import java.awt.event.*;
public class Paddle extends AbstractAction implements ActionListener {
//Global Variables
private int paddleWidth;
private int paddleHeight;
private int paddleX; //paddle x position
private int paddleY;
private GamePanel gamePanel;
//create paddle constructor
Paddle() {
}
//create a paddle constructor based off parameters
Paddle(GamePanel gPanel, int pX, int pY, int pWidth, int pHeight) {
//set the values
this.setPaddleWidth(pWidth);
this.setPaddleHeight(pHeight);
this.setPaddleX(pX);
this.setPaddleY(pY);
this.setGamePanel(gPanel);
}
//create get and set methods for all paddle attributes
public int getPaddleWidth() {
return paddleWidth;
}
public void setPaddleWidth(int pWidth) {
this.paddleWidth = pWidth;
}
public int getPaddleHeight() {
return paddleHeight;
}
public void setPaddleHeight(int pHeight) {
this.paddleHeight = pHeight;
}
public int getPaddleX() {
return paddleX;
}
public void setPaddleX(int pX) {
this.paddleX = pX;
}
public int getPaddleY() {
return paddleY;
}
public void setPaddleY(int pY) {
this.paddleY = pY;
}
public GamePanel getGamePanel() {
return gamePanel;
}
public void setGamePanel(GamePanel gPanel) {
this.gamePanel = gPanel;
}
//move the paddle left if it is not already positoned at 0 (far left)
public void moveLeft() {
try {
if(getPaddleX() <= 0) {
setPaddleX(0);
System.out.println("less than 0, x: " + getPaddleX());
}
else if (getPaddleX() > 0) {
setPaddleX(getPaddleX()-10); //to move paddle left -10
// gamePanel.repaint(getPaddleX()+10, getPaddleY(), getPaddleWidth(), getPaddleHeight()); //repaint old position
// gamePanel.repaint(getPaddleX(), getPaddleY(), getPaddleWidth(), getPaddleHeight()); //repaint new position
System.out.println("left, x: " + getPaddleX());
}
}
catch (Exception e) {
}
}
//move the paddle right if it is not already positioned to the far right of the panel
public void moveRight() {
if(getPaddleX() >= gamePanel.getWidth() - getPaddleWidth()) { //dont move the paddle if it is on the right edge of the panel
setPaddleX(gamePanel.getWidth() - getPaddleWidth());
System.out.println("right1, x:" + getPaddleX());
}
else if ((getPaddleX()+getPaddleWidth()) <= gamePanel.getWidth()){ //if the paddle is within the panel bounds
setPaddleX(getPaddleX() + 10); //to move paddle right +10
System.out.println("right, x:" + getPaddleX());
}
}
}
Here are some of the main resources I've being trying:
This one I tried but got really confused on the best way to integrate it into my own code --> https://tips4java.wordpress.com/2013/06/09/motion-using-the-keyboard/
This one I tried until I realised I don't have jcomponent --> https://coderanch.com/t/606742/java/key-bindings
You could register the key bindings with the GamePanel, or the frame content pane (both extend from JComponent). E.g.:
public class DemoFrame extends JFrame {
public static void main(String[] args) throws Exception {
new DemoFrame().setVisible(true);
}
public DemoFrame() {
setSize(800, 600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
{
String actionId = "left";
KeyStroke keyStroke = KeyStroke.getKeyStroke("LEFT");
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, actionId);
getRootPane().getActionMap().put(actionId, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
actionLeft();
}
});
}
}
private void actionLeft() {
System.out.println("left");
}
}

Mouse Handler in Separate Class not Working

I am writing a Crazy Eights game and trying to have mouse control added in. I just starting writing it but I can't verify if it's working or not. I've added System.out.println() to the pressed and released event calls but no output happens. I just need to get it working and be able to see an output of some kind for debugging. I've also tried to use another example on stackoverflow to help me out but I'm still having issues. The below code is what I'm working with. Let me know if you need to see another class.
Thanks
MouseControl.java
package crazyeightscountdown.CoreClasses;
import java.awt.Canvas;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MouseControl extends MouseAdapter {
public Canvas canvas;
public MouseControl (Canvas c){
this.canvas = c;
}
#Override
public void mouseReleased (MouseEvent e){
System.out.println("Mouse Released.\n");
}
#Override
public void mousePressed (MouseEvent e){
System.out.println("Mouse Pressed.\n");
}
#Override
public void mouseClicked (MouseEvent e){
}
#Override
public void mouseEntered (MouseEvent e){
}
#Override
public void mouseExited (MouseEvent e){
}
}//class
Game.java
package crazyeightscountdown;
import static com.sun.java.accessibility.util.AWTEventMonitor.addMouseListener;
import static crazyeightscountdown.CoreClasses.Constants.CARDPICX;
import crazyeightscountdown.CoreClasses.Deck;
import crazyeightscountdown.CoreClasses.MouseControl;
import crazyeightscountdown.CoreClasses.Player;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
//Sets up parameters for the game window
public class Game implements Runnable {
private Display display;
public int width, height;
public Game(String title, int width, int height) {
this.width = width;
this.height = height;
display = new Display(title, width, height);
StartGame();
}
//create the game decks
//Deck maindeck = new Deck();
public Deck faceupdeck = new Deck();
public Deck facedowndeck = new Deck();
Deck tempdeck = new Deck();
public int deckindex = 0;
public Player playerone = new Player();
public Player playertwo = new Player();
private BufferStrategy bs;
private Graphics g;
private boolean running = false;
private Thread thread;
public void StartGame() {
//setup mouse
addMouseListener (new MouseControl(display.getCanvas()));
//set players
playerone.SetPlayer(1);
playertwo.SetPlayer(2);
//set values to main deck
facedowndeck = facedowndeck.SetDeck(facedowndeck);
//shuffle the deck
facedowndeck = facedowndeck.ShuffleDeck(facedowndeck);
//hand out first deal
FirstDeal();
}
public void FirstDeal() {
int playerindex = 1;
deckindex = 1;
//deal each player 8 cards to start
for (int h = 0; h < 8; h++) {
playerone.hand.card[playerindex] = facedowndeck.card[deckindex];
facedowndeck.card[deckindex].present = false;
playerone.hand.card[playerindex].present = true;
deckindex++;
playertwo.hand.card[playerindex] = facedowndeck.card[deckindex];
facedowndeck.card[deckindex].present = false;
playerone.hand.card[playerindex].present = true;
deckindex++;
playerindex++;
//facedowndeck.Truncate(facedowndeck);
}
//put card face up
faceupdeck.card[1] = facedowndeck.card[deckindex];
deckindex++;
}
private void render() {
bs = display.getCanvas().getBufferStrategy();
if (bs == null) {
display.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
//Clear Screen
g.clearRect(0, 0, width, height);
/******* START DRAWING HERE **********/
//draw player1 deck
for (int f = 1; f < 9; f++) {
g.drawImage(playerone.hand.card[f].pic, (CARDPICX * (f - 1)) + (f * 5), 5, null);
g.drawImage(playertwo.hand.card[f].pic, (CARDPICX * (f - 1)) + (f * 5), 450, null);
}
g.drawImage(faceupdeck.card[1].pic,400, 200, null);
/*********** END DRAWING HERE ***********/
bs.show();
g.dispose();
}
private void tick() {
}
public void run() {
//init();
while (running) {
tick();
render();
}
stop();
}
public synchronized void start() {
if (running) {
return;
}
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop() {
if (!running) {
return;
}
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}//class
Display.java
package crazyeightscountdown;
import java.awt.Canvas;
import java.awt.Dimension;
import javax.swing.JFrame;
//display parameters for the window
public class Display {
public JFrame frame;
public Canvas canvas;
public String title;
public int width, height;
public Display(String title, int width, int height){
this.title = title;
this.width = width;
this.height = height;
createDisplay();
}
private void createDisplay(){
frame = new JFrame(title);
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
canvas = new Canvas();
canvas.setPreferredSize(new Dimension(width, height));
canvas.setMaximumSize(new Dimension(width, height));
canvas.setMinimumSize(new Dimension(width, height));
frame.add(canvas);
frame.pack();
}
public Canvas getCanvas(){
return canvas;
}
}
You have to add the MouseListener to a component: frame.addMouseListener(...)

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);
}
}
}

Help with adding a paddle to a JFrame

This is an excerise i have to complete for a uni course, its not a marked assignment and i could do with a bit of help. I can get the ball to appear on the screen and bounce of the sides, it doesnt matter at the moment if it falls through the bottom of the screen and i can get the paddle to appear on the screen at different times but i cant get them both to appear at the same time. Help please
Here are my classes
MainClass
package movingball;
public class Main
{
public static void main (String []args)
{
MovingBall world = new MovingBall("Moving Ball");
world.setVisible(true);
world.move();
}
}
BallClass
package movingball;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
public class Ball
{
private final int RADIUS = 10;
private Point pos;
private Color ballColour;
private int yChange = 2;
private int xChange = 1;
private int height, width;
private int change;
public Ball (int frameWidth, int frameHeight)
{
change = 3;
ballColour = Color.RED;
width = frameWidth;
height = frameHeight;
pos = new Point();
pos.x = (int)(Math.random() * (width - RADIUS)) + RADIUS;
pos.y = (int)(Math.random() * (height/2 - RADIUS)) + RADIUS;
}
//There are lots of ways you can updateBallState
//Note that the menu bar occupies some of the visible space
public void move()
{
if(pos.y < RADIUS)
{
yChange = - yChange;
}
if(pos.x < RADIUS)
{
xChange = -xChange;
}
if(pos.x > width - RADIUS)
{
xChange = -xChange;
}
if(pos.y < height - RADIUS)
{
pos.translate(xChange, yChange);
}
if(pos.x < width - RADIUS)
{
pos.translate(xChange, yChange);
}
}
public void updateBallState()
{
if (pos.y + change < height - 3*RADIUS)
{
pos.translate(0, change);
// change++; //accelerate
}
}
//This method can be called with a provided graphics context
//to draw the ball in its current state
public void draw(Graphics g)
{
g.setColor(ballColour);
g.fillOval(pos.x - RADIUS, pos.y - RADIUS, 2*RADIUS, 2*RADIUS);
}
public void bounce()
{
yChange = -yChange;
pos.translate(xChange, yChange);
}
public Point getPosition()
{
return pos;
}
}
BallGame
package movingball;
import java.awt.Graphics;
import java.awt.event.*;
public class BallGame extends MovingBall
{
private Paddle myPaddle = new Paddle(FRAME_WIDTH, FRAME_HEIGHT);
public BallGame(String title)
{
super(title);
addKeyListener(new KeyList());
}
public void paint(Graphics g)
{
super.paint(g);
myPaddle.paint(g);
if(isContact())
{
myBall.bounce();
}
else
{
myPaddle.paint(g);
}
}
public boolean isContact()
{
if (myPaddle.area().contains(myBall.getPosition()))
{
return true;
}
else
{
return false;
}
}
public class KeyList extends KeyAdapter
{
public void keyPressed(KeyEvent k)
{
if(k.getKeyCode() == KeyEvent.VK_LEFT)
{
myPaddle.moveLeft();
}
if(k.getKeyCode() == KeyEvent.VK_RIGHT)
{
myPaddle.moveRight();
}
}
}
}
MovingBall class
package movingball;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MovingBall extends JFrame
{
protected final int FRAME_WIDTH = 240;
protected final int FRAME_HEIGHT = 320;
protected Ball myBall = new Ball(FRAME_WIDTH, FRAME_HEIGHT);
public MovingBall (String title)
{
super(title);
setSize(FRAME_WIDTH, FRAME_HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g)
{
super.paint(g);
myBall.draw(g);
}
public void move()
{
while(true)
{
myBall.move();
repaint();
try
{
Thread.sleep(50);
}
catch(InterruptedException e)
{
System.exit(0);
}
}
}
}
Paddle class
package movingball;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Paddle
{
private Color paddleColour = Color.GREEN;
private int x,y;
private int paddleWidth = 20;
private int paddleHeight = 10;
private int move = 5;
/** Creates a new instance of Paddle */
public Paddle(int frameWidth, int frameHeight)
{
x =(int)(Math.random()*(frameWidth - paddleWidth));
y = frameHeight - paddleHeight;
}
public void moveRight()
{
x = x + move;
}
public void moveLeft()
{
x = x - move;
}
public Rectangle area()
{
return new Rectangle(x,y, paddleWidth, paddleHeight);
}
public void paint(Graphics g)
{
g.setColor(paddleColour);
g.fillRect(x,y,paddleWidth, paddleHeight);
}
}
Here are a couple of pointers to get you started. I have a lot of things to suggest but I'll just say this to get you further than you are now. You want your JFrame to be double-buffered. That's the first step. To do this, create a new buffer strategy for your JFrame after making it visible:
world.setVisible(true);
world.createBufferStrategy(2);
As for why your Paddle isn't painting? You're going to kick yourself. Look at this line:
MovingBall world = new MovingBall("Moving Ball");
You're not actually creating a BallGame, which is where the logic for painting the paddle is contained! (BallGame.paint()) Try:
BallGame world = new BallGame("Moving Ball");

Java swing animation looks choppy. How to make it look pro?

UPDATE: semicomplex animation + swing timer = trainwreck. The ultimate source of the problems was the java timer, either the swing or utility version. They are unreliable, especially when performance is compared across operating systems. By implementing a run-of-the-mill thread, the program runs very smoothly on all systems. http://zetcode.com/tutorials/javagamestutorial/animation/. Also, adding Toolkit.getDefaultToolkit().sync() into the paintComponent() method noticeably helps.
I wrote some code that animated smoothly in an awt.Applet (but flickered), then I refactored it to java swing. Now it doesn't flicker but it looks choppy. I've messed with the timer but that doesn't work. Any tips or suggestions for smoothly animating swing components would be greatly appreciated.
import java.util.Random;
import java.util.ArrayList;
import java.awt.event.;
import java.awt.;
import javax.swing.*;
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
public class Ball extends JApplet{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setTitle("And so the ball rolls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initContainer(frame);
frame.pack();
frame.setVisible(true);
}
});
}
public static void initContainer(Container container){
GraphicsPanel graphicsPanel = new GraphicsPanel();
MainPanel mainPanel = new MainPanel(graphicsPanel);
container.add(mainPanel);
graphicsPanel.startTimer();
}
#Override
public void init(){
initContainer(this);
}
}
///////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
class MainPanel extends JPanel {
JLabel label = new JLabel("Particles");
GraphicsPanel gPanel;
public MainPanel(GraphicsPanel gPanel){
this.gPanel = gPanel;
add(gPanel);
add(label);
}
}
///////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
class GraphicsPanel extends JPanel implements MouseListener {
private ArrayList<Particle> ballArr = new ArrayList<Particle>();
private String state="s"; //"s"=spiral, "p"=particle
private int speed=10; //~20 Hz
private Timer timer;
public GraphicsPanel(){
System.out.println("echo from gpanel");
setPreferredSize(new Dimension(500,500));
timer = new Timer(speed, new TimerListener());
addMouseListener(this);
}
public void startTimer(){
timer.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for (Particle b: ballArr){
g.setColor(b.getColor());
g.fillOval(b.getXCoor(),b.getYCoor(),
b.getTheSize(),b.getTheSize());
}
}
public void mousePressed(MouseEvent e) {
ballArr.add(new Particle(e.getX(), e.getY(), state));
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseClicked(MouseEvent e) {}
class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e){
for (Particle b: ballArr)
b.move();
setBackground(Color.WHITE);
repaint();
}
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class Particle
{
private static int instanceCount; {{instanceCount++;}}
private int z = 11, t=1, u=1;
private int[] RGB = new int[3];
private int[] randomizeColor = new int[3];
private double radius, theta;
private int x, y, centerX, centerY, size, spiralDirection=1,
ballSizeLowerBound, ballSizeUpperBound,
radiusLowerBound, radiusUpperBound,
mouseInputX, mouseInputY,
radiusXMultiplier, radiusYMultiplier;
private Color color;
private String state;
private Random random = new Random();
///////////////////////////////////////////////////////////////////////////
public Particle(int x, int y, int centerX, int centerY, int radius,
int theta, int size, Color color){
this.x=x;this.y=y;this.centerX=centerX;this.centerY=centerY;
this.radius=radius;this.theta=theta;this.size=size;this.color=color;
}
public Particle(int mouseInputX, int mouseInputY, String state){
this.mouseInputX=mouseInputX;
this.mouseInputY=mouseInputY;
this.state=state;
//randomize color
RGB[0] = random.nextInt(252);
RGB[1] = random.nextInt(252);
RGB[2] = random.nextInt(252);
randomizeColor[0] = 1+random.nextInt(3);
randomizeColor[0] = 1+random.nextInt(3);
randomizeColor[0] = 1+random.nextInt(3);
centerX=mouseInputX;
centerY=mouseInputY;
if (state.equals("s")){ //setup spiral state
ballSizeLowerBound=5;
ballSizeUpperBound=18;
radiusLowerBound=0;
radiusUpperBound=50;
radiusXMultiplier=1;
radiusYMultiplier=1;
}
if (state.equals("p")){ //setup particle state
ballSizeLowerBound = 15;
ballSizeUpperBound =20 + random.nextInt(15);
radiusLowerBound = 5;
radiusUpperBound = 15+ random.nextInt(34);
radiusXMultiplier=1 + random.nextInt(3);
radiusYMultiplier=1 + random.nextInt(3);
}
size = ballSizeUpperBound-1; //ball size
radius = radiusUpperBound-1;
if (instanceCount %2 == 0) // alternate spiral direction
spiralDirection=-spiralDirection;
}
///////////////////////////////////////////////////////////////////////////
public int getXCoor(){return centerX+x*spiralDirection;}
public int getYCoor(){return centerY+y;}
public int getTheSize(){return size;}
public Color getColor(){return color;}
//////////////////////////////////////////////////////////////////////////
void move(){
//spiral: dr/dt changes at bounds
if (radius > radiusUpperBound || radius < radiusLowerBound)
u = -u;
//spiral shape formula: parametric equation for the
//polar equation radius = theta
x = (int) (radius * radiusXMultiplier * Math.cos(theta));
y = (int) (radius * radiusYMultiplier * Math.sin(theta));
radius += .1*u;
theta += .1;
//ball size formula
if (size == ballSizeUpperBound || size == ballSizeLowerBound)
t = -t;
size += t;
//ball colors change
for (int i = 0; i < RGB.length; i++)
if (RGB[i] >= 250 || RGB[i] <= 4)
randomizeColor[i] = -randomizeColor[i];
RGB[0]+= randomizeColor[0];
RGB[1]+= randomizeColor[1];
RGB[2]+= randomizeColor[2];
color = new Color(RGB[0],RGB[1],RGB[2]);
}
}
Don't set a constant interval timer. Set the timer to go off once -- in the handler
Get the current time (save in frameStartTime)
Do your frame
Set the timer to go off in: interval - (newCurrentTime - frameStartTime)
Should be smoother. If you want to go really pro (and stay in Java), I think you have to consider JavaFX.

Categories

Resources