I'm working on a vertical scrolling game, and I'm using a thread to generate new enemies every 2 seconds. Each enemy is an image in a JPanel. For some reason, The generated enemies are not showing up in the JFrame, but they are present. When the player collides with one of the enemies, all the enemies show up.
This is the main class:
package asteroidblaster;
import javax.swing.*;
import javax.swing.Timer;
import java.awt.event.*;
import java.awt.*;
public class Main extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
public Main() {
initUI();
}
private void initUI() {
add(new Board());
setResizable(false);
pack();
setTitle("Alien Blaster");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame ex = new Main();
ex.setVisible(true);
});
}
}
This is the main game logic:
package asteroidblaster;
import javax.swing.*;
import javax.swing.Timer;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
public class Board extends JPanel {
private static final long serialVersionUID = 1L;
private final int B_WIDTH = 1500;
private final int B_HEIGHT = 700;
private final int REFRESH_TIME = 150;
private AlienShip alien;
private PlayerShip player;
private ArrayList<AlienShip> enemies = new ArrayList<AlienShip>();
private Timer alienScrollTimer, mainTimer;
public Board() {
initBoard();
}
private void initBoard() {
setBackground(Color.BLACK);
setFocusable(true);
setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT));
setPlayer();
initAlienTimer();
gameLoop();
}
private void initAlienTimer() {
alienScrollTimer = new Timer(25, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for(AlienShip as : enemies) {
as.scrollShip();
}
repaint();
}
});
alienScrollTimer.start();
}
private void setPlayer() {
player = new PlayerShip();
add(player);
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseMoved(MouseEvent e) {
player.followMouse(e.getX(), e.getY());
//checkCollision();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
}
});
}
private void checkCollision() {
for(AlienShip as : enemies) {
if(player.getBounds().intersects(as.getBounds()))
player.setVisible(false);
}
}
private Thread alienGenerator() {
for(int i = 0; i < 3; i++) { //these two are being drawn
alien = new AlienShip();
add(alien);
enemies.add(alien);
}
return new Thread(new Runnable() {
#Override
public void run() {
int sleepTime = 2000;
while(true) {
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
System.out.println(e);
}
alien = new AlienShip();
add(alien);
enemies.add(alien);
System.out.println("Enemies: " + enemies.size());
}
}
});
}
private void gameLoop() {
alienGenerator().start();
mainTimer = new Timer(REFRESH_TIME, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
checkCollision();
repaint();
}
});
mainTimer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
}
This is the AlienShip class:
package asteroidblaster;
import java.util.Random;
import java.awt.*;
import javax.swing.*;
public class AlienShip extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private final String ALIEN_SHIP_FILE = "images/alienShip.jpg";
private final int PANEL_WIDTH = 224, PANEL_HEIGHT = 250;
private int panelX, panelY;
private Image alienShipImage;
public AlienShip() {
initAlienShip();
}
private void loadImage() {
alienShipImage = Toolkit.getDefaultToolkit().getImage(ALIEN_SHIP_FILE);
}
public int getX() {
return panelX;
}
public int getY() {
return panelY;
}
private void setY(int yCoord) {
panelY = yCoord;
}
private int setX() {
Random r = new Random();
int rand = r.nextInt(14) + 1;
return panelX = rand * 100;
}
private void initAlienShip() {
panelX = setX();
panelY = 0;
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setBackground(Color.BLUE);
loadImage();
}
#Override
public Rectangle getBounds() {
return new Rectangle(panelX, panelY, PANEL_WIDTH, PANEL_HEIGHT);
}
public void scrollShip() {
setY(getY() + 1);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawImage(alienShipImage, 0, 0, null);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
}
Related
I'm making a dice rolling program in java using swing. I've got 4 classes:
Die
public class Die{
private int faceValue;
public Die(){
System.out.println("Creating new Dice Object");
setValue(roll());
}
public int roll() {
int val = (int) (6 * Math.random() + 1);
setValue(val);
return val;
}
public int getValue() {
return faceValue;
}
public void setValue(int spots) {
faceValue = spots;
}
}
DieFace
public class DieFace {
private int spotDiam,wOffset,hOffset,w,h;
public int faceValue;
public DieFace(){
Die die = new Die();
this.faceValue = die.getValue();
}
public void draw(Graphics g, int paneWidth, int paneHeight){
//draw information
}
}
DieFaceComponent
public class DieFaceComponent extends JComponent{
private static final long serialVersionUID = 1L;
DieFace face;
public DieFaceComponent(){
face = new DieFace();
System.out.println("DIEFACE" + face.faceValue);
repaint();
}
public void paintComponent(Graphics g){
revalidate();
face.draw(g,super.getWidth(),super.getHeight());
}
}
DieFaceViewer
public class DieFaceViewer{
static DieFaceComponent component;
static JFrame frame = new JFrame(); // Create a new JFrame object
public static void main(String[] args){
final int FRAME_WIDTH = 500;
final int FRAME_HEIGHT = 500;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT); // Set initial size
frame.setTitle("Dice Simulator Version 1.0"); // Set title
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Set default close operation
component = new DieFaceComponent(); // Create a new DieFaceComponent object
frame.setLayout(new BorderLayout());
JButton btnRoll = new JButton("Roll!");
btnRoll.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
component = new DieFaceComponent();
}
});
frame.add(component, BorderLayout.CENTER); // Add DieFaceComponent object to frame
frame.add(btnRoll, BorderLayout.SOUTH);
frame.setVisible(true); // Set frame to visible
}
}
My problem is that even though a new Die, DieFace and DieFaceComponent object is created every time I press my btnRoll, the value used to draw the component stays the same as the initial instance. Is there something I've done wrong? Thanks in advance
You create a new instance of DieFaceComponent in your ActionListener but do nothing with it, it's never added to anything, so it's never visible. A better solution would allow you to trigger a change to DieFaceComponent, which triggered a change to DieFace which triggered a change to Die and have the whole thing just repaint itself, for example...
public class Die {
private int faceValue;
public Die() {
System.out.println("Creating new Dice Object");
//setValue(roll());
roll(); // Roll sets the value any way :P
}
public int roll() {
int val = (int) (6 * Math.random() + 1);
setValue(val);
return val;
}
public int getValue() {
return faceValue;
}
public void setValue(int spots) {
faceValue = spots;
}
}
public class DieFace {
private int spotDiam, wOffset, hOffset, w, h;
//public int faceValue;
private Die die;
public DieFace() {
die = new Die();
//Die die = new Die();
// This is pointless, as you should simply as die for it's value
// when ever you need it...
//this.faceValue = die.getValue();
}
public void roll() {
die.roll();
}
public void draw(Graphics g, int paneWidth, int paneHeight) {
//draw information
}
}
public class DieFaceComponent extends JComponent {
private static final long serialVersionUID = 1L;
DieFace face;
public DieFaceComponent() {
face = new DieFace();
//System.out.println("DIEFACE" + face.faceValue);
// Pointless, as you've probably not actually been added to anything
// that could actuallyt paint you anyway...
//repaint();
}
public void roll() {
face.roll();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//revalidate();
face.draw(g, super.getWidth(), super.getHeight());
}
}
Now, you can call roll on DieFaceComponent, which will call roll on DieFace which will call roll on Die, which will update the actual value. DieFaceComponent will then schedule a repaint to ensure that it's update on the screen.
And then you could use it something like...
import java.awt.BorderLayout;
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 javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DiceRoller {
public static void main(String[] args) {
new DiceRoller();
}
public DiceRoller() {
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 DiePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DiePane extends JPanel {
public DiePane() {
setLayout(new BorderLayout());
DieFaceComponent component = new DieFaceComponent();
JButton roll = new JButton("Roll");
roll.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
component.roll();
}
});
add(component);
add(roll, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Now, a better solution would be to have Die as your primary entry point, allowing it to generate notifications to interested parties and having them update themselves
Maybe something like...
import java.awt.BorderLayout;
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.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DiceRoller {
public static void main(String[] args) {
new DiceRoller();
}
public DiceRoller() {
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 DiePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DiePane extends JPanel {
public DiePane() {
setLayout(new BorderLayout());
Die die = new Die();
DieFaceComponent component = new DieFaceComponent(die);
JButton roll = new JButton("Roll");
roll.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
die.roll();
}
});
add(component);
add(roll, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
public class Die {
private PropertyChangeSupport propertyChangeSupport;
private int faceValue;
public Die() {
propertyChangeSupport = new PropertyChangeSupport(this);
System.out.println("Creating new Dice Object");
//setValue(roll());
roll(); // Roll sets the value any way :P
}
public int roll() {
int val = (int) (6 * Math.random() + 1);
setValue(val);
return val;
}
public int getValue() {
return faceValue;
}
public void setValue(int spots) {
int old = faceValue;
faceValue = spots;
propertyChangeSupport.firePropertyChange("value", old, faceValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
}
public class DieFace {
private int spotDiam, wOffset, hOffset, w, h;
private Die die;
public DieFace(Die die) {
this.die = die
}
public void draw(Graphics g, int paneWidth, int paneHeight) {
//draw information
}
}
public class DieFaceComponent extends JComponent {
private static final long serialVersionUID = 1L;
private DieFace face;
public DieFaceComponent(Die die) {
face = new DieFace(die);
die.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//revalidate();
face.draw(g, super.getWidth(), super.getHeight());
}
}
}
This is a simple example of an Observer Pattern, where the Die is the generator of information, to which every body else is interested in knowing when it changes. It's also a variant of the model-view-controller paradigm
In my program I try to paint on a JPanel when the mouse is pressed. The mousePressed method is just to test the painting from another class. Later on the spawn method will be called by other class methods. When I press the mouse button spawnPedestrian() is called, but no Pedestrian is painted. Below is a running example with code from my project. If you create a project Roundabout and paste this code in it, you should be able to run it (images are hotlinked).
How to fix the spawnPedestrian() method?
public class Roundabout extends JFrame {
public static Surface surface;
public Roundabout() {
initUI();
}
private void initUI() {
setTitle("Roundabout");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
surface = new Surface();
add(surface);
this.addMouseListener(new MouseAdapter() {// empty implementation of all
// MouseListener`s methods
#Override
public void mousePressed(MouseEvent e) {
//Spawn
Spawn sp = new Spawn();
sp.spawnPedestrian(300, 100);
}
});
setSize(1618, 850);
setLocationRelativeTo(null);
}
public static JPanel getSurface() {
return surface;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Roundabout roundabout = new Roundabout();
roundabout.setVisible(true);
}
});
}
//Track class
class Track {
BufferedImage track;
Point trackPosition;
Point TRACK_POS = new Point(0, 0);
public Track() {
try {
track = ImageIO.read(new URL("http://i.stack.imgur.com/2U3j5.png"));
} catch (Exception ex) {
System.out.println("Problem loading track image: " + ex);
}
trackPosition = new Point(TRACK_POS.x, TRACK_POS.y);
}
public void paint(Graphics g) {
g.drawImage(track, TRACK_POS.x, TRACK_POS.y, null);
}
}
//Surface class
public class Surface extends JPanel {
Track track = new Track();
public List<Vehicle> toDraw = new ArrayList<>();
public Surface() {
Pedestrian p = new Pedestrian(100, 100);
toDraw.add(p);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//setLayout(null);
track.paint(g);
//Make sure the track is painted first
for (Vehicle v : toDraw) {
v.paint(g);
}
}
}
class Pedestrian extends Vehicle {
BufferedImage pedestrian;
Point pedestrianPosition;
double pedestrianRotation = 0;
int pedestrianW, pedestrianH;
public Pedestrian(int x, int y) {
try {
pedestrian = ImageIO.read(new URL("http://i.stack.imgur.com/wm0I5.png"));
} catch (IOException e) {
System.out.println("Problem loading pedestrian images: " + e);
}
pedestrianPosition = new Point(x, y);
pedestrianW = pedestrian.getWidth();
pedestrianH = pedestrian.getHeight();
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(Math.toRadians(pedestrianRotation), pedestrianPosition.x, pedestrianPosition.y);
g2d.drawImage(pedestrian, pedestrianPosition.x, pedestrianPosition.y, null);
}
#Override
public void setPath(List<Point> path) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void update(double i) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
//Spawn class
class Spawn {
public void spawnPedestrian(int x, int y) {
//Create a new pedestrian.
System.out.println("Spawn a pedestrian.");
Pedestrian p = new Pedestrian(x, y);
Roundabout.surface.toDraw.add(p);
Roundabout.surface.revalidate();
Roundabout.surface.repaint();
}
}
public abstract class Vehicle {
public abstract void setPath(List<Point> path);
public abstract void update(double i);
public abstract void paint(Graphics g);
}
}
EDIT: It works now Pedestrian is spawned on mouse click.
Basically, you want to decouple your code and centralise the responsible to the classes. So the "data" should be maintained by a model of some kind, the rendering should be handle by some kind of view and the updates to the model and view should be handled by some kind of controller.
This makes it easier to swap out any one part with out requiring a whole bunch of new code or other changes. It also means that each class has a defined domain of responsibility and discourages you from trying to, for example, make changes to state from within the view which should be handled by the model (which could put the state into disarray)
Let's start with something that what's to be painted
public interface Sprite {
public void paint(Graphics2D g2d);
}
public interface MoveableSprite extends Sprite {
public void update(Container container);
}
These represent either a static sprite (like a tree for example) or a sprite which is moving (and wants to be updated on a regular bases)
These are contained within a model
public interface GameModel {
public List<Sprite> getSprites();
public void setObserver(Observer<MoveableSprite> observer);
public Observer<MoveableSprite> getObserver();
public void spawnSprite();
}
Which provides some means by which it can notify (in this case, a single) interested party about some kind of state change. For this example, that means a new MoveableSprite has become available
The Observer is pretty basic and just has a single call back...
public interface Observer<T> {
public void stateChanged(T parent);
}
And an "engine" to help drive it...
public class GameEngine {
private GameModel model;
private SurfacePane surface;
private Timer timer;
public GameEngine(GameModel model, SurfacePane surface) {
this.model = model;
this.surface = surface;
model.setObserver(new Observer<MoveableSprite>() {
#Override
public void stateChanged(MoveableSprite sprite) {
sprite.update(getSurface());
}
});
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Sprite sprite : getModel().getSprites()) {
if (sprite instanceof MoveableSprite) {
((MoveableSprite) sprite).update(getSurface());
}
}
getSurface().repaint();
}
});
}
public GameModel getModel() {
return model;
}
public SurfacePane getSurface() {
return surface;
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
}
This is a pretty basic example, but basically, it updates the position of MoveableSprite and asks the surface to repaint itself. It's also observing the GameModel for any new sprites and it will update their position immediately, so they don't appear in some "weird" place
Okay, now we actually need to implement some of this to make it work
public class DefaultGameModel implements GameModel {
private Observer<MoveableSprite> observer;
private List<Sprite> sprites;
public DefaultGameModel() {
sprites = new ArrayList<>(25);
for (int index = 0; index < 10; index++) {
spawnSprite();
}
}
#Override
public List<Sprite> getSprites() {
return Collections.unmodifiableList(sprites);
}
public void spawnSprite() {
try {
ZombieSprite sprite = new ZombieSprite();
sprites.add(sprite);
Observer<MoveableSprite> observer = getObserver();
if (observer != null) {
observer.stateChanged(sprite);
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void setObserver(Observer<MoveableSprite> observer) {
this.observer = observer;
}
#Override
public Observer<MoveableSprite> getObserver() {
return observer;
}
}
public class ZombieSprite implements MoveableSprite {
private int x;
private int y;
private int xDelta;
private int yDelta;
private BufferedImage img;
private Observer<Sprite> observer;
private boolean initialised = false;
public ZombieSprite() throws IOException {
img = ImageIO.read(getClass().getResource("/LogoZombi.png"));
}
#Override
public void update(Container container) {
if (!initialised) {
x = (int) (Math.random() * container.getWidth());
y = (int) (Math.random() * container.getHeight());
Random rnd = new Random();
xDelta = rnd.nextBoolean() ? 1 : -1;
yDelta = rnd.nextBoolean() ? 1 : -1;
initialised = true;
}
x += xDelta;
y += yDelta;
if (x < 0) {
x = 0;
xDelta *= -1;
} else if (x + img.getWidth() > container.getWidth()) {
x = container.getWidth() - img.getWidth();
xDelta *= -1;
}
if (y < 0) {
y = 0;
yDelta *= -1;
} else if (y + img.getHeight() > container.getHeight()) {
y = container.getHeight() - img.getHeight();
yDelta *= -1;
}
}
#Override
public void paint(Graphics2D g2d) {
g2d.drawImage(img, x, y, null);
}
}
These two classes implement the GameModel and MoveableSprite interfaces. We use interfaces to decouple the code, which makes it easier to change the way in which things work and provides a jumping off point for agreed to contracts and exceptions of the implemenations
And finally, something that actually paints the current state...
public class SurfacePane extends JPanel {
private GameModel model;
public SurfacePane(GameModel model) {
this.model = model;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
getModel().spawnSprite();
}
});
}
public GameModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
GameModel model = getModel();
for (Sprite sprite : model.getSprites()) {
sprite.paint(g2d);
}
g2d.dispose();
}
}
You'll not that this class has the MouseListener, this is kind of deliberate, as other components which might be added to this container could prevent the MouseListener from been notified, so don't do that. But the MouseListener just calls the model to spawn another zombie...
And finally, we need to plumb it altogether...
GameModel model = new DefaultGameModel();
SurfacePane surfacePane = new SurfacePane(model);
GameEngine engine = new GameEngine(model, surfacePane);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(surfacePane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
engine.start();
And just because I know that's a lot of disjointed concepts to put together, a complete example...
import java.awt.Container;
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.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
GameModel model = new DefaultGameModel();
SurfacePane surfacePane = new SurfacePane(model);
GameEngine engine = new GameEngine(model, surfacePane);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(surfacePane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
engine.start();
}
});
}
public class SurfacePane extends JPanel {
private GameModel model;
public SurfacePane(GameModel model) {
this.model = model;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
getModel().spawnSprite();
}
});
}
public GameModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
GameModel model = getModel();
for (Sprite sprite : model.getSprites()) {
sprite.paint(g2d);
}
g2d.dispose();
}
}
public class GameEngine {
private GameModel model;
private SurfacePane surface;
private Timer timer;
public GameEngine(GameModel model, SurfacePane surface) {
this.model = model;
this.surface = surface;
model.setObserver(new Observer<MoveableSprite>() {
#Override
public void stateChanged(MoveableSprite sprite) {
sprite.update(getSurface());
}
});
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Sprite sprite : getModel().getSprites()) {
if (sprite instanceof MoveableSprite) {
((MoveableSprite) sprite).update(getSurface());
}
}
getSurface().repaint();
}
});
}
public GameModel getModel() {
return model;
}
public SurfacePane getSurface() {
return surface;
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
}
public interface Observer<T> {
public void stateChanged(T parent);
}
public interface Sprite {
public void paint(Graphics2D g2d);
}
public interface MoveableSprite extends Sprite {
public void update(Container container);
}
public interface GameModel {
public List<Sprite> getSprites();
public void setObserver(Observer<MoveableSprite> observer);
public Observer<MoveableSprite> getObserver();
public void spawnSprite();
}
public class DefaultGameModel implements GameModel {
private Observer<MoveableSprite> observer;
private List<Sprite> sprites;
public DefaultGameModel() {
sprites = new ArrayList<>(25);
for (int index = 0; index < 10; index++) {
spawnSprite();
}
}
#Override
public List<Sprite> getSprites() {
return Collections.unmodifiableList(sprites);
}
public void spawnSprite() {
try {
ZombieSprite sprite = new ZombieSprite();
sprites.add(sprite);
Observer<MoveableSprite> observer = getObserver();
if (observer != null) {
observer.stateChanged(sprite);
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void setObserver(Observer<MoveableSprite> observer) {
this.observer = observer;
}
#Override
public Observer<MoveableSprite> getObserver() {
return observer;
}
}
public class ZombieSprite implements MoveableSprite {
private int x;
private int y;
private int xDelta;
private int yDelta;
private BufferedImage img;
private Observer<Sprite> observer;
private boolean initialised = false;
public ZombieSprite() throws IOException {
img = ImageIO.read(getClass().getResource("/LogoZombi.png"));
}
#Override
public void update(Container container) {
if (!initialised) {
x = (int) (Math.random() * container.getWidth());
y = (int) (Math.random() * container.getHeight());
Random rnd = new Random();
xDelta = rnd.nextBoolean() ? 1 : -1;
yDelta = rnd.nextBoolean() ? 1 : -1;
initialised = true;
}
x += xDelta;
y += yDelta;
if (x < 0) {
x = 0;
xDelta *= -1;
} else if (x + img.getWidth() > container.getWidth()) {
x = container.getWidth() - img.getWidth();
xDelta *= -1;
}
if (y < 0) {
y = 0;
yDelta *= -1;
} else if (y + img.getHeight() > container.getHeight()) {
y = container.getHeight() - img.getHeight();
yDelta *= -1;
}
}
#Override
public void paint(Graphics2D g2d) {
g2d.drawImage(img, x, y, null);
}
}
}
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class slide extends JFrame
{
ImageIcon[] iconArray = new ImageIcon[25];
int iconIndex = 0;
JLabel label;
JPanel panel;
slide ()
{
panel = new JPanel();
label = new JLabel();
add(panel);
setTitle("Slide Show");
panel.add(label);
for(int i = 0; i < iconArray.length; i++)
{
iconArray[i] = new ImageIcon("C:/SlideShow/slide0.jpg");
}
Timer timer = new Timer(1000, new TimerListener());
timer.start();
}
private class TimerListener implements ActionListener
{
public void actionPerformed(ActionEvent actionEvent)
{
label.setIcon(iconArray[iconIndex]);
iconIndex++ ;
if(iconIndex == 25)
iconIndex = 0;
}
}
public static void main(String[] args)
{
slide frame = new slide();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
}
}
Any idea how to make a slideshow to show pictures with different > > time intervals? For example 1 sec for the first picture, 200 ms for > > the next, 3 sec for the third and etc. > > > > many thanks for the help!!!
A Swing timer has at most two delays. You can set the initial delay and the interval delay.
A Thread gives you more control over how long you sleep between images.
Here's one way to develop a slide show viewer that allows you to set the delay for each image. I used 3 images from the Internet to test the viewer.
package com.ggl.testing;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Slideshow implements Runnable {
private JFrame frame;
private SSImage[] imageArray;
private SSShower showImages;
private SSViewer imageViewer;
public Slideshow() {
this.imageArray = new SSImage[3];
Image image0 = null;
Image image1 = null;
Image image2 = null;
try {
image0 = ImageIO.read(new URL(
"http://www.ericofon.com/collection/collection1.jpg"));
image1 = ImageIO
.read(new URL(
"http://magiclinks1.wikispaces.com/file/view"
+ "/collection11_lg.jpg/219833158/collection11_lg.jpg"));
image2 = ImageIO
.read(new URL(
"http://www.pokelol.com/wp-content/uploads/2011/12"
+ "/my_pokemon_collection_by_pa_paiya-d4iiuo5.jpg"));
} catch (IOException e) {
e.printStackTrace();
return;
}
imageArray[0] = new SSImage(image0, 4000L);
imageArray[1] = new SSImage(image1, 2500L);
imageArray[2] = new SSImage(image2, 1500L);
}
#Override
public void run() {
frame = new JFrame("Check Box Test");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
exitProcedure();
}
});
imageViewer = new SSViewer(700, 700);
frame.add(imageViewer);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
showImages = new SSShower(imageArray, imageViewer);
new Thread(showImages).start();
}
private void exitProcedure() {
showImages.setRunning(false);
frame.dispose();
System.exit(0);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Slideshow());
}
public class SSImage {
private final long delay;
private final Image image;
public SSImage(Image image, long delay) {
this.image = image;
this.delay = delay;
}
public long getDelay() {
return delay;
}
public Image getImage() {
return image;
}
}
public class SSViewer extends JPanel {
private static final long serialVersionUID = -7893539139464582702L;
private Image image;
public SSViewer(int width, int height) {
this.setPreferredSize(new Dimension(width, height));
}
public void setImage(Image image) {
this.image = image;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, (this.getWidth() - image.getWidth(this)) / 2,
(this.getHeight() - image.getHeight(this)) / 2, this);
}
}
public class SSShower implements Runnable {
private int counter;
private volatile boolean running;
private SSViewer ssviewer;
private SSImage[] imageArray;
public SSShower(SSImage[] imageArray, SSViewer ssviewer) {
this.imageArray = imageArray;
this.ssviewer = ssviewer;
this.counter = 0;
this.running = true;
}
#Override
public void run() {
while (running) {
SSImage ssimage = imageArray[counter];
ssviewer.setImage(ssimage.getImage());
repaint();
sleep(ssimage.getDelay());
counter = ++counter % imageArray.length;
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ssviewer.repaint();
}
});
}
private void sleep(long delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
}
}
public synchronized void setRunning(boolean running) {
this.running = running;
}
}
}
Hey guys I'm new to Java game programming and I'm working with the Runnable interface right now. For some reason, my run() method never gets called and I'm not sure why. I've tried putting many System.out.println statements in there but they never get printed. Any help would be greatly appreciated!
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable
{
private final int WIDTH = 160;
private final int HEIGHT = WIDTH/12 *9;
private final int RATIO = 3;
private Thread animator;
private volatile boolean running;
private volatile boolean gameOver;
private double FPS = 60D;
private double period = 1000/FPS;
private Image dbImage;
private Graphics dbg;
public GamePanel()
{
setPreferredSize(new Dimension(WIDTH *3, HEIGHT*3));
setBackground(Color.WHITE);
setFocusable(true);
requestFocus();
terminate();
}
public void addNotify()
{
super.addNotify();
startGame();
}
public void startGame()
{
System.out.println("Thread started");
animator = new Thread();
animator.start();
}
public void stopGame()
{
System.out.println("Thread stopped");
running = false;
}
public void run()
{
long beforeTime, timeDiff, sleepTime;
beforeTime = System.currentTimeMillis();
System.out.println(beforeTime);
running = true;
while (running)
{
System.out.println("Running");
gameUpdate();
gameRender();
paintScreen();
timeDiff = System.currentTimeMillis() - beforeTime;
sleepTime = (long) period - timeDiff;
if(sleepTime <= 0)
sleepTime = 5;
try
{
Thread.sleep(sleepTime);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
beforeTime = System.currentTimeMillis();
}
System.exit(0);
}
public void gameRender()
{
if (dbImage == null)
{
dbImage = createImage(WIDTH, HEIGHT);
}
else
dbg = dbImage.getGraphics();
dbg.setColor(Color.WHITE);
dbg.fillRect(0, 0, WIDTH, HEIGHT);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(dbImage, 0, 0, null);
}
public void gameUpdate()
{
}
private void paintScreen()
{
Graphics g;
try
{
g = this.getGraphics();
if (g!= null && dbImage!= null)
g.drawImage(dbImage, 0, 0, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
catch (Exception e)
{
System.out.println("Error: " + e.getMessage());
}
}
public void terminate()
{
addKeyListener (new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_ESCAPE)
{
stopGame();
}
}
});
}
}
import javax.swing.JFrame;
public class GameFrame extends JFrame
{
private final int WIDTH = 160;
private final int HEIGHT = WIDTH/12*9;
private final int RATIO = 3;
public GameFrame()
{
setTitle("User Input Game");
setSize(WIDTH*3,HEIGHT*3);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
GamePanel mainPanel = new GamePanel();
add(mainPanel);
}
}
public class Main
{
public static void main(String[] args)
{
new GameFrame().setVisible(true);
}
}
You need to change your startGame() method:
animator = new Thread(new GamePanel());
You need to pass a Runnable (of which GamePanel is one) into the Thread constructor. The thread then runs the runnable when it starts.
You don't seem to have a main method anywhere. Either in this class or an external class you need a main method that creates an instance of GamePanel and pass it as an argument to a Thread class. Like so:
public class Test
{
public static void main(String[] args)
{
Thread t = new Thread(new GamePanel()).start();
}
}
In the following code i'm using a doubleBuffer to avoid flickering of the image as was suggested in this question of mine
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TestProgram extends JFrame implements KeyListener {
private Image doubleBuffer;
private Graphics myGraphics;
private BufferedImage TestImage;
private int cordX = 100;
private int cordY = 100;
public TestProgram() {
setTitle("Testing....");
setSize(500,500);
imageLoader();
setVisible(true);
}
public static void main(String[] args) {
new TestProgram();
}
public void imageLoader() {
try {
String testPath = "test.png";
TestImage = ImageIO.read(getClass().getResourceAsStream(testPath));
} catch (IOException ex) {
ex.printStackTrace();
}
addKeyListener(this);
doubleBuffer = createImage(getWidth(), getHeight());
myGraphics = doubleBuffer.getGraphics();
drawImages();
}
#Override
public void update(Graphics g) {
drawImages();
g.drawImage(doubleBuffer, 0, 0, this);
}
public void drawImages() {
myGraphics.drawImage(TestImage, cordX, cordY, this);
}
public void keyPressed(KeyEvent ke) {
switch (ke.getKeyCode()) {
case KeyEvent.VK_RIGHT: {
cordX+=5;
}
break;
case KeyEvent.VK_LEFT: {
cordX-=5;
}
break;
case KeyEvent.VK_DOWN: {
cordY+=5;
}
break;
case KeyEvent.VK_UP: {
cordY-=3;
}
break;
}
repaint();
}
public void keyTyped(KeyEvent ke) {}
public void keyReleased(KeyEvent ke) {}
}
The problem is that im getting a nullPointerException at this line
myGraphics = doubleBuffer.getGraphics();
is my approach correct in doing this?
Please help.
thanks
don't paint to JFrame directly, put there JPanel or JComponent
Swing GUI should be starting from Initial Thread
whats TestImage and path to the Image???,
KeyListener isn't designated for Swing JComponents, use KeyBindings instead
after coordinates changed you have to call repaint()
put that altogether
.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MoveIcon extends JPanel {
private static final long serialVersionUID = 1L;
private static final String IMAGE_PATH = "http://duke.kenai.com/misc/Bullfight.jpg";
private static final String IMAGE_PATH_PLAYER = "http://duke.kenai.com/iconSized/duke4.gif";
public static final int STEP = 3;
private static final int TIMER_DELAY = STEP * 8;
private BufferedImage bkgrndImage = null;
private BufferedImage playerImage = null;
private Map<Direction, Boolean> directionMap = new HashMap<Direction, Boolean>();
private int playerX = 0;
private int playerY = 0;
enum Direction {
UP(KeyEvent.VK_UP, 0, -1), DOWN(KeyEvent.VK_DOWN, 0, 1),
LEFT(KeyEvent.VK_LEFT, -1, 0), RIGHT(KeyEvent.VK_RIGHT, 1, 0);
private int keyCode;
private int xDirection;
private int yDirection;
private Direction(int keyCode, int xDirection, int yDirection) {
this.keyCode = keyCode;
this.xDirection = xDirection;
this.yDirection = yDirection;
}
public int getKeyCode() {
return keyCode;
}
public int getXDirection() {
return xDirection;
}
public int getYDirection() {
return yDirection;
}
}
public MoveIcon() {
try {
URL bkgrdImageURL = new URL(IMAGE_PATH);
URL playerImageURL = new URL(IMAGE_PATH_PLAYER);
bkgrndImage = ImageIO.read(bkgrdImageURL);
playerImage = ImageIO.read(playerImageURL);
setPreferredSize(new Dimension(bkgrndImage.getWidth(), bkgrndImage.getHeight()));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
for (Direction direction : Direction.values()) {
directionMap.put(direction, false);
}
setKeyBindings();
Timer timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
private void setKeyBindings() {
InputMap inMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actMap = getActionMap();
for (final Direction direction : Direction.values()) {
KeyStroke pressed = KeyStroke.getKeyStroke(direction.getKeyCode(), 0, false);
KeyStroke released = KeyStroke.getKeyStroke(direction.getKeyCode(), 0, true);
inMap.put(pressed, direction.toString() + "pressed");
inMap.put(released, direction.toString() + "released");
actMap.put(direction.toString() + "pressed", new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
directionMap.put(direction, true);
}
});
actMap.put(direction.toString() + "released", new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
directionMap.put(direction, false);
}
});
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bkgrndImage != null) {
g.drawImage(bkgrndImage, 0, 0, null);
}
if (playerImage != null) {
g.drawImage(playerImage, playerX, playerY, null);
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
boolean moved = false;
for (Direction direction : Direction.values()) {
if (directionMap.get(direction)) {
playerX += STEP * direction.getXDirection();
playerY += STEP * direction.getYDirection();
moved = true;
}
}
if (moved) {
int x = playerX - 2 * STEP;
int y = playerY - 2 * STEP;
int w = playerImage.getWidth() + 4 * STEP;
int h = playerImage.getHeight() + 4 * STEP;
MoveIcon.this.repaint(x, y, w, h); // !! repaint just the player
}
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("MoveIcon");
frame.getContentPane().add(new MoveIcon());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowUI();
}
});
}
}