So I've made custom buttons (yes, there is a reason I'm not using JButtons) and for some reason they're not working. I believe it's to do with the MouseAdapter I'm using, but I can't say for certain. To clarify, I've created the visual aspect of the buttons, and that works, but for some reason the clicking doesn't. I've put in debug code, as you can see, but it's not printing that either. Here's my code:
JPanel:
package com.kraken.towerdefense.graphics;
import com.kraken.towerdefense.TowerDefense;
import com.kraken.towerdefense.scene.Scene;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
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();
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
playGameHighlighted = playGame.contains(e.getPoint());
quitGameHighlighted = quitGame.contains(e.getPoint());
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
if (playGameHighlighted) {
scene = Scene.GAME;
repaint();
System.out.println("playGameHighlighted and mouse clicked");
}
if (quitGameHighlighted) {
running = false;
System.out.println("quitGameHighlighted and mouse clicked");
}
System.out.println("mouse clicked");
}
});
this.tD = tD;
scene = Scene.MENU;
setBackground(new Color(217, 217, 217));
}
#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);
g.setColor(Color.BLACK);
g.setFont(new Font("Gisha", Font.PLAIN, 30));
g.drawString("Tower Defense Menu", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Tower Defense Menu") / 2), (getHeight() / 4) - 15);
g.draw(playGame);
g.draw(quitGame);
}
}
}
JFrame:
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);
}
}
Scene Enum:
package com.kraken.towerdefense.scene;
public enum Scene {
MENU,
GAME
}
So that's my code, any help would be greatly appreciated. Thanks!
MouseMotionListener will only monitor a certain set of mouse events, in order to be notified about mouse click events, you need to use a MouseListener.
Luckily for you, it's really easy to use the MouseAdapter for both...
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
playGameHighlighted = playGame.contains(e.getPoint());
quitGameHighlighted = quitGame.contains(e.getPoint());
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
if (playGameHighlighted) {
scene = Scene.GAME;
repaint();
System.out.println("playGameHighlighted and mouse clicked");
}
if (quitGameHighlighted) {
running = false;
System.out.println("quitGameHighlighted and mouse clicked");
}
System.out.println("mouse clicked");
}
};
addMouseMotionListener(ma);
addMouseListener(ma);
Take a closer look at How to Write a Mouse Listener for more details
Related
Good day,
I am building some basic snake game to learn more about Threading and Graphics in java. And in my eclipse project everything code-wise works not perfect but fine enough for my likings.
Now I tried to export that project from eclipse to a runnable .jar file and somehow suddenly the frame is a different size than i assigned it and everthing is suddenly bigger than it should be which also messes with the rectangle that i set as a "border" for the playing field and so forth.
I'm exporting as Runnable JAR File with the option "Package required libraries into generated jar".
Anybody has an idea as to why that is and what i can do to either fix it or optimize my program to account for these things?
Here's my code for everything that the frame is being used in:
import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Grid extends Canvas{
private JFrame _container;
private int _size;
private int[] food;
private boolean running;
private boolean alive;
private Snake s;
private Rectangle _border;
private int score;
private int highscore;
private BufferStrategy bs;
public Grid() {
super();
_container = new JFrame("Snake Final");
_container.setSize(622,656);
_container.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_container.setVisible(true);
setSize(600, 600);
setVisible(true);
_container.add(this);
createBufferStrategy(2);
bs = getBufferStrategy();
food = new int[2];
highscore = 0;
init();
}
private void init() {
_size = 30;
_border = new Rectangle(0,0,getWidth(),getHeight());
s = new Snake(_size, _border, this);
setKeyListener();
running = true;
generateFood();
run();
}
public void run() {
running = true;
while(running) {
s.update();
if(s.checkFood(food)) {
generateFood();
}
draw();
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
public void gameOver() {
alive = false;
if(score > highscore) {
highscore = score;
}
Graphics g = this.getGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.white);
// g.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
// g.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
}
private void setKeyListener() {
this.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent arg0) {
if(arg0.getKeyCode() == KeyEvent.VK_W) {
if(running) {
if(s.getYDir() == 0) {
s.changeDirection(0, -1);
}
}
}
else if(arg0.getKeyCode() == KeyEvent.VK_S) {
if(running) {
if(s.getYDir() == 0) {
s.changeDirection(0, 1);
}
}
}
else if(arg0.getKeyCode() == KeyEvent.VK_A) {
if(running) {
if(s.getXDir() == 0) {
s.changeDirection(-1, 0);
}
}
}
else if(arg0.getKeyCode() == KeyEvent.VK_D) {
if(running) {
if(s.getXDir() == 0) {
s.changeDirection(1, 0);
}
}
}
else if(arg0.getKeyCode() == KeyEvent.VK_ENTER) {
alive = true;
s.setPosition(4 * _size, 4 * _size);
s.changeDirection(1, 0);
}
}
#Override
public void keyReleased(KeyEvent arg0) {}
#Override
public void keyTyped(KeyEvent arg0) {}
});
}
private void generateFood() {
int gridParts = (getWidth()/_size) - 4;
for(int i = 0; i < food.length; i++) {
food[i] = ((int) (Math.random() * (gridParts)) * _size) + 2 * _size;
}
}
private void draw() {
Graphics bg = bs.getDrawGraphics();
bg.clearRect(0, 0, getWidth(), getHeight());
if(alive) {
Graphics2D bg2d = (Graphics2D) bg;
bg2d.setStroke(new BasicStroke(_size*4));
bg2d.drawRect(0,0,getWidth(),getHeight());
s.show(bg2d);
// bg2d.fillRect(food[0] * _size, food[1] * _size, _size, _size);
score = s.getSize();
bg.setColor(Color.white);
bg.setFont(new Font("SansSerif", Font.PLAIN, 20));
bg.drawString(String.valueOf(score), 56, 35);
bg2d.setColor(Color.RED);
bg2d.fillRect((food[0] + 5), (food[1] + 5), _size - 10, _size - 10);
bg2d.dispose();
}
else {
bg.setColor(Color.black);
bg.fillRect(0, 0, getWidth(), getHeight());
bg.setColor(Color.white);
bg.setFont(new Font("SansSerif", Font.PLAIN, 20));
bg.drawString("Game Over", getWidth() / 2 - 55, getHeight() / 2 - 50);
bg.drawString("Score: " + String.valueOf(score), getWidth() / 2 - 55, getHeight() / 2 - 20);
bg.drawString("Highscore: " + String.valueOf(highscore), getWidth() / 2 - 55, getHeight() / 2 + 10);
bg.drawString("Press Enter to restart", getWidth() / 2 - 55, getHeight() / 2 +40);
}
bs.show();
bg.dispose();
}
}
And sorry it's kind of really messy code but as the old saying goes why refactor it if it actually works kinda alright maybe
_container = new JFrame("Snake Final");
_container.setSize(622,656);
_container.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_container.setVisible(true);
setSize(600, 600);
setVisible(true);
_container.add(this);
You should not be hardcoding the size of a component. The size of your panel is ignored because Swing was designed to be used with layout managers. The default BorderLayout will set the size of you canvas based on the space available in the frame.
Instead you give a suggestion to the layout manager. The code should be something like:
_container = new JFrame("Snake Final");
_container.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//setSize(600, 600);
//setVisible(true); // not needed components are visible by default
setPreferredSize( new Dimension(600, 600) );
_container.add(this);
//_container.setSize(622,656);
_container.pack();
_container.setVisible(true);
So the component is added to the frame before the pack() and the frame is made visible. Then the size of the frame will be determined properly by the pack() method. It will include the preferred size of the canvas and will allow for the decorations of the frame (border and title bar).
Don't know if this will fix your problem with the creation of the Jar file (maybe there is some property that override the packed size?), but this is the better design approach for a Swing frame.
I just had a similar issue, with everything becoming 125% bigger when exported to Jar.
I added this line
System.setProperty("sun.java2d.uiScale", "1");
to my main method, and it solved the issue. More on changing DPI scaling can be found in this question.
(The property is not supported in JDK 8, so you need to run it on JDK 9 or higher. I use 14 and it works fine. Remember to change the CLASSPATH in your environment variables.)
So here's my code. I implemented keyListener and actionListener. I was able to change the coordinates for the panel so It could be able to move left or right. But I have noticed that keyListener doesn't focus very well. I Have to close and rerun the app again and again for it to work and I am able to control it. I have heard of keyBidings but I don't really get it as much. How can I implement keyBindings to make the keyboard responses more focusable?
package brickBreaker;
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.Timer;
import javax.swing.JPanel;
public class game extends JPanel implements KeyListener, ActionListener {
private Timer timer;
private boolean play = false;
private int playerx = 650;
private int ballx=900, bally=500,ballxdir=-1,ballydir=-2;
int delay =8;
public game() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer = new Timer(delay, this);
timer.start();
}
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(1, 1, 1500 ,950);
// user panel
g.setColor(Color.CYAN);
g.fillRect(playerx, 900, 250, 15);
//ball
g.setColor(Color.GREEN);
g.fillOval(ballx, bally, 30, 30);
g.dispose();
}
public void right() {
play = true;
playerx += 20;
}
public void left() {
play = true;
playerx -=20;
}
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key ==KeyEvent.VK_LEFT) {
System.out.print("Left\n");
left();
}if (key == KeyEvent.VK_RIGHT) {
System.out.print("Right\n");
right();
}
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
if (play) {
ballx +=ballxdir;
bally +=ballydir;
if (ballx <0) {
ballxdir =-ballxdir;
}
if (bally <0) {
ballydir =-ballydir;
}
if (ballx <1000) {
ballxdir =-ballxdir;
}
}
repaint();
}
}
Sorry for being extremely late to answer, but I think I've fixed your problem.
You're right - You need to open and close the JPanel again and again before it works. But the issue is this: The JPanel keeps losing focus. So all you have to do is add:
requestFocus(true);
to the paint() method, like so:
public void paint(Graphics g) {
requestFocus(true);
g.setColor(Color.BLACK);
g.fillRect(1, 1, 1500 ,950);
// user panel
g.setColor(Color.CYAN);
g.fillRect(playerx, 900, 250, 15);
//ball
g.setColor(Color.GREEN);
g.fillOval(ballx, bally, 30, 30);
g.dispose();
}
and the program works!
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
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.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.Timer;
public class FinalFlappy implements ActionListener, MouseListener,
KeyListener
{
public static FinalFlappy finalFlappy;
public final int WIDTH = 800, HEIGHT = 800;
public FinalFlappyRend renderer;
public Rectangle bee;
public ArrayList<Rectangle> rect_column;
public int push, yMotion, score;
public boolean gameOver, started;
public Random rand;
public FinalFlappy()
{
JFrame jframe = new JFrame();
Timer timer = new Timer(16, this);
renderer = new FinalFlappyRend();
rand = new Random();
jframe.add(renderer);
jframe.setTitle("Flappy Bee");
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setSize(WIDTH, HEIGHT);
jframe.addMouseListener(this);
jframe.addKeyListener(this);
jframe.setResizable(false);
jframe.setVisible(true);
bee = new Rectangle(WIDTH / 2 - 10, HEIGHT / 2 - 10, 40, 40);
rect_column = new ArrayList<Rectangle>();
addColumn(true);
addColumn(true);
addColumn(true);
addColumn(true);
timer.start();
}
public void addColumn(boolean start)
{
int space = 300;
int width = 60;
int height = 50 + rand.nextInt(300);
if (start)
{
rect_column.add(new Rectangle(WIDTH + width + rect_column.size() * 300, HEIGHT - height - 120, width, height));
rect_column.add(new Rectangle(WIDTH + width + (rect_column.size() - 1) * 300, 0, width, HEIGHT - height - space));
}
else
{
rect_column.add(new Rectangle(rect_column.get(rect_column.size() - 1).x + 600, HEIGHT - height - 120, width, height));
rect_column.add(new Rectangle(rect_column.get(rect_column.size() - 1).x, 0, width, HEIGHT - height - space));
}
}
public void jump()
{
if (gameOver)
{
bee = new Rectangle(WIDTH / 2 - 10, HEIGHT / 2 - 10, 40, 40);
rect_column.clear();
yMotion = 0;
score = 0;
addColumn(true);
addColumn(true);
addColumn(true);
addColumn(true);
gameOver = false;
}
if (!started)
{
started = true;
}
else if (!gameOver)
{
if (yMotion > 0)
{
yMotion = 0;
}
yMotion -= 10;
}
}
#Override
public void actionPerformed(ActionEvent e)
{
int speed = 10;
push++;
if (started)
{
for (int i = 0; i < rect_column.size(); i++)
{
Rectangle column = rect_column.get(i);
column.x -= speed;
}
if (push % 2 == 0 && yMotion < 15)
{
yMotion += 2;
}
for (int i = 0; i < rect_column.size(); i++)
{
Rectangle column = rect_column.get(i);
if (column.x + column.width < 0)
{
rect_column.remove(column);
if (column.y == 0)
{
addColumn(false);
}
}
}
bee.y += yMotion;
for (Rectangle column : rect_column)
{
if (column.y == 0 && bee.x + bee.width / 2 > column.x + column.width / 2 - 10 && bee.x + bee.width / 2 < column.x + column.width / 2 + 10)
{
score++;
}
if (column.intersects(bee))
{
gameOver = true;
if (bee.x <= column.x)
{
bee.x = column.x - bee.width;
}
else
{
if (column.y != 0)
{
bee.y = column.y - bee.height;
}
else if (bee.y < column.height)
{
bee.y = column.height;
}
}
}
}
if (bee.y > HEIGHT - 120 || bee.y < 0)
{
gameOver = true;
}
if (bee.y + yMotion >= HEIGHT - 120)
{
bee.y = HEIGHT - 120 - bee.height;
gameOver = true;
}
}
renderer.repaint();
}
public void paintColumn(Graphics g, Rectangle column)
{
g.setColor(Color.green.darker());
g.fillRect(column.x, column.y, column.width, column.height);
g.fillRect(column.x-20, column.y+column.height-10, column.width+40, 10);
g.fillRect(column.x-20, column.y-10, column.width+40, 10);
}
public void repaint(Graphics g)
{
g.setColor(new Color(153,204,255));
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(new Color(255,255,255));
g.fillOval(50, 50, 100, 100);
g.setColor(Color.YELLOW);
g.fillOval(600, 50, 100, 100);
g.setColor(new Color(156,93,82));
g.fillRect(0, HEIGHT - 120, WIDTH, 120);
g.setColor(new Color(128,255,0));
g.fillRect(0, HEIGHT - 120, WIDTH, 20);
g.setColor(Color.YELLOW);
g.fillRect(bee.x, bee.y, bee.width, bee.height);
for (Rectangle column : rect_column)
{
paintColumn(g, column);
}
g.setColor(Color.white);
g.setFont(new Font("Times New Roman", 1, 100));
if (!started)
{
g.drawString("Push A to start", 100, HEIGHT / 2 - 50);
}
if (gameOver)
{
g.drawString("Game Over", 100, HEIGHT / 2 - 50);
g.drawString("A to replay", 100, HEIGHT / 2 + 90);
}
}
public static void main(String[] args)
{
finalFlappy = new FinalFlappy();
}
#Override
public void mouseClicked(MouseEvent e)
{
jump();
}
#Override
public void keyReleased(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_A)
{
jump();
}
}
#Override
public void mousePressed(MouseEvent e)
{
}
#Override
public void mouseReleased(MouseEvent e)
{
}
#Override
public void mouseEntered(MouseEvent e)
{
}
#Override
public void mouseExited(MouseEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
#Override
public void keyPressed(KeyEvent e)
{
}
}
import java.awt.Graphics;
import javax.swing.JPanel;
public class FinalFlappyRend extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
FinalFlappy.finalFlappy.repaint(g);
}
}
I am working on making a Flappy bird game and I am stuck on how to make and display a timer that updates every second onto the screen
How do I make it start as the game starts and end as the game over pops up?
There are a few ways you might achieve what you're asking. The important thing to remember is, any solution is going to have a degree of drift, meaning that it's unlikely to absolutely accurate, the degree of drift will depend on a lot of factors, so just beware.
You could use a Swing Timer
It's among the safest means for updating the UI on a regular basis, it's also useful if your main loop is already based on a Swing Timer
See How to Use Swing Timers for more details
You could...
Maintain some kind of counter within in your main loop. This assumes that you're using a separate thread (although you can do the same thing with a Swing Timer) and are simply looping at some consistent rate
long tick = System.nanoTime();
long lastUpdate = -1;
while (true) {
long diff = System.nanoTime() - tick;
long seconds = TimeUnit.SECONDS.convert(diff, TimeUnit.NANOSECONDS);
if (seconds != lastUpdate) {
lastUpdate = seconds;
updateTimerLabel(seconds);
}
Thread.sleep(100);
}
This basically runs a while-loop, which calculates the difference between a given point in time (tick) and now, if it's a "second" difference, it then updates the UI (rather than constantly updating the UI with the same value)
The updateTimerLabel method basically updates the label with the specified time, but does so in a manner which is thread safe
protected void updateTimerLabel(long seconds) {
if (EventQueue.isDispatchThread()) {
timerLabel.setText(Long.toString(seconds));
} else {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
updateTimerLabel(seconds);
}
});
}
}
To make and display a timer that updates every second, Put this code in your main class:
private Timer timer = new Timer();
private JLabel timeLabel = new JLabel(" ", JLabel.CENTER);
timer.schedule(new UpdateUITask(), 0, 1000);
private class UpdateUITask extends TimerTask {
int nSeconds = 0;
#Override
public void run() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
timeLabel.setText(String.valueOf(nSeconds++));
}
});
}
}
I am trying to get into graphics programming in Java and am trying a little exercise where I want to make a car go back and forth in a frame. Then I want to make it go faster or slower if I press the up or down arrow keys. However, I am not seeming to be able to add the key listener correctly. To test my code, I am only trying to print a message to the command prompt. Any help will be greatly appreciated!
The code compiles and runs as is. I get a car going back and forth in the frame. The only thing not working is the key listener.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Racing extends JFrame
{
public static class Car extends JComponent implements ActionListener
{
int x=0;
int y=0;
int delta = 10;
Timer timer;
int z = 300;
public Car()
{
timer = new Timer(20,this);
timer.start();
addKeyListener(new KeyAdapter()
{
#Override
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_UP)
System.out.println("The up key was pressed!");
}
});
}
public void paint(Graphics g)
{
super.paintComponent(g);
y = getHeight();
z = getWidth();
g.setColor(Color.BLUE);
g.fillRect(0, 0, z, y);
Polygon polygon = new Polygon();
polygon.addPoint(x + 10, y - 20);
polygon.addPoint(x + 20, y - 30);
polygon.addPoint(x + 30, y - 30);
polygon.addPoint(x + 40, y - 20);
g.setColor(Color.BLACK);
g.fillOval(x + 10, y - 11, 10, 10);
g.fillOval(x + 30, y - 11, 10, 10);
g.setColor(Color.GREEN);
g.fillRect(x, y - 21, 50, 10);
g.setColor(Color.RED);
g.fillPolygon(polygon);
g.setColor(Color.BLUE);
}
public void actionPerformed(ActionEvent e) {
x += delta;
if (x > z-40) {
delta *= -1;
x = (z-40);
} else if (x < 0) {
delta *= -1;
x = 0;
}
repaint();
}
}
public static void main(String[] args)
{
JFrame frame = new JFrame("Racing");
frame.setPreferredSize(new Dimension(600,300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.add(new Car());
frame.setVisible(true);
frame.setFocusable(true);
}
}
The short answer is don't, there are too many issues involved.
Instead, use the key bindings API. See How to Use Key Bindings for more details
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Racing extends JFrame {
public static class Car extends JComponent implements ActionListener {
int x = 0;
int y = 0;
int delta = 10;
Timer timer;
int z = 300;
public Car() {
timer = new Timer(20, this);
timer.start();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "goingUp");
ActionMap am = getActionMap();
am.put("goingUp", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("The up key was pressed!");
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
y = getHeight();
z = getWidth();
g.setColor(Color.BLUE);
g.fillRect(0, 0, z, y);
Polygon polygon = new Polygon();
polygon.addPoint(x + 10, y - 20);
polygon.addPoint(x + 20, y - 30);
polygon.addPoint(x + 30, y - 30);
polygon.addPoint(x + 40, y - 20);
g.setColor(Color.BLACK);
g.fillOval(x + 10, y - 11, 10, 10);
g.fillOval(x + 30, y - 11, 10, 10);
g.setColor(Color.GREEN);
g.fillRect(x, y - 21, 50, 10);
g.setColor(Color.RED);
g.fillPolygon(polygon);
g.setColor(Color.BLUE);
}
public void actionPerformed(ActionEvent e) {
x += delta;
if (x > z - 40) {
delta *= -1;
x = (z - 40);
} else if (x < 0) {
delta *= -1;
x = 0;
}
repaint();
}
}
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();
}
JFrame frame = new JFrame("Racing");
frame.setPreferredSize(new Dimension(600, 300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.add(new Car());
frame.setVisible(true);
frame.setFocusable(true);
}
});
}
}
Also, don't call super.paintComponent from paint, you've basically broken the entire paint chain. Instead, override the paintComponent method itself...
wondered if anyone could point me in the right directon, i have developed a pong game and it needs double buffering due to flickering. Iv tryed some of the post on here to try and make it work, but im still a beginner with the swing awt suff, any help would be amazing thanks.
public class PongPanel extends JPanel implements Runnable {
private int screenWidth = 500;
private int screenHeight = 300;
private boolean isPaused = false;
private boolean isGameOver = false;
private int playToPoints = 10;
private Padel player1,player2;
private Ball ball;
private Thread gameThread;
private Image dbImage;
private Graphics dbg;
public PongPanel() {
setPreferredSize(new Dimension(screenWidth,screenHeight));
setBackground(Color.BLACK);
setDoubleBuffered(true);
setFocusable(true);
requestFocus();
player1 = new Padel(Position.LEFT,screenWidth,screenHeight);
player2 = new Padel(Position.RIGHT,screenWidth,screenHeight);
ball = new Ball(10,screenWidth/2,screenHeight/2,Color.WHITE);
}
public void addNotify(){
super.addNotify();
startGame();
}
private void startGame(){
gameThread = new Thread(this);
gameThread.start();
}
#Override
public void run() {
while (!isGameOver) {
dbImage = createImage(screenWidth,screenHeight);
dbg = this.getGraphics();
if(!isPaused){
if(!gameOverCheck()){
updateGame();
paintComponents(dbg);
}
}else if(isPaused){
dbg.setColor(Color.ORANGE);
dbg.setFont(new Font("serif",Font.BOLD,50));
dbg.drawString("Paused", screenWidth/2-82, screenHeight/2);
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {e.printStackTrace();}
}
}
private boolean gameOverCheck(){
if(player1.getScore() == playToPoints){
dbg.setColor(player1.getColour());
dbg.setFont(new Font("serif",Font.BOLD,50));
dbg.drawString("Player 1 Wins!", screenWidth/2 - 161, screenHeight/2);
setGameOver(true);
return true;
}else if(player2.getScore() == playToPoints){
dbg.setColor(player2.getColour());
dbg.setFont(new Font("serif",Font.BOLD,50));
dbg.drawString("Player 2 Wins!", screenWidth/2 - 161, screenHeight/2);
setGameOver(true);
return true;
}
return false;
}
private void updateGame(){
ball.move(screenWidth,screenHeight,player1,player2);
player1.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
player2.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
}
#Override
public void paintComponents(Graphics g) {
super.paintComponents(g);
dbg.setColor(Color.BLACK);
dbg.fillRect(0, 0, screenWidth+20, screenHeight+20);
dbg.setColor(Color.WHITE);
dbg.drawLine(screenWidth/2, 0, screenWidth/2, screenHeight);
dbg.setFont(new Font("serif",Font.BOLD,32));
dbg.drawString(player1.getScore()+"", screenWidth/2-40, screenHeight - 20);
dbg.drawString(player2.getScore()+"", screenWidth/2+20, screenHeight - 20);
ball.drawBall(dbg);
player1.drawPadel(dbg);
player2.drawPadel(dbg);
}
}
There's a really good tutorial here which describes how to use BufferStrategy to produce non-flickering animation.
The important points are:
Call setIgnoreRepaint(true) on the top-level Canvas to prevent AWT from repainting it, as you'll typically be doing this yourself within the animation loop.
Obtain the Graphics2D object from the BufferStrategy (rather than using the instance passed in via paintComponent(Graphics g).
A must-read about the painting mechanism in AWT and Swing
Painting in AWT and Swing
The basic problem is, you're violating the basic painting system of Swing. Swing uses a "passive rendering" algorithm, where paints are performed only when they need to be. You can make suggestions to the API about when something should be update via the repaint call.
Based on your code, the basic problem is, you're calling paintComponents with your own Graphics context, but the system is is then trashing it with it's paint paint pass, you are fighting the paint system rather then working with it.
If done correctly, Swing components are already double buffered, so you don't need to do anything "extra", other then actual work with the API/system.
I strongly recommend having a look at:
Performing Custom Painting
Painting in AWT and Swing
to get a better understanding of how painting works in Swing.
So, let's start with...
#Override
public void run() {
while (!isGameOver) {
dbImage = createImage(screenWidth, screenHeight);
dbg = this.getGraphics();
if (!isPaused) {
if (!gameOverCheck()) {
updateGame();
paintComponents(dbg);
}
} else if (isPaused) {
dbg.setColor(Color.ORANGE);
dbg.setFont(new Font("serif", Font.BOLD, 50));
dbg.drawString("Paused", screenWidth / 2 - 82, screenHeight / 2);
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Swing is NOT thread safe, you should NOT be updating the UI from outside the context of the Event Dispatching Thread
You should NEVER call any paint method directly. The system will perform this operation when it wants to update your component.
I would strongly recommend having a read of:
Concurrency in Swing
How to Use Swing Timers for a possible solution
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.text.Position;
public class PongPanel extends JPanel implements Runnable {
private int screenWidth = 500;
private int screenHeight = 300;
private boolean isPaused = false;
private boolean isGameOver = false;
private int playToPoints = 10;
private Padel player1, player2;
private Ball ball;
private Timer gameThread;
public PongPanel() {
setPreferredSize(new Dimension(screenWidth, screenHeight));
setBackground(Color.BLACK);
setDoubleBuffered(true);
setFocusable(true);
requestFocus();
player1 = new Padel(Position.LEFT, screenWidth, screenHeight);
player2 = new Padel(Position.RIGHT, screenWidth, screenHeight);
ball = new Ball(10, screenWidth / 2, screenHeight / 2, Color.WHITE);
}
public void addNotify() {
super.addNotify();
startGame();
}
private void startGame() {
gameThread = new Timer(30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateGame();
gameOverCheck();
if (isGameOver) {
repaint();
return;
}
if (!isPaused) {
if (!gameOverCheck()) {
updateGame();
}
}
repaint();
}
});
gameThread.start();
}
private boolean gameOverCheck() {
if (player1.getScore() == playToPoints) {
setGameOver(true);
return true;
} else if (player2.getScore() == playToPoints) {
setGameOver(true);
return true;
}
return false;
}
private void updateGame() {
ball.move(screenWidth, screenHeight, player1, player2);
player1.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
player2.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, screenWidth + 20, screenHeight + 20);
g2d.setColor(Color.WHITE);
g2d.drawLine(screenWidth / 2, 0, screenWidth / 2, screenHeight);
g2d.setFont(new Font("serif", Font.BOLD, 32));
g2d.drawString(player1.getScore() + "", screenWidth / 2 - 40, screenHeight - 20);
g2d.drawString(player2.getScore() + "", screenWidth / 2 + 20, screenHeight - 20);
ball.drawBall(g2d);
player1.drawPadel(g2d);
player2.drawPadel(g2d);
if (isGameOver) {
if (player1.getScore() == playToPoints) {
g2d.setColor(player1.getColour());
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Player 1 Wins!", screenWidth / 2 - 161, screenHeight / 2);
} else if (player2.getScore() == playToPoints) {
g2d.setColor(player2.getColour());
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Player 2 Wins!", screenWidth / 2 - 161, screenHeight / 2);
}
} else if (isPaused) {
g2d.setColor(Color.ORANGE);
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Paused", screenWidth / 2 - 82, screenHeight / 2);
}
g2d.dispose();
}
}
BufferedStrategy
Has already been suggested, BufferStrategy is a viable solution in cases where you want to take complete control off the painting system. It's more complex, but gets you around the oddities of the passive rendering system used by Swing
I think you can just call super(true);
first thing, and this just tells the JPanel that it is double buffered... because one of JPanel's constructors is:
public Jpanel(boolean isDoubleBuffered) {...}
I hope this helps someone even though it is nearly four years later.