I'm pretty new to java coding, but i know the basic structures of how to code. I am not familiar with awt/swing, and don't know what my error is.
Background: I searched up ways to animate with java, found zetcode tutorials.
I copy/pasted their code, and tested the program, to make sure it works. the JFrame opened, but nothing drew. Maybe this is a system compatibility error? if so, how would I fix it?
these are the two separate classes:
package com.zetcode;
import java.awt.EventQueue;
import javax.swing.JFrame;
public class ThreadAnimationExample extends JFrame {
public ThreadAnimationExample() {
initUI();
}
private void initUI() {
add(new Board());
setResizable(false);
pack();
setTitle("Star");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame ex = new ThreadAnimationExample();
ex.setVisible(true);
}
});
}
}
This is the main class.
Board.java
package com.zetcode;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Board extends JPanel
implements Runnable {
private final int B_WIDTH = 350;
private final int B_HEIGHT = 350;
private final int INITIAL_X = -40;
private final int INITIAL_Y = -40;
private final int DELAY = 25;
private Image star;
private Thread animator;
private int x, y;
public Board() {
initBoard();
}
private void loadImage() {
ImageIcon ii = new ImageIcon("star.png");
star = ii.getImage();
}
private void initBoard() {
setBackground(Color.BLACK);
setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT));
setDoubleBuffered(true);
loadImage();
x = INITIAL_X;
y = INITIAL_Y;
}
#Override
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawStar(g);
}
private void drawStar(Graphics g) {
g.drawImage(star, x, y, this);
Toolkit.getDefaultToolkit().sync();
}
private void cycle() {
x += 1;
y += 1;
if (y > B_HEIGHT) {
y = INITIAL_Y;
x = INITIAL_X;
}
}
#Override
public void run() {
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while (true) {
cycle();
repaint();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
if (sleep < 0) {
sleep = 2;
}
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
System.out.println("Interrupted: " + e.getMessage());
}
beforeTime = System.currentTimeMillis();
}
}
}
When I run the program, I get a blank black window. The program is supposed to draw a star. Here is what it is supposed to look like.
Related
I have made 2 JPanels, Panel and BackGround, in a JFrame. I am dynamically painting the Panel after 10ms(using a Timer), but the BackGround is only painted once at the beginning of the game. The Panel is responsible for the displaying of the fighters(spacecrafts), the projectiles and the aliens. The BackGround is responsible for the displaying of the background scene which is non-dynamic. The paintComponent(Graphics) method does paint the fighters and the projectiles, but flickers when they are updating. Can someone find the cause.
This is my Frame:
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Frame extends JFrame {
private static final long serialVersionUID = 1L;
public static final int WIDTH = 1280;
public static final int HEIGHT = 720;
public static final int DELAY = 10;
private Panel panel;
private Background bg;
public Frame() {
panel = new Panel();
bg = new Background();
initComponents();
}
public void initComponents() {
this.setSize(WIDTH, HEIGHT);
this.add(bg);
this.add(panel);
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT) panel.moveLeft();
else if(e.getKeyCode() == KeyEvent.VK_RIGHT) panel.moveRight();
}
});
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Frame().setVisible(true);
}
});
}
}
This is my Panel:
import java.awt.Color;
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.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Panel extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
public static final int DELAY = Frame.DELAY;
private Timer timer;
private BufferedImage fighter;
int x, y;
public Panel() {
timer = new Timer(DELAY, this);
try {
fighter = ImageIO.read(this.getClass().getResource("fighter.png"));
} catch (IOException e) {
e.printStackTrace();
}
initComponents();
timer.start();
}
public void initComponents() {
this.setSize(Frame.WIDTH, Frame.HEIGHT);
this.setDoubleBuffered(true);
this.setBackground(new Color(0, 0, 0, 0));
x = 150;
y = 200;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
doDrawing(g2d);
}
private void doDrawing(Graphics2D g2d) {
g2d.drawImage(fighter, x, y, null);
}
#Override
public void actionPerformed(ActionEvent arg0) {
this.repaint();
}
public void moveLeft() {
x -= 10;
}
public void moveRight() {
x += 10;
}
}
This is the BackGround:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Background extends JPanel implements ActionListener{
private static final long serialVersionUID = 1L;
private BufferedImage backGround;
private Timer timer;
public Background() {
this.setSize(Frame.WIDTH, Frame.HEIGHT);
this.setBackground(new Color(0, 0, 0, 0));
timer = new Timer(Frame.DELAY, this);
try {
backGround = ImageIO.read(this.getClass().getResource("background.png"));
} catch (IOException e) {
e.printStackTrace();
}
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(backGround, 0, 0, null);
}
#Override
public void actionPerformed(ActionEvent e) {
this.repaint();
}
}
I expect the sprites not to flicker and not to lag(which is not happening after lots of trials).
Do not call repaint within a painting method
Get rid of the Background class and do all drawing in one JPanel. For example:
See example below of both as well as of MCVE design
whole thing can be copy/pasted into IDE and run
uses images available to all online, not on disk
I would also remove the Timer that simply calls repaint() and instead either
call repaint from within the KeyListener
or use the timer to do the actual sprite movement code (with repaint()). This would be useful if you want continuous movement
Example MCVE:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class Frame1 extends JFrame {
// Image attribution:
// By Adam Evans - M31, the Andromeda Galaxy (now with h-alpha)
// Uploaded by NotFromUtrecht, CC BY 2.0,
// https://commons.wikimedia.org/w/index.php?curid=12654493
public static final String ANDROMEDA_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/"
+ "thumb/9/98/Andromeda_Galaxy_%28with_h-alpha%29.jpg/"
+ "1280px-Andromeda_Galaxy_%28with_h-alpha%29.jpg";
public static final String SPRITE_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/"
+ "thumb/a/a1/Glossy_3d_blue_blue2.png/" + "120px-Glossy_3d_blue_blue2.png";
private static final long serialVersionUID = 1L;
public static final int WIDTH = 1280;
public static final int HEIGHT = 720;
public static final int DELAY = 10;
private Panel1 panel;
// private Background bg;
public Frame1() {
panel = new Panel1();
// bg = new Background();
initComponents();
}
public void initComponents() {
this.setSize(WIDTH, HEIGHT);
// this.add(bg);
this.add(panel);
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT)
panel.moveLeft();
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
panel.moveRight();
}
});
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Frame1().setVisible(true);
}
});
}
}
class Panel1 extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
public static final int DELAY = Frame1.DELAY;
private Timer timer;
private BufferedImage fighter;
private BufferedImage background;
int x, y;
public Panel1() {
timer = new Timer(DELAY, this);
try {
URL url = new URL(Frame1.SPRITE_IMAGE);
// fighter = ImageIO.read(this.getClass().getResource("fighter.png"));
fighter = ImageIO.read(url);
url = new URL(Frame1.ANDROMEDA_IMAGE);
background = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
initComponents();
timer.start();
}
public void initComponents() {
this.setSize(Frame1.WIDTH, Frame1.HEIGHT);
this.setDoubleBuffered(true);
this.setBackground(new Color(0, 0, 0, 0));
x = 150;
y = 200;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, this);
g.drawImage(fighter, x, y, this);
}
#Override
public void actionPerformed(ActionEvent arg0) {
this.repaint();
}
public void moveLeft() {
x -= 10;
}
public void moveRight() {
x += 10;
}
}
While using Swing in java, I am trying to move a circle slowly from a starting position to an end position when clicking a button. However, I can't see the circle moving. It just moves from start to end in an instant.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MyApp {
private int x = 10;
private int y = 10;
private JFrame f;
private MyDraw m;
private JButton b;
public void go() {
f = new JFrame("Moving circle");
b = new JButton("click me to move circle");
m = new MyDraw();
f.add(BorderLayout.SOUTH, b);
f.add(BorderLayout.CENTER, m);
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
b.addActionListener(new Bute());
}
public static void main(String[] args) {
MyApp m = new MyApp();
m.go();
}
private class Bute implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 150; i++) {
++x;
++y;
m.repaint();
Thread.sleep(50);
}
}
}
private class MyDraw extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, 500, 500);
g.setColor(Color.red);
g.fillOval(x, y, 40, 40);
}
}
}
I think the problem is with the action listener because when I'm doing it without using button it is working. Any suggestions?
As Andrew Thompson said, calling Thread.sleep() without defining a second thread freezes everything, so the solution is to define and run another thread like so:
class Bute implements ActionListener, Runnable {
//let class implement Runnable interface
Thread t; // define 2nd thread
public void actionPerformed(ActionEvent e) {
t = new Thread(this); //start a new thread
t.start();
}
#Override //override our thread's run() method to do what we want
public void run() { //this is after some java-internal init stuff called by start()
//b.setEnabled(false);
for (int i = 0; i < 150; i++) {
x++;
y++;
m.repaint();
try {
Thread.sleep(50); //let the 2nd thread sleep
} catch (InterruptedException iEx) {
iEx.printStackTrace();
}
}
//b.setEnabled(true);
}
}
The only problem with this solution is that pressing the button multiple times will speed up the circle, but this can be fixed by making the button unclickable during the animation via b.setEnabled(true/false). Not the best solution but it works.
As said in the comments and another answer, don't block the EDT. Thead.sleep(...) will block it, so you have two options:
Create and manage your own (new) thread.
Use a Swing Timer
In this answer I'll be using a Swing Timer, since it's easier to use. I also changed the paintComponent method to use the Shape API and change the button text to start and stop accordingly as well as reusing the same ActionListener for the button and the timer:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingCircle {
private JFrame frame;
private CustomCircle circle;
private Timer timer;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(this.getClass().getSimpleName());
circle = new CustomCircle(Color.RED);
timer = new Timer(100, listener);
button = new JButton("Start");
button.addActionListener(listener);
circle.setBackground(Color.WHITE);
frame.add(circle);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private ActionListener listener = (e -> {
if (!timer.isRunning()) {
timer.start();
button.setText("Stop");
} else {
if (e.getSource().equals(button)) {
timer.stop();
button.setText("Start");
}
}
circle.move(1, 1);
});
#SuppressWarnings("serial")
class CustomCircle extends JPanel {
private Color color;
private int circleX;
private int circleY;
public CustomCircle(Color color) {
this.color = color;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(color);
g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50));
}
#Override
public Dimension preferredSize() {
return new Dimension(100, 100);
}
public void move(int xGap, int yGap) {
circleX += xGap;
circleY += yGap;
revalidate();
repaint();
}
public int getCircleX() {
return circleX;
}
public void setCircleX(int circleX) {
this.circleX = circleX;
}
public int getCircleY() {
return circleY;
}
public void setCircleY(int circleY) {
this.circleY = circleY;
}
}
}
I'm sorry, I can't post a GIF as I wanted but this example runs as expected.
In my game I have different states, and the first state is the Main Menu. However, it does not draw that Main Menu for an unknown reason to me.
Main-code:
package Game.Window;
import java.awt.BorderLayout;
import javax.swing.JFrame;
public class Window {
public static void main(String[] args){
JFrame frame = new JFrame("TITLE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLayout(new BorderLayout());
frame.add(new Game(), BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Game-code:
package Game.Window;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import Game.Framework.GameStateManager;
public class Game extends JPanel implements Runnable, KeyListener{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 800;
public static final int HEIGHT = WIDTH/12*9;
private Thread thread;
private boolean isRunning = true;
private int FPS = 60;
private long targetTime = 1000/FPS;
private GameStateManager gsm;
public Game(){
setPreferredSize(new Dimension(WIDTH, HEIGHT));
start();
}
private void start(){
isRunning = true;
thread = new Thread(this);
thread.start();
}
public void run() {
long start, elapsed, wait;
gsm = new GameStateManager();
while(isRunning){
start = System.nanoTime();
tick();
repaint();
elapsed = System.nanoTime();
wait = targetTime - elapsed/1000000;
if(wait <= 0){
wait = 5;
}
try{
Thread.sleep(wait);
}catch(Exception e){
e.printStackTrace();
}
}
}
public void tick(){
gsm.tick();
// Shows Current FPS: System.out.println(FPS);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.clearRect(0, 0, WIDTH, WIDTH);
gsm.draw(g);
}
public void keyPressed(KeyEvent e) {
gsm.keyPressed(e.getKeyCode());
}
public void keyReleased(KeyEvent e) {
gsm.keyReleased(e.getKeyCode());
}
public void keyTyped(KeyEvent e) {
}
}
GameStateManager-code:
package Game.Framework;
import java.awt.Graphics;
import java.util.Stack;
import Game.States.MainMenu;
public class GameStateManager {
private Stack<GameState> states;
public GameStateManager(){
states = new Stack<GameState>();
states.push(new MainMenu(this));
}
public void tick(){
states.peek().tick();
}
public void draw(Graphics g){
states.peek().draw(g);
}
public void keyPressed(int k){
states.peek().keyPressed(k);
}
public void keyReleased(int k){
states.peek().keyReleased(k);
}
}
GameState-code:
package Game.Framework;
import java.awt.Graphics;
public abstract class GameState {
protected GameStateManager gsm;
public GameState(GameStateManager gsm){
this.gsm = gsm;
init();
}
public abstract void init();
public abstract void tick();
public abstract void draw(Graphics g);
public abstract void keyPressed(int k);
public abstract void keyReleased(int k);
}
And the Main Menu-code:
package Game.States;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import Game.Framework.GameState;
import Game.Framework.GameStateManager;
import Game.Window.Game;
public class MainMenu extends GameState{
private String[] options = {"New Game", "Load Game", "Options", "Exit"};
private int currentSelection = 0;
public MainMenu(GameStateManager gsm) {
super(gsm);
}
public void init() {}
public void tick() {
}
public void draw(Graphics g) {
for(int i = 0; i < options.length; i++){
if(i == currentSelection){
g.setColor(Color.GREEN);
}else{
g.setColor(Color.BLACK);
}
g.setFont(new Font("Arial", Font.PLAIN, 24));
g.drawString(options[i], Game.WIDTH/2-50, 50*i*30);
}
}
public void keyPressed(int k) {
}
public void keyReleased(int k) {
}
}
I can however draw rectangles from the Game-code, line 65.
Your math looks a little wrong.
g.drawString(options[i], Game.WIDTH/2-50, 50 * i + 30);
//add, not multiply^
A couple other things
class MainMenu extends GameState{
//declare font once
private static final Font ARIAL_24 = new Font("Arial", Font.PLAIN, 24);
private String[] options = {"New Game", "Load Game", "Options", "Exit"};
private int currentSelection = 0;
public MainMenu(GameStateManager gsm) {
super(gsm);
}
public void init() {}
public void tick() {
}
public void draw(Graphics g) {
for(int i = 0; i < options.length; i++){
//simple ternary operator can replace what was here before.
g.setColor(i == currentSelection ? Color.GREEN : Color.BLACK);
//since this never changes make it static and final
//avoid recreating objects in a paint method, or when not neccesary
g.setFont(ARIAL_24);
g.drawString(options[i], Game.WIDTH/2-50, 50 * i + 30);
}
}
public void keyPressed(int k) {
}
public void keyReleased(int k) {
}
}
I think you want to draw those strings from top to bottom,so in MainMenu-code,line 32,the value of "y" should be "50*i+30".
This is my Game Class:
package Game;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable {
Thread thread;
private boolean running = true;
private double FPS = 1.0/60.0;
private Level level;
public Game(){
level = new LevelOne();
level.setBackground(Color.BLACK);
add(level);
}
public synchronized void start() {
thread = new Thread(this, "Game");
thread.start();
}
public synchronized void stop() {
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void run() {
double firstTime = 0;
double lastTime = System.nanoTime() / 1000000000.0;
double passedTime = 0;
double updateTime = 0;
double timer = System.nanoTime() / 1000000000.0;
int rendered = 0;
int updates = 0;
while (running) {
firstTime = System.nanoTime() / 1000000000.0;
passedTime = firstTime - lastTime;
lastTime = firstTime;
updateTime += passedTime;
while(updateTime > FPS){
updates++;
update();
updateTime -= FPS;
}
render();
rendered++;
if((System.nanoTime() / 1000000000) - timer > 1){
timer += 1;
System.out.println("FPS:"+rendered+" Updates:"+updates);
updates = 0;
rendered = 0;
}
}
}
private void update() {
}
private void render() {
this.repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
level.repaint();
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setLocationRelativeTo(null);
frame.setSize(300, 300);
frame.setVisible(true);
frame.setTitle("Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Game game = new Game();
frame.add(game);
game.start();
}
}
My LevelOne class:
package Game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LevelOne extends Level {
private BufferedImage spriteSheet;
private BufferedImage[] player = new BufferedImage[4]; //0 = down, 1 = right, 2 = up, 3 = left
private int dir = 0;
private String UP = "W";
private String DOWN = "S";
private String LEFT = "A";
private String RIGHT = "D";
private int speedX = 0;
private int speedY = 0;
public LevelOne(){
try {
spriteSheet = ImageIO.read(new File("images/sprites.png"));
} catch (IOException e) {
e.printStackTrace();
}
for (int i = 0; i < 4; i++) {
player[i] = spriteSheet.getSubimage((i * 18), 0, 18, 28);
}
this.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {
String key = e.getKeyText(e.getKeyCode());
if (key.equals(UP)) {
dir = 2;
speedY = -5;
} else if (key.equals(LEFT)) {
dir = 3;
speedX = -5;
} else if (key.equals(RIGHT)) {
dir = 1;
speedX = 5;
} else if (key.equals(DOWN)) {
dir = 0;
speedY = 5;
}
}
#Override
public void keyReleased(KeyEvent e) {
String key = e.getKeyText(e.getKeyCode());
if (key.equals(UP)) {
speedY = 0;
} else if (key.equals(LEFT)) {
speedX = 0;
} else if (key.equals(RIGHT)) {
speedX = 0;
} else if (key.equals(DOWN)) {
speedY = 0;
}
}
#Override
public void keyTyped(KeyEvent e) {
}
});
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(player[dir], 0, 0, 18, 28, null);
}
}
LevelOne extends Level, which is currently empty(And extends JPanel), I'm not sure if I need to add anything to Level. I just dont get what I need to do to make the player image show up...
And sorry if my code is sloppy, I'm trying to get into higher level Java, and im not sure if I am just approaching it wrong.
Thanks.
Six things...
Don't call level.repaint from within the paintComponent method of Game, this could cause an infinite loop of repaint requests which is going to screw with your frame rate. Consider calling it within your render method
paintComponent should never public, there's no reason for anybody to ever call it directly.
Use key bindings over KeyListener, they will solve focus related issues. How to Use Key Bindings
Move frame.setVisible AFTER frame.add(game);, in fact, it really should the last thing you do
Make sure your images are loading properly
Set the layout manager for Game to BorderLayout
I was trying to follow these tutorials http://zetcode.com/tutorials/javagamestutorial/animation/ and none of the three examples on that page seem to be working for me. One of them uses a swing timer, one uses the utility timer, and the last and supposedly most effective and accurate according to the page uses a thread to animate.
I will show you the one using the thread, since it is the way that I think I will be doing thing's when using animation for making games.
ThreadAnimationExample.java (in the tutorial it is called star.java but obviously that wont work)
import java.awt.EventQueue;
import javax.swing.JFrame;
public class ThreadAnimationExample extends JFrame {
public ThreadAnimationExample() {
add(new Board());
setTitle("Star");
pack();
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame ex = new ThreadAnimationExample();
ex.setVisible(true);
}
});
}
}
Board.java (the main class)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Board extends JPanel
implements Runnable {
private final int B_WIDTH = 350;
private final int B_HEIGHT = 350;
private final int INITIAL_X = -40;
private final int INITIAL_Y = -40;
private final int DELAY = 25;
private Image star;
private Thread animator;
private int x, y;
public Board() {
loadImage();
initBoard();
}
private void loadImage() {
ImageIcon ii = new ImageIcon("star.png");
star = ii.getImage();
}
private void initBoard() {
setBackground(Color.BLACK);
setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT));
setDoubleBuffered(true);
x = INITIAL_X;
y = INITIAL_Y;
}
#Override
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawStar(g);
}
private void drawStar(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(star, x, y, this);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
private void cycle() {
x += 1;
y += 1;
if (y > B_HEIGHT) {
y = INITIAL_Y;
x = INITIAL_X;
}
}
#Override
public void run() {
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while (true) {
cycle();
repaint();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
if (sleep < 0) {
sleep = 2;
}
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
System.out.println("Interrupted: " + e.getMessage());
}
beforeTime = System.currentTimeMillis();
}
}
}
If you are using Eclipse you should create a source folder and add that image to the source folder. Then you could use this:
ImageIcon ii = new ImageIcon( getClass().getResource("/imageName.png") );