How do i cycle through different images, Java - java

My code is used to draw an image onto the screen by g.draw(img);. Is there a way to make the image cycle through different images instead of being static? I've tried .gif files but they don;t work. Here is my code:
static BufferedImage img = null;
{
try {
img = ImageIO.read(new File("assets/textures/bird.png"));
} catch (IOException e) {
System.out.println(e.getMessage();
}
}
Is there a way to animate the textures?

You can create a small class to do this for you:
public class SimpleImageLoop {
private final BufferedImage[] frames;
private int currentFrame;
public SimpleImageLoop(BufferedImage[] frames) {
this.frames = frames;
this.currentFrame = 0;
}
/**
* Moves the loop to the next frame.
* If we are on the last frame, this loops back to the first
*/
public void nextFrame() {
this.currentFrame++;
if (this.currentFrame >= frames.length) {
this.currentFrame = 0;
}
}
/**
* Draws the current frame on the provided graphics context
*/
public void draw(Graphics g) {
g.draw(this.frames[this.currentFrame];
}
}
Then you need a simple animation loop to call through update() and draw():
final SimpleImageLoop imageLoop = new SimpleImageLoop(frames);
while (true) {
imageLoop.nextFrame();
imageLoop.draw(g);
}
If you need to smooth out the result, you can include additional parameters like how many loops should you perform, the time duration of the frames, etc.

Related

Running out of ways to add a sound file in java

The trouble I'm having now is that I want to insert a sound within the explosion class, specifically in the run function. I have tried a number of ways to get the .WAV sound file to play but all of them failed.
Explosion:
import javax.swing.ImageIcon;
import javax.swing.JLabel;
public class Explosion implements Runnable {
ColoredBallPanel myPanel;
private JLabel img_label;
private ImageIcon imageIcon; // The explosion GIF
private int x_position; // The x position of the GIF.
private int y_position; // The Y position of the GIF.
private final int img_display_time = 650;
public Explosion(ColoredBallPanel panel, int x, int y) {
super();
myPanel = panel;
x_position = x;
y_position = y;
my_GIF();
}
/**
* Adds the GIF to the panel relative to where the balls have collided.
*/
public void my_GIF() {
imageIcon = new ImageIcon(
"C:\\Users\\Oscar\\Desktop\\Bangor Uni Y2 S1\\ICP-2150-0 Advanced Java Programming 201819\\Lab9\\explo.gif"); // sets
// the
// image
// icon
img_label = new JLabel();
img_label.setBounds(x_position, y_position, 200, 200);// Add image at position x_position, y_position
img_label.setIcon(imageIcon);
myPanel.add(img_label);
}
/**
* runs the thread on completion deletes the image
*/
#Override
public void run() {
try {
Thread.sleep(img_display_time); // Sleeps for a set amount of time.
} catch (InterruptedException e) {
}
img_label.setIcon(null); // Deletes the image.
img_label = null;
}
}
Try this:
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
Explosion.class.getResourceAsStream("/path/to/sounds/" + url));
clip.open(inputStream);
clip.start();
There is a pretty good explanation here: https://stackoverflow.com/tags/javasound/info

New Thread not working with JFrame

I have made a class that displays a series of images to create an animation. I would like the animation to take place on a separate thread than the rest of the program. However, when I try to start the animation class, I get an error.
This is some of the animation class (There is more but is is irrelevant):
/**
* Starts a new thread to play the animation on
*/
public void start()
{
playing = true;
animateThread = (new Thread(() -> run()));
animateThread.start();
}
/**
* Will go through sprites and display the images
*/
public void run()
{
int index = 0;
while (playing)
{
if (index > sprites.length)
{
index = 0;
}
try
{
g.drawImage(sprites[index].getImage(), x, y, null);
animateThread.sleep(speed);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
index++;
}
}
I have also attempted at making the animation class Runnable, then making the whole object a new Thread but I received the same error.
This is the class which holds the JFrame and starts the animation (There is more but is is irrelevant):
public static void main(String[] args)
{
AnimationTester tester = new AnimationTester();
tester.frame.setResizable(false);
tester.frame.setTitle("Tester");
tester.frame.add(tester);
tester.frame.pack();
tester.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tester.frame.setLocationRelativeTo(null);
tester.frame.setVisible(true);
//Make the window not have to be clicked on to get input (Set is as the main focus when it begins)
tester.requestFocusInWindow();
//Start the program
tester.start();
}
public void start()
{
createGraphics();
animation.start();
}
public void createGraphics()
{
BufferStrategy bs = getBufferStrategy();
//Checks to see if the BufferStrategy has already been created, it only needs to be created once
if (bs == null)
{
//Always do triple buffering (put 3 in the param)
createBufferStrategy(3);
return;
}
//Links the bufferStrategy and graphics, creating a graphics context
g = bs.getDrawGraphics();
try
{
animation = new Animation(ImageIO.read(getClass().getResource("/SpriteSheet.jpg")), 16, 2, 200, 250, 250, 2.0);
animation.addGraphics(g);
}
catch (IOException e)
{
e.printStackTrace();
}
}
That's not really how BufferStrategy works, instead of using createGraphics, you should be calling it to update the state and render it to the screen
So, in your Thread, it should be updating the state in some way and call some "render" method which would get the next page, render the state and push that state to the screen.
Take a closer look at BufferStrategy and BufferStrategy and BufferCapabilities for more details about how it's suppose to work
For example

Erase an image after its been moved to a new location

I am making the game Asteroids. Everything functionally works properly, but originally I set the background color to black and had the objects represented by shapes that moved around on a Canvas. I've since changed the background to an Image and am working on changing the objects to be represented by images.
However, regardless of the background, I am having trouble repainting the image in a new location. You can see the path of each object after its been moved to each new location. I've been mostly focusing on the shot fired and I noticed if I fire shots all around the screen, the background is refreshed, but it almost seems to be completely at random. If anyone could help guide me in the right direction, that would be great! I've read several documents, textbooks, and watched several videos to try to understand.
package comets;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.sound.sampled.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import java.net.URL;
// This class is primarily responsible for organizing the game of Comets
public class CometsMain extends JPanel implements KeyListener
{
// GUI Data
private JFrame frame; // The window itself
private JPanel playArea; // The area where the game takes place
private final int playWidth = 500; // The width of the play area (in pixels)
private final int playHeight = 500; // The height of the play area (in pixels)
// Game Data
private SpaceObject spaceObject;
private Ship ship; // The ship in play
private Shot s = new Shot(0, 0, 0, 0);
private LargeComet large = new LargeComet(0, 0, 0, 0);
private MediumComet medium = new MediumComet(0, 0, 0, 0);
private SmallComet small = new SmallComet(0, 0, 0, 0);
private Vector<Shot> shots; // The shots fired by the player
private Vector<Comet> comets; // The comets floating around
private boolean shipDead; // Whether or not the ship has been blown up
private long shipTimeOfDeath; // The time at which the ship blew up
// Keyboard data
// Indicates whether the player is currently holding the accelerate, turn
// left, or turn right buttons, respectively
private boolean accelerateHeld = false;
private boolean turnLeftHeld = false;
private boolean turnRightHeld = false;
private boolean slowDownHeld = false;
// Indicates whether the player struck the fire key
private boolean firing = false;
// Create Images
private Image background; // background image
private BufferedImage spaceShip = null;
private BufferedImage largeComet = null;
private BufferedImage mediumComet = null;
private BufferedImage smallComet = null;
private BufferedImage bullet = null;
private int type = AlphaComposite.SRC_OVER;
private float alpha = 0;
// Set up the game and play!
public CometsMain()
{
// Get everything set up
configureGUI();
configureGameData();
// Display the window so play can begin
frame.setVisible(true);
//Use double buffering
frame.createBufferStrategy(2);
//play music
playMusic();
// Start the gameplay
playGame();
}
private void playMusic(){
try {
URL url = this.getClass().getClassLoader().getResource("BackgroundMusic.wav");
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
clip.loop(5);
} catch (Exception e) {
e.printStackTrace();
}
}
// Set up the initial positions of all space objects
private void configureGameData()
{
// Configure the play area size
SpaceObject.playfieldWidth = playWidth;
SpaceObject.playfieldHeight = playHeight;
// Create the ship
ship = new Ship(playWidth/2, playHeight/2, 0, 0);
// Create the shot vector (initially, there shouldn't be any shots on the screen)
shots = new Vector<Shot>();
// Read the comets from comets.cfg
comets = new Vector<Comet>();
try
{
Scanner fin = new Scanner(new File("comets.cfg"));
// Loop through each line of the file to read a comet
while(fin.hasNext())
{
String cometType = fin.next();
double xpos = fin.nextDouble();
double ypos = fin.nextDouble();
double xvel = fin.nextDouble();
double yvel = fin.nextDouble();
if(cometType.equals("Large"))
comets.add(new LargeComet(xpos, ypos, xvel, yvel));
else if(cometType.equals("Medium")){
comets.add(new MediumComet(xpos, ypos, xvel, yvel));
}
else
comets.add(new SmallComet(xpos, ypos, xvel, yvel));
}
}
// If the file could not be read correctly for whatever reason, abort
// the program
catch(FileNotFoundException e)
{
System.err.println("Unable to locate comets.cfg");
System.exit(0);
}
catch(Exception e)
{
System.err.println("comets.cfg is not in a proper format");
System.exit(0);
}
}
// Set up the game window
private void configureGUI()
{
// Load Images & Icons
// Background Image
try {
background = ImageIO.read(this.getClass().getClassLoader().getResource("galaxy.jpg"));
} catch (IOException e) {
}
// Space Ship Image
try {
spaceShip = ImageIO.read(this.getClass().getClassLoader().getResource("ship.png"));
} catch (IOException e) {
}
// Large Comet Image
try {
largeComet = ImageIO.read(this.getClass().getClassLoader().getResource("largecomet.png"));
} catch (IOException e) {
}
// Medium Comet Image
try {
mediumComet = ImageIO.read(this.getClass().getClassLoader().getResource("mediumcomet.png"));
} catch (IOException e) {
}
// Medium Comet Image
try {
smallComet = ImageIO.read(this.getClass().getClassLoader().getResource("smallcomet.png"));
} catch (IOException e) {
}
// bullet Image
try {
bullet = ImageIO.read(this.getClass().getClassLoader().getResource("bullet.png"));
} catch (IOException e) {
}
// Create the window object
frame = new JFrame("Comets");
frame.setSize(playWidth+20, playHeight+35);
frame.setResizable(false);
// The program should end when the window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(playWidth, playHeight);
// Set the window's layout manager
frame.setLayout(new FlowLayout());
// Create background
JLabel bgLabel = new JLabel( new ImageIcon(background.getScaledInstance(playWidth, playHeight, 0) ) );
bgLabel.setSize(playWidth, playHeight);
frame.setContentPane(bgLabel);
frame.pack();
// Create the play area
playArea = new JPanel();
playArea.setSize(playWidth, playHeight);
playArea.setFocusable(false);
playArea.setOpaque(false);
frame.add(playArea);
// Make the frame listen to keystrokes
frame.addKeyListener(this);
}
// The main game loop. This method coordinates everything that happens in
// the game
private void playGame()
{
while(true)
{
// Measure the current time in an effort to keep up a consistent
// frame rate
long time = System.currentTimeMillis();
// If the ship has been dead for more than 3 seconds, revive it
if(shipDead && shipTimeOfDeath + 3000 < time)
{
shipDead = false;
ship = new Ship(playWidth/2, playHeight/2, 0, 0);
}
// Process game events, move all the objects floating around,
// and update the display
if(!shipDead)
handleKeyEntries();
handleCollisions();
moveSpaceObjects();
// Sleep until it's time to draw the next frame
// (i.e. 32 ms after this frame started processing)
try
{
long delay = Math.max(0, 32-(System.currentTimeMillis()-time));
Thread.sleep(delay);
}
catch(InterruptedException e)
{
}
}
}
// Deal with objects hitting each other
private void handleCollisions()
{
// Anything that is destroyed should be erased, so get ready
// to erase stuff
Graphics g = playArea.getGraphics();
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(type, alpha));
// Deal with shots blowing up comets
for(int i = 0; i < shots.size(); i++)
{
Shot s = shots.elementAt(i);
for(int j = 0; j < comets.size(); j++)
{
Comet c = comets.elementAt(j);
// If a shot has hit a comet, destroy both the shot and comet
if(s.overlapping(c))
{
// Erase the bullet
shots.remove(i);
i--;
repaint((int)shots.elementAt(i).getXPosition(), (int)shots.elementAt(i).getYPosition(), (int)(2*shots.elementAt(i).getRadius()), (int)(2*shots.elementAt(i).getRadius()));
// If the comet was actually destroyed, replace the comet
// with the new comets it spawned (if any)
Vector<Comet> newComets = c.explode();
if(newComets != null)
{
paintComponent(g);
comets.remove(j);
j--;
comets.addAll(newComets);
}
break;
}
}
}
// Deal with comets blowing up the ship
if(!shipDead)
{
for(Comet c : comets)
{
// If the ship hit a comet, kill the ship and mark down the time
if(c.overlapping(ship))
{
shipTimeOfDeath = System.currentTimeMillis();
shipDead = true;
spaceObject=ship;
paintComponent(g);
}
}
}
}
// Check which keys have been pressed and respond accordingly
private void handleKeyEntries()
{
// Ship movement keys
if(accelerateHeld)
ship.accelerate();
if(slowDownHeld)
ship.slowDown();
// Shooting the cannon
if(firing)
{
firing = false;
shots.add(ship.fire());
}
}
// Deal with moving all the objects that are floating around
private void moveSpaceObjects()
{
Graphics g = playArea.getGraphics();
// Handle the movements of all objects in the field
if(!shipDead)
updateShip(g);
updateShots(g);
updateComets(g);
}
// Move all comets and draw them to the screen
private void updateComets(Graphics g)
{
for(Comet c : comets)
{
spaceObject=c;
paintComponent(g);
// Move the comet to its new position
c.move();
paintComponent(g);
}
}
// Move all shots and draw them to the screen
private void updateShots(Graphics g)
{
for(int i = 0; i < shots.size(); i++)
{
Shot s = shots.elementAt(i);
// Erase the shot at its old position
paintComponent(g);
// Move the shot to its new position
s.move();
// Remove the shot if it's too old
if(s.getAge() > 180)
{
shots.remove(i);
i--;
}
// Otherwise, draw it at its new position
else
{
moveImage(g, s, (int)s.getXPosition(), (int)s.getYPosition());
paintComponent(g);
}
}
}
// Moves the ship and draws it at its new position
private void updateShip(Graphics g)
{
// Erase the ship at its old position
paintComponent(g);
// Ship rotation must be handled between erasing the ship at its old position
// and drawing it at its new position so that artifacts aren't left on the screen
if(turnLeftHeld)
ship.rotateLeft();
if(turnRightHeld)
ship.rotateRight();
ship.move();
// Draw the ship at its new position
moveImage(g, ship, (int)ship.getXPosition(), (int)ship.getYPosition());
paintComponent(g);
}
// Draws this ship s to the specified graphics context
private void drawShip(Graphics g, Ship s)
{
Graphics2D ship = (Graphics2D) spaceShip.getGraphics();
double x = Math.sin(s.getAngle());
double y = Math.cos(s.getAngle());
AffineTransform transformsave = AffineTransform.getRotateInstance(x, y, spaceShip.getWidth()/2, spaceShip.getHeight()/2);
AffineTransformOp transform = new AffineTransformOp( transformsave, AffineTransformOp.TYPE_BILINEAR );
// Figure out where the ship should be drawn
int xCenter = (int)s.getXPosition();
int yCenter = (int)s.getYPosition();
// Draw the ship body
g.drawImage(transform.filter(spaceShip, null), xCenter-10, yCenter-20, null);
ship.setTransform(transformsave);
}
public void setSpaceObject(SpaceObject s){
spaceObject=s;
}
public SpaceObject getSpaceObject(){
return spaceObject;
}
#Override
protected void paintComponent( Graphics g ){
super.paintComponent(g);
spaceObject=getSpaceObject();
int radius = (int)s.getRadius();
int xCenter = (int)s.getXPosition();
int yCenter = (int)s.getYPosition();
// Draw the object
if(spaceObject==s)
g.drawImage( bullet, xCenter-radius, yCenter-radius, this );
else if(spaceObject==large)
g.drawImage( largeComet, xCenter-radius, yCenter-radius, this );
else if(spaceObject==medium)
g.drawImage( mediumComet, xCenter-radius, yCenter-radius, this );
else if(spaceObject==small)
g.drawImage( smallComet, xCenter-radius, yCenter-radius, this );
else if(spaceObject==ship)
drawShip(g, ship);
}
public void moveImage(Graphics g, SpaceObject s, int x, int y){
int radius = (int)s.getRadius();
int xCenter=0, yCenter=0;
if(xCenter!=x || yCenter!=y){
xCenter= (int)s.getXPosition();
yCenter = (int)s.getYPosition();
repaint(xCenter, yCenter, radius*2, radius*2);
}
}
// Deals with keyboard keys being pressed
public void keyPressed(KeyEvent key)
{
// Mark down which important keys have been pressed
if(key.getKeyCode() == KeyEvent.VK_UP)
this.accelerateHeld = true;
if(key.getKeyCode() == KeyEvent.VK_LEFT)
this.turnLeftHeld = true;
if(key.getKeyCode() == KeyEvent.VK_RIGHT)
this.turnRightHeld = true;
if(key.getKeyCode() == KeyEvent.VK_SPACE)
this.firing = true;
//ADD DOWN TO SLOW DOWN SHIP!!!
if(key.getKeyCode() == KeyEvent.VK_DOWN)
this.slowDownHeld = true;
}
// Deals with keyboard keys being released
public void keyReleased(KeyEvent key)
{
// Mark down which important keys are no longer being pressed
if(key.getKeyCode() == KeyEvent.VK_UP)
this.accelerateHeld = false;
if(key.getKeyCode() == KeyEvent.VK_LEFT)
this.turnLeftHeld = false;
if(key.getKeyCode() == KeyEvent.VK_RIGHT)
this.turnRightHeld = false;
//ADD DOWN TO SLOW DOWN SHIP!!!
if(key.getKeyCode() == KeyEvent.VK_DOWN)
this.slowDownHeld = false;
}
// This method is not actually used, but is required by the KeyListener interface
public void keyTyped(KeyEvent arg0)
{
}
public static void main(String[] args)
{
// A GUI program begins by creating an instance of the GUI
// object. The program is event driven from that point on.
new CometsMain();
}
}
Don't use getGraphics. The problem with this, is it's simply a snap shot of what was last painted.
This is like taking a sheet of paper and repeatedly drawing on top of it, it makes a real mess real quick.
getGraphics can also return null.
In Swing painting is controlled by the RepaintManager which makes decisions about what and when things should be painted.
The basic concept you should be trying to follow is to...
Update the state of the game model
Update the view of the game model
Render the view to the screen
This can be achieved in a few different ways, but to start with, if you want to make updates to the UI, you should override the paintComponent method of a component that extends from something like JComponent.
When called, call super.paintComponent which will automatically prepare the Graphics context for painting.
To update the view...
You Could...
In a background thread, update the game model and request that the view be repainted
In the view's paintComponent method, re-draw the model
This is a relatively simple approach, but can, if not controlled well, can get the view and the model out of sync. You also need to ensure that the model is not changed while the view is been updated...
Or, You Could...
Create two (or more) BufferedImages
In a background thread, update the game model
In the background thread, update the "offscreen" buffer to represent the current game model state
Switch the buffered images (moving the "offscreen" buffer to the "active screen" and the "active screen" to the "offscreen")
Request that the view be updated
In the view's paintComponent method, simply draw the "active screen"
This is a more complex process, which will require you to ensure that the buffer's are the same size as the view, but means that the model can be updated independently of the view repaints. There is still the danger that you could change the "off" and "active" screen buffers while the view is been painted.
You could elevate this process slightly by using some kind of queue, where you place BufferedImages that can be used for rendering to (by popping them off the queue) and having the "view" push them back again once it has rendered it...
Or some combination of these, where you lock the switching of the "active" and "off" screen buffers to ensure that that "active" buffer isn't being painted.
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details
For example...
Java Bouncing Ball
the images are not loading
Swing animation running extremely slow
Modern graphics applications use following approach:
For every frame repeat these steps
erase screen
draw background
draw your objects in proper order
do anything else
With this approach you don't need to track previous locations of your objects that can be very tricky because object may overlap.
Of course this will cause flickering, due to lack of performance. Various algorithms to prevent this are exists, take a look on this Documentation and this Question
you can just use getContentPane().repaint() every time you use repaint().

Java slideshow image delay using paintComponent

I am putting together a slideshow program that will measure a user's time spent on each slide. The slideshow goes through several different magic tricks. Each trick is shown twice. Interim images are shown between the repetition. Transition images are shown between each trick.
On the first repetition of a trick the JPanel color flashes on the screen after a click before the next image is shown. This doesn't happen during the second repetition of the same trick. It's possible that the image is taking too long to load.
Is there an easy way to pre-load the images so that there isn't a delay the first time through?
NOTE: Original code deleted.
EDIT 1/10/2013: This code now works on slower machines. trashgod's second addendum helped the most. The mouseClick control structure periodically asks SwingWorker classes to load 40 images or less of the current trick while also setting the used images to null. I have simplified my code down for this to just two Image[]s and added a main method so it stands alone. Images are still required to run though. This is now pretty bare bones code, and if you're trying to make a slideshow with a lot of images I think it would be a good place to start.
NOTE: I think I figured out how to properly implement SwingWorker while still using multiple Image[]s. trashgod and kleopatra is this implementation in-line with what you were suggesting? I didn't end up using publish and process since I couldn't figure out how to get that to work appropriately with an indexed array, but because the StringWorker doesn't load all images in the array (only 40), and the code calls StringWorker every 20 images, there should be a pretty good buffer.
EDIT 1/10/2013 Changed out MouseListener by instead extending MouseAdapter on my Mouse class. Also fixed my paintComponent method to include a call to super.paintComponent(g).
Added publish/process methods to my SwingWorker class ImageWorker. Added a wrapper class, ArrayWrapper to allow passing imageArray[i] and its corresponding index int i with publish to process.
package slideshow3;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import java.util.List;
public class SlideShow3 extends JFrame
{
//screenImage will be replaced with each new slide
private Image screenImage;
private int width;
private int height;
//Create panel for displaying images using paintComponent()
private SlideShow3.PaintPanel mainImagePanel;
//Used for keybinding
private Action escapeAction;
//Image array variables for each trick
private Image[] handCuffs; //h
private Image[] cups; //c
//Used to step through the trick arrays one image at a time
private int h = 0;
private int c = 0;
//Used by timeStamp() for documenting time per slide
private long time0 = 0;
private long time1;
public SlideShow3()
{
super();
//Create instance of each Image array
handCuffs = new Image[50];
cups = new Image[176];
//start(handCuffsString);
start("handCuffs");
try
{
screenImage = ImageIO.read(new File("images/begin1.jpg"));
}
catch (IOException nm)
{
System.out.println("begin");
System.out.println(nm.getMessage());
System.exit(0);
}
/******************************************
* Removes window framing. The next line sets fullscreen mode.
* Once fullscreen is set width and height are determined for the window
******************************************/
this.setUndecorated(true);
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
width = this.getWidth();
height = this.getHeight();
//Mouse click binding to slide advance control structure
addMouseListener(new Mouse());
//Create panel so that I can use key binding which requires JComponent
mainImagePanel = new PaintPanel();
add(mainImagePanel);
/******************************************
* Key Binding
* ESC will exit the slideshow
******************************************/
// Key bound AbstractAction items
escapeAction = new EscapeAction();
// Gets the mainImagePanel InputMap and pairs the key to the action
mainImagePanel.getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "doEscapeAction");
// This line pairs the AbstractAction enterAction to the action "doEnterAction"
mainImagePanel.getActionMap().put("doEscapeAction", escapeAction);
/******************************************
* End Key Binding
******************************************/
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run()
{
SlideShow3 show = new SlideShow3();
show.setVisible(true);
}
});
}
//This method executes a specific SwingWorker class to preload images
public void start(String e)
{
if(e.equals("handCuffs"))
{
new ImageWorker(handCuffs.length, h, e).execute();
}
else if(e.equals("cups"))
{
new ImageWorker(cups.length, c, e).execute();
}
}
//Stretches and displays images in fullscreen window
private class PaintPanel extends JPanel
{
#Override
public void paintComponent(Graphics g)
{
if(screenImage != null)
{
super.paintComponent(g);
g.drawImage(screenImage, 0, 0, width, height, this);
}
}
}
/******************************************
* The following SwingWorker class Pre-loads all necessary images.
******************************************/
private class ArrayWrapper
{
private int i;
private Image image;
public ArrayWrapper(Image image, int i)
{
this.i = i;
this.image = image;
}
public int getIndex()
{
return i;
}
public Image getImage()
{
return image;
}
}
private class ImageWorker extends SwingWorker<Image[], ArrayWrapper>
{
private int currentPosition;
private int arraySize;
private String trickName;
private Image[] imageArray;
public ImageWorker(int arraySize, int currentPosition, String trick)
{
super();
this.currentPosition = currentPosition;
this.arraySize = arraySize;
this.trickName = trick;
}
#Override
public Image[] doInBackground()
{
imageArray = new Image[arraySize];
for(int i = currentPosition; i < currentPosition+40 && i < arraySize; i++)
{
try
{
imageArray[i] = ImageIO.read(new File("images/" + trickName + (i+1) + ".jpg"));
ArrayWrapper wrapArray = new ArrayWrapper(imageArray[i], i);
publish(wrapArray);
}
catch (IOException e)
{
System.out.println(trickName);
System.out.println(e.getMessage());
System.exit(0);
}
}
return imageArray;
}
#Override
public void process(List<ArrayWrapper> chunks)
{
for(ArrayWrapper element: chunks)
{
if(trickName.equals("handCuffs"))
{
handCuffs[element.getIndex()] = element.getImage();
}
else if(trickName.equals("cups"))
{
cups[element.getIndex()] = element.getImage();
}
}
}
#Override
public void done()
{
try
{
if(trickName.equals("handCuffs"))
{
handCuffs = get();
}
else if(trickName.equals("cups"))
{
cups = get();
}
}
catch(InterruptedException ignore){}
catch(java.util.concurrent.ExecutionException e)
{
String why = null;
Throwable cause = e.getCause();
if(cause != null)
{
why = cause.getMessage();
}
else
{
why = e.getMessage();
}
System.err.println("Error retrieving file: " + why);
}
}
}
/******************************************
* End SwingWorker Pre-Loading Classes
******************************************/
//Prints out time spent on each slide
public void timeStamp()
{
time1 = System.currentTimeMillis();
if(time0 != 0)
{
System.out.println(time1 - time0);
}
time0 = System.currentTimeMillis();
}
/******************************************
* User Input Classes for Key Binding Actions and Mouse Click Actions
******************************************/
private class EscapeAction extends AbstractAction
{
#Override
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
public class Mouse extends MouseAdapter
{
#Override
public void mouseClicked(MouseEvent e)
{
if(!(h<handCuffs.length) && !(c<cups.length))
{
timeStamp();
System.exit(0);
}
else if(h<handCuffs.length)
{
timeStamp();
screenImage = handCuffs[h];
repaint();
System.out.print("handCuffs[" + (h+1) + "]\t");
h++;
//purge used slides and refresh slide buffer
if(h == 20 || h == 40)
{
for(int i = 0; i < h; i++)
{
handCuffs[i] = null;
}
start("handCuffs");
}
if(h == 45)
{
start("cups");
}
}
else if(c<cups.length)
{
timeStamp();
screenImage = cups[c];
repaint();
System.out.print("cups[" + (c+1) + "]\t");
c++;
//purge used slides and refresh slide buffer
if(c == 20 || c == 40 || c == 60 || c == 80 || c == 100 || c == 120 || c == 140 || c == 160)
{
for(int i = 0; i < c; i++)
{
cups[i] = null;
}
start("cups");
}
}
}
}
/******************************************
* End User Input Classes for Key Binding Actions and Mouse Click Actions
******************************************/
}
This example uses a List<ImageIcon> as a cache of images returned by getImage(). Using getResource(), the delay is imperceptible. The next and previous buttons are bound to the Space key by default.
Addendum: You can control navigation by conditioning a button's setEnabled() state using an instance of javax.swing.Timer, for example.
Addendum: Your second example waits until the mouse is clicked to begin reading an image, an indeterminate process that may return a copy immediately or may not complete until after repaint(). Instead, begin reading the images in the background using ImageIO.read(), as shown here. You can process() your List<Image> and show progress, as seen here. The SwingWorker can be launched from the initial thread, running while you subsequently build your GUI on the EDT. You can display the first image as soon as it is processed.

GameLoop Thread occasionally slow

For the game I'm currently writing for Android devices, I've got a class called RenderView, which is a Thread and updates and renders everything. Occasionally the class logs the message "Game thread is only updating the update method and is not rendering anything". The game is running at 30 fps on my nexus s. And I get the message a couple of times throughout the session. Could someone tell me how I could optimize the class or if I'm forgetting something or that it's totally normal?
Here's my code:
public class RenderView extends SurfaceView implements Runnable {
public final String classTAG = this.getClass().getSimpleName();
Game game;
Bitmap framebuffer;
Thread gameloop;
SurfaceHolder holder;
boolean running;
int sleepTime;
int numberOfFramesSkipped;
long beginTime;
long endTime;
long lastTime;
int differenceTime;
int framePeriod;
Canvas canvas;
int frameCount;
WSLog gameEngineLog;
public RenderView(Game game, Bitmap framebuffer) {
super(game);
this.game = game;
this.framebuffer = framebuffer;
this.holder = getHolder();
framePeriod = 1000/game.getFramesPerSecond();
lastTime = System.currentTimeMillis();
gameEngineLog = game.getGameEngineLog();
}
#Override
public void run() {
while(running == true) {
if(holder.getSurface().isValid()) {
beginTime = System.currentTimeMillis();
numberOfFramesSkipped = 0;
game.getCurrentScreen().update();
game.getCurrentScreen().render(); // Draw out everything to the current virtual screen (the bitmap)
game.getGraphics().renderFrameBuffer(); // Actually draw everything to the real screen (combine both bitmaps)
canvas = holder.lockCanvas();
if(canvas != null) { // Fix for mysterious bug ( FATAL EXCEPTION: Thread)
// The viewing area of our virtual screen on our real screen
canvas.drawBitmap(framebuffer, null, game.getWSScreen().getGameScreenextendeddst(), null);
holder.unlockCanvasAndPost(canvas);
}
else {
gameEngineLog.e(classTAG, "Surface has not been created or otherwise cannot be edited");
}
endTime = System.currentTimeMillis();;
differenceTime = (int) (endTime - beginTime);
sleepTime = (int) (framePeriod - differenceTime);
if(sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
else {
while(sleepTime < 0 && numberOfFramesSkipped < game.getMaxFrameSkippes()) {
gameEngineLog.d(classTAG, "Game thread is only updating the update method and is not rendering anything");
try {
Thread.sleep(5);
}
catch (InterruptedException exception) {
exception.printStackTrace();
}
game.getCurrentScreen().update();
sleepTime += framePeriod;
numberOfFramesSkipped++;
}
}
// Frame Per Second Count
frameCount++;
if(lastTime + 1000 < System.currentTimeMillis()) {
game.getGameEngineLog().d(classTAG, "REAL FPS: " + frameCount);
lastTime = System.currentTimeMillis();
frameCount = 0;
}
}
}
}
public void resume() {
running = true;
gameloop = new Thread(this);
gameloop.start();
}
public void pause() {
running = false;
while(running == true) {
try {
gameloop.join();
running = false;
}
catch (InterruptedException e) {
}
}
gameloop = null;
}
}
Here's the code for the Graphics class (the getGraphics() just return an graphics object):
public class Graphics {
public final String classTAG = this.getClass().getSimpleName();
Game game;
Canvas frameBuffer;
Canvas canvasGameScreenextended;
Canvas canvasGameScreen; // Used for customeScreen
Bitmap gameScreenextended;
Bitmap gameScreen;
Rect gameScreendst;
Rect gameScreenextendeddst;
WSLog gameEngineLog;
Graphics(Game game, Bitmap framebuffer, Bitmap gameScreen) {
this.game = game;
// Initialize canvases to render to
frameBuffer = new Canvas(framebuffer);
canvasGameScreen = new Canvas(gameScreen);
// Initialize images to be rendered to our composition
this.gameScreen = gameScreen;
// Set up the Log
gameEngineLog = game.getGameEngineLog();
}
public void resetCanvasGameScreenextended() {
// This method has to be called each time the screen scaling type changes
canvasGameScreenextended = new Canvas(game.getWSScreen().getGameScreenextended());
gameScreenextended = game.getWSScreen().getGameScreenextended();
}
public Canvas getCanvasGameScreenextended() {
return canvasGameScreenextended;
}
public Canvas getCanvasGameScreen() {
return canvasGameScreen;
}
public void renderFrameBuffer() {
// Composition
// First layer (bottom)
frameBuffer.drawBitmap(gameScreen, null, game.getWSScreen().getGameScreendst(), null);
// Second layer (top)
frameBuffer.drawBitmap(gameScreenextended, null, game.getWSScreen().getGameScreenextendeddst(), null);
}
public void clearFrameBuffer() {
canvasGameScreen.drawColor(Color.BLACK);
//canvasGameScreenextended.drawColor(Color.BLACK);
gameScreenextended.eraseColor(Color.TRANSPARENT); // Make top layer transparent
}
}
Here's the code for the screen class (the getCurrentScreen() method returns a screen object):
public class Screen {
public final String classTAG = this.getClass().getSimpleName();
protected final Game game;
protected final Graphics graphics;
protected Screen(Game game) {
this.game = game;
this.graphics = game.getGraphics();
//game.getInput().reset();
}
public void update() {
}
public void render() {
}
/** Initialize all the sensory that should be used within this screen.*/
public void resume() {
}
public void pause() {
game.getInput().useAccelerometer(false);
game.getInput().useKeyboard(false);
game.getInput().useTouchscreen(false);
}
public void onDispose() {
game.getGraphics().clearFrameBuffer();
}
public void setScreenResizeType(int screenResizeType) {
}
The Screen class is extended and the render() method is shadowed with methods like:
graphics.getCanvasGameScreen().drawRect(play, red);
The funny thing is, when I override the render() method and don't place any code in it, the logger fires constantly with the message: "Game thread is only updating the update method and is not rendering anything". What kind of sorcery is this?!
Help is hugely appreciated!
As far as I understand from your updated post, there is no rendering problem actually. Instead, your code mistakenly prints that message.
This is because you check if(sleepTime > 0) , so if the rendering is very fast and sleepTime is zero, you get that message. Just change it to if(sleepTime >= 0).

Categories

Resources