Java Game in Fullscreen has way less fps than in window mode - java

The game runs at about 2000 to 3100 fps in normal window mode. If i set the JFrame component to fullscreen and scale up my JPanel to also the same resolution, the fps drops to 20-70.
(This is a prototype, hardcoded resolutions will be later swapped out)
This is my relevant code (if this is not enough, I can provide more):
Game.java
import javax.swing.JFrame;
public class Game {
public static void main(String[] args) {
JFrame window = new JFrame("Platformer Test");
window.setContentPane(new GamePanel(window));
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(true);
//window.setUndecorated(true);
window.pack();
window.setVisible(true);
}
}
GamePanel.java
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
// custom imports
import GameState.GameStateManager;
#SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable, KeyListener{
// dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
// Graphic Device (used for fullscreen)
static GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
private JFrame frame;
// game Thread
private Thread thread;
private boolean running;
private double GameTicks = 60;
// image
private BufferedImage image;
private Graphics2D g;
boolean renderFPS = false;
int frames = 0;
// game state manager
private GameStateManager gsm;
public GamePanel(JFrame frame) {
super();
this.frame = frame;
// set Window Size
setFocusable(true);
setFullscreen(true);
}
private void setFullscreen(boolean t) {
if(t) {
setPreferredSize(new Dimension(1920, 1080));
device.setFullScreenWindow(frame);
requestFocus();
}else {
setSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
requestFocus();
}
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
// create image --> Game is drawn on here
image = new BufferedImage(
WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB
);
// get graphics component of game image
g = (Graphics2D) image.getGraphics();
// starts game clock
running = true;
// adds new GameStateManager
gsm = new GameStateManager();
}
#Override
public void run() {
init();
//game loop setup
double ns = 1000000000 / GameTicks;
double delta = 0;
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
int ticks = 0;
// game loop
while(running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1) {
update();
ticks++;
delta--;
}
if(running)
render();
frames++;
if(System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames + ", ticks: " + ticks);
renderFPS = true;
frames = 0;
ticks = 0;
}
}
}
private void update() {
gsm.update();
}
private void render() {
gsm.render(g);
int fps = 0;
// Draw To Screen
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
//g2.drawImage(image, 0, 0, 1920, 1080, null);
if(renderFPS) {
fps = frames;
}
g2.setColor(Color.red);
g2.drawString("FPS: " + fps, 100,100);
g2.dispose();
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
gsm.keyPressed(e.getKeyCode());
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
gsm.keyReleased(e.getKeyCode());
}
}

Swing is not thread safe, you shouldn't be updating the UI, or the state the UI relies on, from outside the context of the Event Dispatching Thread. This means you shouldn't be using Thread as your "game loop".
See Concurrency in Swing for more details and How to Use Swing Timers for the most common solution.
Don't use JPanel#getGraphics, this is not how painting in Swing is done. Instead, override paintComponent. See Painting in AWT and Swing
and Performing Custom Painting for more details.
Don't use KeyListener, seriously, it's just not worth all the hacking around to make it work. Instead, use the key bindings API
The following, simple, example runs at roughly 171 updates a second (it separates the "timer ticks" and "paint ticks", as in Swing, it's not really possible to know when something is actually rendered to the screen) in both windowed and full screen
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
JFrame frame = new JFrame();
frame.add(new GamePanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// device.setFullScreenWindow(frame);
}
});
}
public class GamePanel extends JPanel {
private Timer timer;
private int ticksPerSecond = 0;
private int paintsPerSecond = 0;
private int xDelta = 1;
private Rectangle boxy = new Rectangle(0, 0, 50, 50);
// Graphic Device (used for fullscreen)
public GamePanel() {
timer = new Timer(5, new ActionListener() {
private Instant lastTick;
private int ticks = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (lastTick == null) {
lastTick = Instant.now();
}
if (Duration.between(lastTick, Instant.now()).toMillis() >= 1000) {
ticksPerSecond = ticks;
lastTick = Instant.now();
ticks = 0;
}
ticks++;
boxy.x += xDelta;
if (boxy.x + boxy.width > getWidth()) {
boxy.x = getWidth() - boxy.width;
xDelta *= -1;
} else if (boxy.x < 0) {
boxy.x = 0;
xDelta *= -1;
}
boxy.y = (getHeight() - boxy.height) / 2;
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
timer.stop();
super.removeNotify();
}
private Instant lastPaint;
private int paints = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lastPaint == null) {
lastPaint = Instant.now();
}
if (Duration.between(lastPaint, Instant.now()).toMillis() >= 1000) {
paintsPerSecond = paints;
lastPaint = Instant.now();
paints = 0;
}
paints++;
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString("Ticks p/s " + ticksPerSecond, 10, 10 + fm.getAscent());
g2d.drawString("Paints p/s " + paintsPerSecond, 10, 10 + fm.getAscent() + fm.getHeight());
g2d.fill(boxy);
g2d.dispose();
}
}
}
If you "really" need absolute control over the painting process, then you should be using a BufferStrategy, see BufferStrategy and BufferCapabilities and the JavaDocs which has an excellent example of how it should be used.
Lots of things might effect the performance of the paint process, for example
A crazy experiment...
So, this example allows you to change the number of entities been rendered on the screen. Each entity is moving in it's own direction and is rotating (no collision detection).
I can get this to run up to roughly 20-25, 0000 entities before I start seeing a (significant) change in the number of updates per second. I rolled it to 100, 000 and it dropped to roughly 42 updates per second.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
GamePanel gamePanel = new GamePanel();
JSlider slider = new JSlider(1, 100000);
slider.setValue(100);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (slider.getValueIsAdjusting()) {
return;
}
gamePanel.setBoxCount(slider.getValue());
}
});
JFrame frame = new JFrame();
frame.add(gamePanel);
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
device.setFullScreenWindow(frame);
}
});
}
public class GamePanel extends JPanel {
private Timer timer;
private int ticksPerSecond = 0;
private int paintsPerSecond = 0;
private List<Box> boxes = new ArrayList<>(100);
// Graphic Device (used for fullscreen)
public GamePanel() {
for (int index = 0; index < 100; index++) {
boxes.add(new Box());
}
timer = new Timer(5, new ActionListener() {
private Instant lastTick;
private int ticks = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (lastTick == null) {
lastTick = Instant.now();
}
if (Duration.between(lastTick, Instant.now()).toMillis() >= 1000) {
ticksPerSecond = ticks;
lastTick = Instant.now();
ticks = 0;
}
ticks++;
for (Box box : boxes) {
box.update(getSize());
}
repaint();
}
});
}
public void setBoxCount(int count) {
if (count < boxes.size()) {
boxes = boxes.subList(0, count);
return;
}
while (boxes.size() < count) {
boxes.add(new Box());
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
timer.stop();
super.removeNotify();
}
private Instant lastPaint;
private int paints = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lastPaint == null) {
lastPaint = Instant.now();
}
if (Duration.between(lastPaint, Instant.now()).toMillis() >= 1000) {
paintsPerSecond = paints;
lastPaint = Instant.now();
paints = 0;
}
paints++;
Graphics2D g2d = (Graphics2D) g.create();
for (Box box : boxes) {
box.paint(g2d);
}
FontMetrics fm = g2d.getFontMetrics();
int yPos = 10 + fm.getAscent();
g2d.drawString("Ticks p/s " + ticksPerSecond, 10, yPos + fm.getAscent());
yPos += fm.getHeight();
g2d.drawString("Paints p/s " + paintsPerSecond, 10, yPos + fm.getAscent());
yPos += fm.getHeight();
g2d.drawString("Count " + boxes.size(), 10, yPos + fm.getAscent());
g2d.dispose();
}
}
private List<Color> colors = new ArrayList<>(Arrays.asList(new Color[]{
Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN,
Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE,
Color.YELLOW
}));
public class Box {
private Color fill;
private Rectangle shape;
private int x;
private int y;
private int xDelta;
private int yDelta;
private int rotationDelta;
private int angle = 0;
public Box() {
Random rnd = new Random();
int width = rnd.nextInt(45) + 5;
int height = rnd.nextInt(45) + 5;
x = rnd.nextInt(400) - width;
y = rnd.nextInt(400) - height;
xDelta = rnd.nextInt(2) + 1;
yDelta = rnd.nextInt(2) + 1;
if (rnd.nextBoolean()) {
xDelta *= -1;
}
if (rnd.nextBoolean()) {
yDelta *= -1;
}
rotationDelta = rnd.nextInt(5);
if (rnd.nextBoolean()) {
rotationDelta *= -1;
}
shape = new Rectangle(x, y, width, height);
Collections.shuffle(colors);
fill = colors.get(0);
shape = new Rectangle(0, 0, width, height);
}
public void update(Dimension bounds) {
x += xDelta;
y += yDelta;
angle += rotationDelta;
if (x + getWidth() > bounds.width) {
x = bounds.width - getWidth();
xDelta *= -1;
} else if (x < 0) {
x = 0;
xDelta *= -1;
}
if (y + getWidth() > bounds.height) {
y = bounds.height - getWidth();
yDelta *= -1;
} else if (y < 0) {
y = 0;
yDelta *= -1;
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return shape.width;
}
public int getHeight() {
return shape.height;
}
public void paint(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
g2d.translate(x, y);
g2d.rotate(Math.toRadians(angle), getWidth() / 2, getHeight() / 2);
g2d.setColor(fill);
g2d.fill(shape);
g2d.dispose();
}
}
}

Related

How do I make a top down view with the ability to rotate with built in java graphics?

I'm trying to make a racing game with the top down view on a static player in the middle of the screen, so instead of moving the player through the map, the map would move around the player. Since it's a racing game, I wanted it to also be somewhat similar to a car, but I've been having trouble with rotating the map around the player and having that work with translations.
I've tried keeping track of the center by adding or subtracting from it, which is what I did for the translations, but it doesn't work with the rotate method. The rotate function wouldn't rotate about the player and instead would rotate the player around some other point, and the translations would snap to a different location from the rotations. I'm sure my approach is flawed, and I have read about layers and such, but I'm not sure what I can do with them or how to use them. Also, any recommendations as to how to use java graphics in general would be greatly appreciated!
This is what I have in my main:
import javax.swing.JFrame;
import java.awt.BorderLayout;
public class game
{
public static void main(String []args)
{
JFrame frame = new JFrame();
final int FRAME_WIDTH = 1000;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Map b = new Map();
frame.add(b,BorderLayout.CENTER);
frame.setVisible(true);
b.startAnimation();
}
}
And this is the class that handles all the graphics
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Map extends JComponent implements Runnable, KeyListener
{
private int speed = 5;
private int xcenter = 500; // starts on player
private int ycenter = 300;
private double angle = 0.0;
private int[] xcords = {xcenter+10, xcenter, xcenter+20};
private int[] ycords = {ycenter-10, ycenter+20, ycenter+20};
private boolean moveNorth = false;
private boolean moveEast = false;
private boolean moveSouth = false;
private boolean moveWest = false;
public Map()
{
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void startAnimation()
{
Thread t = new Thread(this);
t.start();
}
public void paintComponent(Graphics g)
{
g.fillPolygon(xcords, ycords, 3);
// move screen
if(moveNorth)
{
ycenter += speed;
g.translate(xcenter, ycenter);
}
else if(moveEast)
{
angle += ((1 * Math.PI/180) % (2 * Math.PI));
((Graphics2D) g).rotate(angle, 0, 0);
}
else if(moveSouth)
{
System.out.println(xcenter + ", " + ycenter);
ycenter -= speed;
((Graphics2D) g).rotate(angle, 0, 0);
g.translate(xcenter, ycenter);
}
else if(moveWest)
{
angle -= Math.toRadians(1) % (2 * Math.PI);
((Graphics2D) g).rotate(angle, 0, 0);
}
for(int i = -10; i < 21; i++)
{
g.drawLine(i * 50, -1000, i * 50, 1000);
g.drawLine(-1000, i * 50, 1000, i * 50);
}
g.drawOval(0, 0, 35, 35);
}
public void run()
{
while (true)
{
try
{
if(moveNorth || moveEast || moveSouth || moveWest)
{
repaint();
}
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
}
}
public void keyPressed(KeyEvent e)
{
if(e.getExtendedKeyCode() == 68) // d
{
moveEast = true;
}
else if(e.getExtendedKeyCode() == 87) // w
{
moveNorth = true;
}
else if(e.getExtendedKeyCode() == 65) // a
{
moveWest = true;
}
else if(e.getExtendedKeyCode() == 83) // s
{
moveSouth = true;
}
}
public void keyReleased(KeyEvent e)
{
moveNorth = false;
moveEast = false;
moveSouth = false;
moveWest = false;
}
public void keyTyped(KeyEvent e)
{
}
}
You have to keep in mind that transformations are compounding, so if you rotate the Graphics context by 45 degrees, everything painted after it will be rotated 45 degrees (around the point of rotation), if you rotate it again by 45 degrees, everything painted after it will be rotated a total of 90 degrees.
If you want to paint additional content after a transformation, then you either need to undo the transformation, or, preferably, take a snapshot of the Graphics context and dispose of it (the snapshot) when you're done.
You also need to beware of the point of rotation, Graphics2D#rotate(double) will rotate the Graphics around the point of origin (ie 0x0), which may not be desirable. You can change this by either changing the origin point (ie translate) or using Graphics2D#rotate(double, double, double), which allows you to define the point of rotation.
For example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
enum Direction {
LEFT, RIGHT;
}
protected enum InputAction {
PRESSED_LEFT, PRESSED_RIGHT, RELEASED_LEFT, RELEASED_RIGHT
}
private BufferedImage car;
private BufferedImage road;
private Set<Direction> directions = new TreeSet<>();
private double directionOfRotation = 0;
public TestPane() throws IOException {
car = ImageIO.read(getClass().getResource("/images/Car.png"));
road = ImageIO.read(getClass().getResource("/images/Road.png"));
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), InputAction.PRESSED_LEFT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), InputAction.RELEASED_LEFT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), InputAction.PRESSED_RIGHT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), InputAction.RELEASED_RIGHT);
am.put(InputAction.PRESSED_LEFT, new DirectionAction(Direction.LEFT, true));
am.put(InputAction.RELEASED_LEFT, new DirectionAction(Direction.LEFT, false));
am.put(InputAction.PRESSED_RIGHT, new DirectionAction(Direction.RIGHT, true));
am.put(InputAction.RELEASED_RIGHT, new DirectionAction(Direction.RIGHT, false));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (directions.contains(Direction.RIGHT)) {
directionOfRotation += 1;
} else if (directions.contains(Direction.LEFT)) {
directionOfRotation -= 1;
}
// No doughnuts for you :P
if (directionOfRotation > 180) {
directionOfRotation = 180;
} else if (directionOfRotation < -180) {
directionOfRotation = -180;
}
repaint();
}
});
timer.start();
}
protected void setDirectionActive(Direction direction, boolean active) {
if (active) {
directions.add(direction);
} else {
directions.remove(direction);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(213, 216);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
drawRoadSurface(g2d);
drawCar(g2d);
g2d.dispose();
}
protected void drawCar(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
int x = (getWidth() - car.getWidth()) / 2;
int y = (getHeight() - car.getHeight()) / 2;
g2d.drawImage(car, x, y, this);
g2d.dispose();
}
protected void drawRoadSurface(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
// This sets the point of rotation at the center of the window
int midX = getWidth() / 2;
int midY = getHeight() / 2;
g2d.rotate(Math.toRadians(directionOfRotation), midX, midY);
// We then need to offset the top/left corner so that what
// we want draw appears to be in the center of the window,
// and thus will be rotated around it's center
int x = midX - (road.getWidth() / 2);
int y = midY - (road.getHeight() / 2);
g2d.drawImage(road, x, y, this);
g2d.dispose();
}
protected class DirectionAction extends AbstractAction {
private Direction direction;
private boolean active;
public DirectionAction(Direction direction, boolean active) {
this.direction = direction;
this.active = active;
}
#Override
public void actionPerformed(ActionEvent e) {
setDirectionActive(direction, active);
}
}
}
}

Why is my JFrame blank in my breakout game?

So I'm new at java and need some help with my breakout game. My JFrame is just blank and i don't know how to fix it?
So I have a ball class, paddle class, canvas class and a brick class as well as a main class. In my canvas class I set all functions the ball, paddle and bricks has etc. In brick class I draw the bricks. And in my main I do the JFrame but it's blank
Main class :
public class Main {
public static void main(String[] args){
JFrame frame = new JFrame();
Canvas c = new Canvas();
frame.add(c);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I expect the JFrame to show the game instead of just blank window
package breakout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.KeyEvent;
import breakout.Bricks.Type;
public class Canvas extends JPanel implements ActionListener, MouseMotionListener, MouseListener, KeyListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int HEIGHT = 600;
public static final int WIDTH = 720;
private int horizontalCount;
private BufferedImage image;
private Graphics2D bufferedGraphics;
private Timer time;
private static final Font endFont = new Font(Font.SANS_SERIF, Font.BOLD, 20);
private static final Font scoreFont = new Font(Font.SANS_SERIF, Font.BOLD, 15);
private Paddle player;
private Ball ball;
ArrayList<ArrayList<Bricks>> bricks;
public Canvas() {
super();
setPreferredSize( new Dimension(WIDTH, HEIGHT));
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
bufferedGraphics = image.createGraphics();
time = new Timer (15, this);
player = new Paddle((WIDTH/2)-(Paddle.PADDLE_WIDTH/2));
ball = new Ball (((player.getX() + (Paddle.PADDLE_WIDTH / 2 )) - (Ball.DIAMETER / 2)), (Paddle.Y_POS - (Ball.DIAMETER + 10 )), -5, -5);
bricks = new ArrayList<ArrayList<Bricks>>();
horizontalCount = WIDTH / Bricks.BRICK_WIDTH;
for(int i = 0; i < 8; ++i) {
ArrayList<Bricks> temp = new ArrayList<Bricks>();
#SuppressWarnings("unused")
Type rowColor = null;
switch(i) {
case 0 :
case 2:
rowColor = Type.LOW;
break;
case 1 :
case 3 :
case 5 :
rowColor = Type.MEDIUM;
break;
case 4 :
case 6 :
rowColor = Type.HIGH;
break;
case 7 :
default :
rowColor = Type.ULTRA;
break;
}
for(int j = 0; j < horizontalCount; ++j) {
Bricks tempBrick = new Bricks();
temp.add(tempBrick);
}
bricks.add(temp);
addMouseMotionListener(this);
addMouseListener(this);
addKeyListener(this);
requestFocus();
}
}
public void actionPerformed(ActionEvent e) {
checkCollisions();
ball.Move();
for(int i = 0; i < bricks.size(); ++i) {
ArrayList<Bricks> al = bricks.get(i);
for(int j = 0; j < al.size(); ++j) {
Bricks b = al.get(j);
if(b.dead()) {
al.remove(b);
}
}
}
repaint();
}
private void checkCollisions() {
if(player.hitPaddle(ball)) {
ball.setDY(ball.getDY() * -1);
return;
}
if(ball.getX() >= (WIDTH - Ball.DIAMETER) || ball.getX() <= 0) {
ball.setDX(ball.getDX() * -1);
}
if(ball.getY() > (Paddle.Y_POS + Paddle.PADDLE_HEIGHT + 10)) {
resetBall();
}
if(ball.getY() <= 0) {
ball.setDY(ball.getDY() * -1);
}
int brickRowActive = 0;
for(ArrayList<Bricks> alb : bricks) {
if(alb.size() == horizontalCount) {
++brickRowActive;
}
}
for(int i = (brickRowActive==0) ? 0 : (brickRowActive - 1); i < bricks.size(); ++i) {
for(Bricks b : bricks.get(i)) {
if(b.hitBy(ball)) {
player.setScore(player.getScore() + b.getBrickType().getPoints());
b.decrementType();
}
}
}
}
private void resetBall() {
if(gameOver()) {
time.stop();
return;
}
ball.setX(WIDTH/2);
ball.setDY((HEIGHT/2) + 80);
player.setLives(player.getLives() -1);
player.setScore(player.getScore() <= 1);
}
private boolean gameOver() {
if(player.getLives() <= 1) {
return true;
}
return false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
bufferedGraphics.clearRect(0, 0, WIDTH, HEIGHT);
player.drawPaddle(bufferedGraphics);
player.drawBall(bufferedGraphics);
for(ArrayList<Bricks> row : bricks) {
for(Bricks b : row) {
b.drawBrick(bufferedGraphics);
}
}
bufferedGraphics.setFont(scoreFont);
bufferedGraphics.drawString("Score: " + player.getScore(), 10, 25);
if(gameOver() && ball.getY() >= HEIGHT) {
bufferedGraphics.setColor(Color.black);
bufferedGraphics.setFont(endFont);
bufferedGraphics.drawString("Game Over Score: " + player.getScore(), (WIDTH /2) -85, (HEIGHT/2));
}
if(empty()) {
bufferedGraphics.setColor(Color.black);
bufferedGraphics.setFont(endFont);
bufferedGraphics.drawString("You won. Score: " + player.getScore(), (WIDTH /2) -85, (HEIGHT /2));
time.stop();
}
g.drawImage(image, 0, 0, this);
Toolkit.getDefaultToolkit().sync();
}
private boolean empty() {
for(ArrayList<Bricks> al : bricks) {
if(al.size() != 0) {
return false;
}
}
return true;
}
#Override
public void mouseMoved(MouseEvent e) {
player.setX(e.getX() - (Paddle.PADDLE_WIDTH / 2));
}
#Override
public void mouseClicked(MouseEvent e) {
if(time.isRunning()) {
return;
}
time.start();
}
#Override
public void mouseDragged(MouseEvent e) { }
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
#Override
public void keyPressed(KeyEvent arg0) {}
#Override
public void keyReleased(KeyEvent arg0) {}
#Override
public void keyTyped(KeyEvent arg0) {}
}
Preparing an MCVE, as required in SO, not only it makes helping much easier.
In many case, while preparing one, you are likely to find the problem, so it is a good debugging tool.
To answer "why is my JFrame blank ?" you could create the minimal code example like the following (copy-paste the entire code into GameBoard.java and run):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameBoard extends JPanel {
static final int HEIGHT = 600, WIDTH = 720, BRICK_ROWS = 8;
private final int horizontalCount;
private static final Font scoreFont = new Font(Font.SANS_SERIF, Font.BOLD, 15);
private final Paddle player;
private final Ball ball;
ArrayList<ArrayList<Brick>> bricks;
public GameBoard() {
super();
setPreferredSize( new Dimension(WIDTH, HEIGHT));
player = new Paddle(WIDTH/2-Paddle.PADDLE_WIDTH/2);
ball = new Ball (player.getX() + Paddle.PADDLE_WIDTH / 2 - Ball.DIAMETER / 2,
Paddle.Y_POS - (Ball.DIAMETER + 10 ));
bricks = new ArrayList<>();
horizontalCount = WIDTH / Brick.BRICK_WIDTH;
for(int i = 0; i < BRICK_ROWS; ++i) {
ArrayList<Brick> temp = new ArrayList<>();
for(int j = 0; j < horizontalCount; ++j) {
Brick tempBrick = new Brick(j*Brick.BRICK_WIDTH , Brick.BRICK_YPOS + i*Brick.BRICK_HEIGHT);
temp.add(tempBrick);
}
bricks.add(temp);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
g2D.clearRect(0, 0, WIDTH, HEIGHT);
player.drawPaddle(g2D);
ball.drawBall(g2D);
for(ArrayList<Brick> row : bricks) {
for(Brick b : row) {
b.drawBrick(g2D);
}
}
g2D.setFont(scoreFont);
g2D.drawString("Score: " + player.getScore(), 10, 25);
}
}
class Paddle{
public final static int PADDLE_WIDTH = 100, PADDLE_HEIGHT= 30, Y_POS = GameBoard.HEIGHT - 2* PADDLE_HEIGHT;
private int xPos, score;
Paddle(int xPos) {
this.xPos = xPos;
}
void setX(int xPos) {this.xPos = xPos;}
int getX() {return xPos;}
String getScore() {
return String.valueOf(score);
}
void drawPaddle(Graphics2D g2D) {
g2D.setColor(Color.GREEN);
g2D.fillRect(xPos, Y_POS, PADDLE_WIDTH, PADDLE_HEIGHT);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(400,250);
frame.add(new GameBoard());
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}
class Brick{
final static int BRICK_WIDTH = 80, BRICK_HEIGHT = 15, BRICK_YPOS = 50;
int xPos, yPos;
Brick(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBrick(Graphics2D g2D) {
g2D.setColor(Color.RED);
g2D.fillRect(xPos, yPos, BRICK_WIDTH, BRICK_HEIGHT);
g2D.setColor(Color.BLACK);
g2D.drawRect(xPos, yPos, BRICK_WIDTH, BRICK_HEIGHT);
}
}
class Ball{
final static int DIAMETER = 40;
int xPos, yPos;
Ball(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBall(Graphics2D g2D) {
g2D.setColor(Color.BLUE);
g2D.fillOval(xPos, yPos, DIAMETER, DIAMETER);
}
}
This produces the following result, which I believe can serve as the basis of what you wanted to achieve:
Now start adding the missing functionality and see what breaks it.

Bricks array not forming

I'm having troubles with my Brick Breaker game that I am making in my 12th grade programming class.
Currently, it seems that only the brick in Bricks.render does draw. When I output what the X and Y of the bricks should be it puts each brick in the right position. So I believe I'm drawing it wrong.
MCVE:
Game.java
package main;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferStrategy;
public class Game extends Canvas implements Runnable {
// variables
private static final long serialVersionUID = 1L;
public static final int WIDTH = 480, HEIGHT = 600, SCALE = 1;
public final String TITLE = "Brick Breaker";
private static boolean running = false;
private Thread thread;
private int fpsDisplay = 0;
Bricks[] level1 = new Bricks[16];
// creates a new Game (which creates the game window)
public static void main(String[] args) {
new Game();
}
public Game() {
new GameWindow(WIDTH, HEIGHT, SCALE, TITLE, this);
System.out.println("[" + TITLE + " has started.]");
}
// called in GameWindow, starts the thread and game loop
public synchronized void start() {
if (running)
return;
thread = new Thread(this);
thread.start();
running = true;
}
public void init() {
this.createlevel1();
}
// used for updating what's happening in the game
private void tick() {
}
// renders the graphics
private void render() {
// --------- INIT GRAPHICS --------- //
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D) g;
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontMetrics fontMetrics = g2d.getFontMetrics();
// --------- INIT GRAPHICS --------- //
// clearing background
super.paint(g);
g2d.setColor(Color.black);
g2d.drawString(Integer.toString(fpsDisplay) + " FPS",
WIDTH - 9 - fontMetrics.stringWidth(Integer.toString(fpsDisplay) + " FPS"), 14);
// render level 1
for (int i = 0; i < 16; i++) {
level1[i].render(g);
}
// render level 1
// ---------
g.dispose();
g2d.dispose();
bs.show();
// ---------
}
public void createlevel1() {
int brickCount = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
level1[brickCount] = new Bricks(50 + i * 100, 50 + j * 100);
System.out.println("X: " + (50 + i * 100) + " Y: " + (50 + j * 100));
brickCount++;
}
}
}
// 60 tps game loop
public void run() {
init();
long lastTime = System.nanoTime();
final double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
if (delta >= 1) {
tick();
updates++;
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("Ticks: " + updates + ", FPS: " + frames);
fpsDisplay = frames;
updates = 0;
frames = 0;
}
}
stop();
}
// called at the end of the loop, stops the thread
public synchronized void stop() {
try {
thread.join();
running = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Bricks.java
package main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
#SuppressWarnings("unused")
public class Bricks {
private int x, y, w, h;
private Boolean isAlive;
private Color randColour;
public Bricks(int x, int y) {
x = this.x;
y = this.y;
w = 75;
h = 25;
isAlive = true;
randColour = Color.blue;
}
public void render(Graphics g) {
g.setColor(randColour);
g.fillRect(x, y, w, h);
}
}
GameWindow.java
package main;
import java.awt.Canvas;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
public class GameWindow extends Canvas {
private static final long serialVersionUID = -5131420759044457697L;
public GameWindow(int WIDTH, int HEIGHT, int SCALE, String TITLE, Game game) {
JFrame frame = new JFrame(TITLE);
frame.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
frame.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
frame.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
try {
frame.setIconImage(ImageIO.read(new File("res/Brick-icon.png")));
} catch (IOException e) {
frame.setIconImage(null);
e.printStackTrace();
System.out.println("ICON ERROR: Icon image not found. (Default Icon is set.)");
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.add(game);
frame.pack();
game.start();
// displays the window 400ms later to allow for the game to fully load
try {
Thread.sleep(400);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
frame.setVisible(true);
}
}
In Bricks.java:
x = this.x;
y = this.y;
should be:
this.x = x;
this.y = y;

The repaint method stops working for short delays

I'm trying to create a simple panel where a 2-dimensional ball is bouncing up and down. I can't get it to work because for some reason I can't call the repaint method more than once a second. The design is basically that there is an object that can be given "an impulse" with the method move(). Everytime the evaluatePosition method is called, the current position will be calculated through the time that has passed, the velocity and the acceleration. The code for the panel is:
public class Display extends JPanel {
private MovableObject object = new MovableObject(new Ellipse2D.Double(5,5,50,50));
private static final int DELAY = 1000;
public Display(){
object.move(50,50);
Timer timer = new Timer(DELAY, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
object.evaluatePosition();
repaint();
}
});
timer.start();
}
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawOval((int)object.getPosition().getX(), (int)object.getPosition.getY()
(int)object.getShape().getWidth(), object.getShape().getHeight());
}
This code works for DELAY=1000 but not for DELAY=100 or DELAY=10 and so on. I read some example code here on SO but they all seem to me like what I already did. So why is my code not working?
EDIT (2016-01-30):
Since it really seems to be a performance issue, here's the code for the MovableObject (I just thought it would be irrelevant and you will probably see why):
public class MovableObject {
// I would really like to use Shape instead of Ellipse2D so that
// objects of any shape can be created
private Ellipse2D.Double shape;
private Point position;
// Vector is my own class. I want to have some easy vector addition and
// multiplication and magnitude methods
private Vector velocity = new Vector(0, 0);
private Vector acceleration = new Vector(0, 0);
private Date lastEvaluation = new Date();
public MovableObject(Ellipse2D.Double objectShape){
shape = objectShape;
}
public void evaluatePosition(){
Date currentTime = new Date();
long deltaTInS = (currentTime.getTime()-lastEvaluation.getTime())/1000;
// s = s_0 + v*t + 0.5*a*t^2
position = new Point((int)position.getX()+ (int)(velocity.getX()*deltaTInS) + (int)(0.5*acceleration.getX()*deltaTInS*deltaTInS),
(int)position.getY()+ (int)(velocity.getY()*deltaTInS) + (int)(0.5*acceleration.getY()*deltaTInS*deltaTInS));
lastEvaluation = currentTime;
}
}
public void move(Vector vector){
velocity = velocity.add(vector);
evaluatePosition();
}
public Point getPosition(){
return position;
}
public Ellipse2D.Double getShape(){
return shape;
}
My move method does not change position but velocity. Please notice that I just changed the shape Object from Shape to Ellipse2D for testing if my code has a performance issue because of the additional code. So if you think this is more complex than it needs to be: I actually want to add some complexity so that the MovableObject can have the shape of any subclass of shape. I've seen a lot of code that seemed more complex to me and rendered fast. So I'd like to know what's wrong with this (besides the fact that it's a bit too complex for just rendering an ellipse).
Okay, so this is a simple example, based on the out-of-context code snippet you left which doesn't seem to have any problems. It has variable speed controlled by a simple slider...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Display extends JPanel {
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("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Display());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private MovableObject object = new MovableObject(new Ellipse2D.Double(5, 5, 50, 50));
private int delay = 40;
private Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
object.evaluatePosition(getSize());
repaint();
}
});
private JSlider slider = new JSlider(5, 1000);
public Display() {
object.move(50, 50);
slider = new JSlider(5, 1000);
slider.setSnapToTicks(true);
slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(5);
setLayout(new BorderLayout());
add(slider, BorderLayout.SOUTH);
// This is simply designed to put an artificate delay between the
// change listener and the time the update takes place, the intention
// is to stop it from pausing the "main" timer...
Timer delay = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (timer != null) {
timer.stop();
}
timer.setDelay(slider.getValue());
timer.start();
}
});
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
delay.restart();
repaint();
}
});
slider.setValue(40);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.draw(object.getTranslatedShape());
FontMetrics fm = g2.getFontMetrics();
String text = Integer.toString(slider.getValue());
g2.drawString(text, 0, fm.getAscent());
g2.dispose();
}
public class MovableObject {
private Shape shape;
private Point location;
private int xDelta, yDelta;
public MovableObject(Shape shape) {
this.shape = shape;
location = shape.getBounds().getLocation();
Random rnd = new Random();
xDelta = rnd.nextInt(8);
yDelta = rnd.nextInt(8);
if (rnd.nextBoolean()) {
xDelta *= -1;
}
if (rnd.nextBoolean()) {
yDelta *= -1;
}
}
public void move(int x, int y) {
location.setLocation(x, y);
}
public void evaluatePosition(Dimension bounds) {
int x = location.x + xDelta;
int y = location.y + yDelta;
if (x < 0) {
x = 0;
xDelta *= -1;
} else if (x + shape.getBounds().width > bounds.width) {
x = bounds.width - shape.getBounds().width;
xDelta *= -1;
}
if (y < 0) {
y = 0;
yDelta *= -1;
} else if (y + shape.getBounds().height > bounds.height) {
y = bounds.height - shape.getBounds().height;
yDelta *= -1;
}
location.setLocation(x, y);
}
public Shape getTranslatedShape() {
PathIterator pi = shape.getPathIterator(AffineTransform.getTranslateInstance(location.x, location.y));
GeneralPath path = new GeneralPath();
path.append(pi, true);
return path;
}
}
}
You could also have a look at
Swing animation running extremely slow
Rotating multiple images causing flickering. Java Graphics2D
Java Bouncing Ball
for some more examples...

moving buffered image to specific coordinates

I am trying to move the blue square around the grid by pressing the arrow keys. I already set up the KeyListener but when I repaint the frame, I have to move the frame around in order for it to update. How do I make it a smooth animation (moving the square from one coordinate to another)?
Example:
if (keyCode == KeyEvent.VK_LEFT){
x5=x5-xChange;
frame.repaint();
}
My images are buffered images and are all set to specific coordinates.
Here is my graphics class:
public static class myGraphicsPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(block1,x1,y1,null);
g.drawImage(block2,x2,y2,null);
g.drawImage(block3,x3,y3,null);
g.drawImage(block4,x4,y4,null);
g.drawImage(block5,x5,y5,null);
g.drawImage(block6,x6,y6,null);
g.drawImage(block7,x7,y7,null);
g.drawImage(block8,x8,y8,null);
g.drawImage(blue,x9,y9,null);
}
You question is lacking a lot of information, how is the screen updated? How is the screen created? What relationship do the objects have with each other?
Many of these questions could be answered with a MCVE
Animation is the illusion of change over time. So you need a few things. You need some way to change the current state and some way to trigger and update to the UI over time.
The simplest (and safest) way to do this within Swing is to use a Swing Timer
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.Timer;
public class TestAnimation {
public static void main(String[] args) {
new TestAnimation();
}
public TestAnimation() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage img;
private int xDelta, yDelta;
private int xPos, yPos;
public TestPane() throws IOException {
img = ImageIO.read(new File("/Users/shane/Dropbox/MegaTokyo/thumnails/2.jpg"));
Random rnd = new Random();
do {
xDelta = rnd.nextInt(4);
} while (xDelta == 0);
do {
yDelta = rnd.nextInt(4);
} while (yDelta == 0);
if (rnd.nextBoolean()) {
xDelta *= -1;
}
if (rnd.nextBoolean()) {
yDelta *= -1;
}
xPos = (getPreferredSize().width - img.getWidth()) / 2;
yPos = (getPreferredSize().height - img.getHeight()) / 2;
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
yPos += yDelta;
if (xPos + img.getWidth() > getWidth()) {
xPos = getWidth() - img.getWidth();
xDelta *= -1;
} else if (xPos < 0) {
xPos = 0;
xDelta *= -1;
}
if (yPos + img.getHeight() > getHeight()) {
yPos = getHeight() - img.getHeight();
yDelta *= -1;
} else if (yPos < 0) {
yPos = 0;
yDelta *= -1;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(img, xPos, yPos, this);
g2d.dispose();
}
}
}
}
Have a closer look at Concurrency in Swing and w to Use Swing Timers for more details
But, how do you do this using the key board? Surprisingly, it's very similar.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
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 TestAnimation {
public static void main(String[] args) {
new TestAnimation();
}
public TestAnimation() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage img;
private int xPos, yPos;
public TestPane() throws IOException {
img = ImageIO.read(new File("/Users/shane/Dropbox/MegaTokyo/thumnails/2.jpg"));
xPos = (getPreferredSize().width - img.getWidth()) / 2;
yPos = (getPreferredSize().height - img.getHeight()) / 2;
addKeyBinding("up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new KeyAction(0, -4));
addKeyBinding("down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new KeyAction(0, 4));
addKeyBinding("left", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), new KeyAction(-4, 0));
addKeyBinding("right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), new KeyAction(4, 0));
}
protected void addKeyBinding(String name, KeyStroke keyStroke, Action action) {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(keyStroke, name);
am.put(name, action);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(img, xPos, yPos, this);
g2d.dispose();
}
}
public class KeyAction extends AbstractAction {
private int xDelta, yDelta;
public KeyAction(int xDelta, int yDelta) {
this.xDelta = xDelta;
this.yDelta = yDelta;
}
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
yPos += yDelta;
if (xPos + img.getWidth() > getWidth()) {
xPos = getWidth() - img.getWidth();
} else if (xPos < 0) {
xPos = 0;
}
if (yPos + img.getHeight() > getHeight()) {
yPos = getHeight() - img.getHeight();
} else if (yPos < 0) {
yPos = 0;
}
repaint();
}
}
}
}
Have a look at How to use key bindings and How to use actions for more details
Try not to repaint the whole panel but only the area on which the square will be drawn, use
repaint(int x, int y , int width , int height);
where x and y will be the coordinates and width and height will width and height of your square.
You could give each of your BufferedImages a target variable. You would check to see if the current x position was greater than or less than the target variables, and make changes according the the value. You can do this with y coordinate too.
int x1 = 100;
int x2 = 100;
int xTarget1 = 200; // x target coordinate
int xTarget2 = 200;
// And so on, for as many BufferedImages
int xChange = 1;
// A loop, wherever it may be.
public void loop() {
if(x1 > xTarget1) {
x1 = x1 - xChange;
else if(x1 < xTarget1) {
x1 = x1 + xChange;
}
// Same for the other BufferedImages
}
And then you would draw it. This should create a more smooth move of your images. It will be a constant move unless you change the speed variable through the change. I hopes this helps.

Categories