Java2D Game Running 20-30 FPS
I am writing a java2D game that as of now has an almost perfect memory footprint running only at around 2% of about a 4GB usable RAM space. The problem is the game only is running at around 20-30 fps just from rendering the character and the surrounding environment. I read already something about utilizing the GraphicsConfiguration, but when I tried that my fps soared right up to 120-130 fps while my memory increased to 70-80% and the frame was frozen in one place even if i moved the character. At this point I am stuck. I assume it has something to do with how many times i call g2d.fillRect() because after removing an area that's being referenced the game seems to speed up. The game seems to run smoothly at 20-30 fps, but I need to fix this otherwise my viewable gameobject painter loop will reduce my game to nothing but lag.
Rendering Method With Clipping Areas
private void renderEnvironment(Graphics2D g2d){
for(int paint_index = 0; paint_index < paint_textures.size(); paint_index++){
TexturePaint current_paint = paint_textures.get(paint_index);
//Area a = new Area(new Rectangle(0, 0, host_frame.getWidth(), host_frame.getHeight()));
//a.intersect(new Area(AffineTransform.getTranslateInstance(CamX, CamY).createTransformedShape(paint_regions.get(current_paint))));
g2d.setPaint(new TexturePaint(current_paint.getImage(), new Rectangle(CamX, CamY, current_paint.getImage().getWidth(), current_paint.getImage().getHeight())));
g2d.setClip(AffineTransform.getTranslateInstance(CamX, CamY).createTransformedShape(paint_regions.get(current_paint)));
//g2d.fill(a);
g2d.fillRect(0, 0, host_frame.getWidth(), host_frame.getHeight());
}
for(int paint_index = 0; paint_index < animated_textures.size(); paint_index++){
TextureAnimation current_paint = animated_textures.get(paint_index);
//Area a = new Area(new Rectangle(0, 0, host_frame.getWidth(), host_frame.getHeight()));
//a.intersect(new Area(AffineTransform.getTranslateInstance(CamX, CamY).createTransformedShape(paint_regions.get(current_paint))));
g2d.setPaint(new TexturePaint(current_paint.animatedPaint().getImage(), new Rectangle(current_paint.animatedPaint().getAnchorRect().getBounds().x + CamX, current_paint.animatedPaint().getAnchorRect().getBounds().y + CamY, current_paint.animatedPaint().getImage().getWidth(), current_paint.animatedPaint().getImage().getHeight())));
g2d.setClip(AffineTransform.getTranslateInstance(CamX, CamY).createTransformedShape(paint_regions.get(current_paint)));
//map_graphics.fill(a);
g2d.fillRect(0, 0, host_frame.getWidth(), host_frame.getHeight());
}g2d.setClip(null);
g2d.drawImage(character_sprite.spriteImage(), host_frame.getWidth() / 2, host_frame.getHeight() / 2, host_frame);
for(int paint_index = 0; paint_index < global_textures.size(); paint_index++){
TextureAnimation current_paint = global_textures.get(paint_index);
g2d.setPaint(new TexturePaint(current_paint.animatedPaint().getImage(), new Rectangle(current_paint.animatedPaint().getAnchorRect().getBounds().x + CamX, current_paint.animatedPaint().getAnchorRect().getBounds().y + CamY, current_paint.animatedPaint().getImage().getWidth(), current_paint.animatedPaint().getImage().getHeight())));
//g2d.setClip(new Rectangle(0, 0, host_frame.getWidth(), host_frame.getHeight()));
g2d.fillRect(0, 0, host_frame.getWidth(), host_frame.getHeight());
}
}
EDIT : TextureAnimation.java
public class TextureAnimation implements ActionListener {
private final Action behavior;
private final BufferedImage image;
private final Component host;
private TexturePaint paint;
private final Timer t;
private final boolean isGlobal;
public int x;
public int y;
public TextureAnimation(Action a, BufferedImage i, Component c, boolean global){
isGlobal = global;
behavior = a;
image = i;
host = c;
paint = new TexturePaint(image, new Rectangle(0, 0, image.getWidth(), image.getHeight()));
t = new Timer(300, this);
t.setInitialDelay(0);
}
public void playAnimation(){
t.start();
}
public Boolean isGlobal(){
return isGlobal;
}
public void delayAnimation(int ms_delay){
t.setInitialDelay(ms_delay);
t.restart();
}
public void setIntervalDelay(int ms_delay){
t.setDelay(ms_delay);
}
public void setTilePosition(int x, int y){
paint = new TexturePaint(image, new Rectangle(Gscale.setX(x), Gscale.setY(y), Gscale.setWidth(image.getWidth()), Gscale.setHeight(image.getHeight())));
}
public TexturePaint animatedPaint(){
return paint;
}
#Override
public void actionPerformed(ActionEvent e) {
behavior.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null));
//host.repaint(0,0, host.getWidth(), host.getHeight());
}
}
Researched Link
Java2D Performance Issues
Note : The Graphic2D parameter is taking on the graphics of the JPanel itself. So shouldn't java already create a Compatible image for me?
Related
I am currently making a RPG hack and slash in java with just the standard libraries.
For these games , I had made a CustomButton class in an independent package that I just copy and paste in almost all of my games and then make some game-specific modifications.
This is the class::
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import objects.Collectable;
public final class CustomButton implements MouseListener {
// private BufferedImage image;
private String text = "";
private boolean pressed = false;
private int x;
private int y;
private int width;
private int height;
private Color currentColor, defaultColor, hoverColor, pressColor;
private Point mousePoint = new Point(0, 0);
private ButtonListener btnListener = null;
private Canvas canvasObject;
private BufferedImage image;
private BufferedImage darkImage;
private String actionCommand = "default";
private Collectable object;
private boolean enabled;
/*
* private CustomButton(Canvas canvasObject,JFrame frame) { this.x=100;
* this.y=100; this.width=100; this.height=100;
*
* canvasObject.addMouseListener(this); currentColor=new Color(255,255,255);
* defaultColor=new Color(255,255,255); hoverColor=new Color(255,255,255);
* pressColor=new Color(255,255,255); }
*/
public CustomButton(int x, int y, int width, int height, Canvas canvasObject) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.canvasObject = canvasObject;
canvasObject.addMouseListener(this);
currentColor = Color.GREEN;
currentColor = new Color(255, 255, 255);
defaultColor = new Color(255, 255, 255);
hoverColor = new Color(255, 255, 255);
pressColor = new Color(255, 255, 255);
enabled = true;
}
public CustomButton(int x, int y, int width, int height, Canvas canvasObject, BufferedImage image,
Collectable object) {
this.image = image;
this.darkImage = darkenImage(image);
this.x = x;
this.y = y;
this.width = width;
this.height = height;
canvasObject.addMouseListener(this);
currentColor = Color.GREEN;
currentColor = new Color(255, 255, 255);
defaultColor = new Color(255, 255, 255);
hoverColor = new Color(255, 255, 255);
pressColor = new Color(255, 255, 255);
this.canvasObject = canvasObject;
this.object = object;
enabled = true;
}
public void render(Graphics g) {
if (image == null) {
g.setColor(currentColor);
if (!pressed)
g.fillRect(this.x, this.y, width, height);
else
g.fill3DRect(this.x, this.y, width, height, true);
g.setColor(Color.BLACK);
g.drawString(text, this.x + 10, this.y + 15);
} else {
if (enabled) {
g.drawImage(image, x, y, width, height, null);
} else {
g.drawImage(darkImage, x, y, width, height, null);
}
}
}
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
public void tick() {
mousePoint = getMouseLocation();
changeColor();
}
private Point getMouseLocation() {
int x = 0;
int y = 0;
try {
x = (int) (canvasObject.getMousePosition().getX());
y = (int) (canvasObject.getMousePosition().getY());
} catch (NullPointerException nl) {
}
return new Point(x, y);
}
public void changeColor() {
if (!pressed) {
if (getBounds().contains(mousePoint))
currentColor = hoverColor;
else
currentColor = defaultColor;
} else {
currentColor = pressColor;
}
}
public void addButtonListener(ButtonListener btnListener) {
this.btnListener = btnListener;
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
if (enabled) {
if (btnListener != null) {
if (getBounds().contains(mousePoint)) {
ButtonID id = ButtonID.UNDETERMINABLE;
if (e.getButton() == MouseEvent.BUTTON1) id = ButtonID.LEFT;
if (e.getButton() == MouseEvent.BUTTON2) id = ButtonID.RIGHT;
btnListener.buttonClicked(new ButtonEvent(id, object, actionCommand));
}
}
}
}
public void mousePressed(MouseEvent e) {
if (getBounds().contains(mousePoint)) pressed = true;
}
public void mouseReleased(MouseEvent e) {
pressed = false;
}
public void setActionCommand(String actionCommand) {
this.actionCommand = actionCommand;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public void setDefaultColor(Color c) {
defaultColor = c;
}
public void setHoverColor(Color c) {
hoverColor = c;
}
public void setPressColor(Color c) {
pressColor = c;
}
public Collectable getObject() {
return object;
}
public void setObject(Collectable object) {
this.object = object;
}
public void destroy() {
canvasObject.removeMouseListener(this);
}
public void disable() {
enabled = false;
}
public void enable() {
enabled = true;
}
public boolean isEnabled() {
return enabled;
}
private BufferedImage darkenImage(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
image = deepCopy(image);
WritableRaster raster = image.getRaster();
for (int xx = 0; xx < width; xx++) {
for (int yy = 0; yy < height; yy++) {
int[] pixels = raster.getPixel(xx, yy, (int[]) null);
pixels[0] -= 50;
pixels[1] -= 50;
pixels[2] -= 50;
pixels[0] = Math.max(pixels[0], 0);
pixels[1] = Math.max(pixels[0], 0);
pixels[2] = Math.max(pixels[0], 0);
raster.setPixel(xx, yy, pixels);
}
}
return image;
}
private BufferedImage deepCopy(BufferedImage bi) {
ColorModel cm = bi.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = bi.copyData(null);
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
}
As you can probably see from the mouseClicked() method that a event is sent to the ButtonListener class.The ButtonListener interface is also declared in the package.
This button is drawn on the canvas itself.For example, in the levelmap, there is a button in the lower-right corner, which on clicking will open the inventory.Let this button be called btn_INV.
Until now,I have been taking inputs for moving the player through the keyboard.But I plan to change keyInput to mouse input where the player will move to the tile where the user clicks on.
For that I have to make a class , sayMouseInput that implements MouseListener.Now the problem is that when I click btn_INV , not only will the button generate an event, but also, since the button is actually drawn on the canvas, the MouseInput class will also get an event as to the tile to which the player wants to moving.Now, I thought that when the MouseInput class gets a MouseEvent, it will check with the buttons as whenever there is a mouse click on the canvas, the buttons always gets informed, although it may not generate a ButtonEvent, as you can see from the code.But this is a rather bad method and highly inefficient.So , I want another method for this.
NOTE : I thought of creating another canvas for displaying the HUD and the btn_INV and other such buttons, but that does not really solve the issue as much as bypass it.
I think there's two ways to solve this problem :
The first will be to divide your game screen into two parts : one for the buttons, the other for the tiles, so you can test whilecatching a MouseEvent if the click is positionned on the tiles or not. Note that the tiles can be placed on a new button. This solution is easy to implement but you won't be able to place buttons on your tile's area.
The second will be to create a "ButtonManager". Your game class will have this button manager that will listen for mouse event and send then to the buttons. Buttons won't listen directly to this event. They will say one after one if the click is on their bounds and if no button have been fired it means that the click occured on the tiles. This method is little bit more difficult to implement but allows you to create a priority beetween buttons and so buttons would be able to have thei bounds intersecting !
Hope it helped you !
Generally speaking, concepts like button listeners are used in applications, not games.
Games generally work with a game loop which will have an update and a draw phase. During the update phase, user input is caught (well... more checked than caught, but I'll get to that in a moment) and interpreted.
So you'd just have one big mouse listener that would then check to see which button might be pressed or if the mouse is within the game area so the character should get the move command.
Of course technically that mouse listener shouldn't directly do anything, as it is bound to the swing thread. It should just change some variables (like: "left mouse button is down, position is x, y") that will then be checked in your update phase, which will then actually work with it. That way, you're no longer dependant on the swing thread (which isn't ideal for games anyway) and - almost as important - your game logic is completely predictable.
However, you'd still have to make sure your draw method gets called regularly and actually paints what and when you want it to paint. That's besides the question though, so I won't go into more detail on it.
Keep in mind that even swing isn't just some magical construct. When the mouse is clicked, swing will also iterate through all elements you have and see if one of them should get an event. If you write your own Buttons, that's what you're gonna have to do, too.
Giving each button it's own Mouse Listener will only get confusing the bigger your game gets. It will also destroy your game loop as mouse events can be thrown and caught at any time regardless of what you're doing at the moment. If your game logic happens in a seperate thread from swing (which it should), it's even possible that you get an event while you're processing something. Which will screw up your result.
This may also lead to a couple of very odd one-time errors that you don't understand and can't reproduce. So be very careful with that.
I am trying to develop a game that imports the background images from a [100][100] matrix. The matrix will hold int values to correlate to what should be drawn on the background. A loop draws the images to the canvas and updates it based on key input from the user. Everything paints and moves fine however, it is very slow. Is there a better way to load the images rather than the way I am doing it?
This is the main game class:
package com.game.src.main;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable{
static GraphicsEnvironment environment;
static GraphicsDevice device;
private static final long serialVersionUID = 1L;
public static final int WIDTH = 320;
public static final int HEIGHT = WIDTH / 12 * 9;
public static final int SCALE = 2;
public static final String TITLE = "fgfdsa";
private boolean running = false;
private Thread thread;
private Player p;
private Background b;
private Controller c;
private BufferedImage spriteSheet;
boolean isFiring = false;
public void init(){
BufferedImageLoader loader = new BufferedImageLoader();
try{
spriteSheet = loader.loadImage("/sprite_sheet_test.png");
}catch(IOException e){
e.printStackTrace();
}
requestFocus();
addKeyListener(new KeyInput(this));
c = new Controller();
p = new Player(getWidth() / 2, getHeight() / 2, this);
b = new Background(this);
}
private synchronized void start(){
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
private synchronized void stop(){
if(!running)
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(1);
}
public void run(){
init();
long lastTime = System.nanoTime();
final double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
if(delta >= 1){
tick();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000){
timer += 1000;
System.out.println(updates + " Ticks, Fps " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
public void tick(){
p.tick();
b.tick();
c.tick();
}
public void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
b.render(g);
p.render(g);
c.render(g);
g.dispose();
bs.show();
}
public void keyPressed(KeyEvent e){
int key = e.getKeyCode();
switch(key){
case 37:
b.setX(5);
break;
case 38:
b.setY(5);
break;
case 39:
b.setX(-5);
break;
case 40:
b.setY(-5);
break;
case 32:
if(!isFiring){
c.addBullet(new Bullet(p.getX(), p.getY(), this));
isFiring = true;
}
}
}
public void keyReleased(KeyEvent e){
int key = e.getKeyCode();
switch(key){
case 37:
b.setX(0);
break;
case 38:
b.setY(0);
break;
case 39:
b.setX(0);
break;
case 40:
b.setY(0);
break;
case 32:
isFiring = false;
}
}
public static void main(String[] args){
Game game = new Game();
game.setPreferredSize(new Dimension(600, 600));
game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame(game.TITLE);
frame.add(game);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
device = environment.getDefaultScreenDevice();
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
game.start();
}
public BufferedImage getSpriteSheet(){
return spriteSheet;
}
}
This is the background class used to draw the image to the screen:
package com.game.src.main;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class Background {
private BufferedImage grass;
private BufferedImage background;
private BufferedImage tree;
int[][] matrix;
Game game;
//original starting coordinates of matrix to be drawn
int setX = -3200;
int setY = -3200;
//integers used to update coordinates of the matrix to be drawn
int helpX = 0;
int helpY = 0;
public Background(Game game){
this.game = game;
// load matrix into matrix array
GetMatrix gm = new GetMatrix();
matrix = gm.getMatrix();
//import the sprite from game class
background = game.getSpriteSheet();
//call sprite sheet class
SpriteSheet ss = new SpriteSheet(background);
//get coordinates of grass image
grass = ss.grabImage(1, 1, 32, 32);
// get coordinates of tree image
tree = ss.grabImage(4, 1, 32, 32);
}
public void tick(){
//update the start pixel of the background
setX += helpX;
setY += helpY;
if(setX > 0)
setX = 0;
if(setX < -4500)
setX = -4500;
if(setY > 0)
setY = 0;
if(setY < -5340)
setY = -5340;
}
public void render(Graphics g){
int x = 0;
int y = 0;
for(int i = setX; i < setX + 6400; i +=64){
x = 0;
for(int j = setY; j < setY + 6400; j += 64){
switch(matrix[x][y]){
case 0: g.drawImage(grass, i, j, i + 64, j + 64,
0, 0, 32, 32, null);
break;
case 1:
g.drawImage(grass, i, j, i + 64, j + 64,
0, 0, 32, 32, null);
g.drawImage(tree, i, j, i + 64, j + 64,
0, 0, 32, 32, null);
}
x++;
}
y++;
}
}
//sets the background start coordinates from key input
public void setX(int x){
helpX = x;
}
public void setY(int y){
helpY = y;
}
}
It is not obvious what SpriteSheet#grabImage(...) does. But I'm pretty sure that there is some call to BufferedImage#getSubImage(...) involved. Is that right?
If this is right, there are two potential issues here:
When you are loading an image with ImageIO, the type of the resulting image is not known. By "type" I refer to the BufferedImage#getType(). This type may be BufferedImage.TYPE_CUSTOM, particularly when it is a PNG with transparency. When an image with this type is painted, then this painting is awfully slow, because there are some color conversions done internally.
When you call BufferedImage#getSubImage(...), then the image on which you call this method will become "unmanaged". That means that the actual image data can no longer be held directly in video memory. (There are some very involved technical details behind that one. And these details may change between different JRE versions. For example, they changed between Java 6 and Java 7. However, the rule of thumb is: If you want to draw images with high performance, don't call BufferedImage#getSubImage(...) on the image that you want to paint)
The solution for both issues can be to convert the images into managed images of the type BufferedImage.TYPE_INT_ARGB. So for each image that you want to paint, you can call
BufferedImage toPaint = convertToARGB(originalImage);
with this method:
public static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
In your example, you could apply this to your grass and tree images.
Another (maybe even more important) issue is that you seem to be drawing your tiles scaled: You seem to paint a 64x64 pixels sprite with a size of 32x32. If this is correct, then you could consider rescaling the input image once, and then drawing the tiles with their original size of 32x32.
In any case, it's hard to predict how much speedup each of these changes will actually bring, but they should be worth a try.
I am Chris.
I am fairly new to coding, I don't know many terms, this is my third and most successful attempt at learning Java. Yeah, It didn't work out so much was I was younger.
Anyhow, I am trying to create a game but I'm having a slight problem.
I made a 16 x 16 image as my character, however - as expected- it shows up as fairly small.
I have absolutely no clue how to make it larger.
Here is the code:
package code;
import java.awt.Graphics;
public class Skeleton extends Loop{ //Should extend Applet?
private static final long serialVersionUID = 1L;
public void init(){
Thread th= new Thread(this);
th.start();
offscreen = createImage(120,160); // 120, 160
d = offscreen.getGraphics();
addKeyListener(this); //15:43
}
public static final int HEIGHT = 120; //Original Height/Width= "120 x 160"
public static final int WIDTH = 160;
public static final String TITLE= "Test Game BETA";
public static final int SCALE = 3;
public void paint(Graphics g) {
d.clearRect(0, 0, 160, 120); //Error Here, Scale perhaps? -Disregard //0, 0, 160, 120
d.drawImage(him, x, y, this); //12:17 http://www.youtube.com/watch?v=XmRD0PlAXEY
g.drawImage(offscreen, 0, 0, this);
}
public void update(Graphics g){
paint(g);
} //Finished at 15:33 ERROR w/ the circle -Fixed
}
//2D Tile Engine Must be Created
The drawImage function can take a destination size, and it will scale your image for you:
drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
Reference: http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html
I am creating a GUI, and am fairly new to swing and awt. I am trying to create a gui that, upon launch, sets the background to an image, then uses a method to create a slideshow of sorts. I have attempted it, and I am not attached to the code so I am able to take both revisions and/or whole new concepts.
EDIT(9/15/13): I am having trouble with the slideshow, I cant seem to get it to work.
Here is my current code.
public class MainFrame extends JFrame{
JLabel backgroundL = null;
private JLabel bakckgroundL;
BufferedImage backimg;
Boolean busy;
double width;
double height;
public MainFrame() throws IOException {
initMainframe();
}
public void initMainframe() throws IOException {
//misc setup code, loads a default jpg as background
setTitle("Pemin's Aura");
busy = true;
String backgroundDir = "resources/frame/background.jpg";
backimg = ImageIO.read(new File(backgroundDir));
backgroundL = new JLabel(new ImageIcon(backimg));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
refreshframe();
setVisible(true);
busy = false;
}
public void adjSize() { // the attempted start of a fullscreen mode
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
width = this.getWidth();
height = this.getHeight();
setVisible(true);
}
public void setmastheadText() {//unfinished code
busy = true;
busy = false;
}
public void setbackground() {
add(backgroundL);
}
public void refreshframe() { //should refresh image?
setSize(2049, 2049);
setSize(2048, 2048);
}
public void loadingscreen() throws IOException, InterruptedException {
//this is the code in question that is faulty.
if (busy == false) {
busy = true;
String backgroundDir1 = "resources/frame/background.jpg";
String backgroundDir2 = "resources/frame/scr1.jpg";
String backgroundDir3 = "resources/frame/scr2.jpg";
BufferedImage backimg1 = ImageIO.read(new File(backgroundDir1));
BufferedImage backimg2 = ImageIO.read(new File(backgroundDir2));
BufferedImage backimg3 = ImageIO.read(new File(backgroundDir3));
backgroundL = new JLabel(new ImageIcon(backimg1));
Thread.sleep(2000);
setbackground();
setVisible(true);
backgroundL = new JLabel(new ImageIcon(backimg2));
setbackground();
setVisible(true);
Thread.sleep(2000);
bakckgroundL = new JLabel(new ImageIcon(backimg3));
setbackground();
setVisible(true);
if(backimg != null) {
backgroundL = new JLabel(new ImageIcon(backimg));;
}
}
busy = false;
}//end of loading screen
See ImageViewer for a working example of displaying images using a Swing based Timer.
See also How to use Swing Timers.
And while I'm here, another (prettier) example of animating an image. It uses this Mercator map of land masses. The image can be tiled horizontally, and therefore be scrolled left/right as needed.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class WorldView {
public static void main(String[] args) throws Exception {
URL url = new URL("http://i.stack.imgur.com/P59NF.png");
final BufferedImage bi = ImageIO.read(url);
Runnable r = new Runnable() {
#Override
public void run() {
int width = 640;
int height = 316;
Graphics2D g = bi.createGraphics();
float[] floats = new float[]{0f, .4f, .55f, 1f};
Color[] colors = new Color[]{
new Color(20, 20, 20, 0),
new Color(0, 10, 20, 41),
new Color(0, 10, 20, 207),
new Color(0, 10, 20, 230),};
final LinearGradientPaint gp2 = new LinearGradientPaint(
new Point2D.Double(320f, 0f),
new Point2D.Double(0f, 0f),
floats,
colors,
MultipleGradientPaint.CycleMethod.REFLECT);
final BufferedImage canvas = new BufferedImage(
bi.getWidth(), bi.getHeight() + 60,
BufferedImage.TYPE_INT_RGB);
final JLabel animationLabel = new JLabel(new ImageIcon(canvas));
ActionListener animator = new ActionListener() {
int x = 0;
int y = 30;
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g = canvas.createGraphics();
g.setColor(new Color(55, 75, 125));
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
int offset = (x % bi.getWidth());
g.drawImage(bi, offset, y, null);
g.drawImage(bi, offset - bi.getWidth(), y, null);
g.setPaint(gp2);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
g.dispose();
animationLabel.repaint();
x++;
}
};
Timer timer = new Timer(40, animator);
timer.start();
JOptionPane.showMessageDialog(null, animationLabel);
timer.stop();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
Here is a version of that image with the equator added (it is 44 pixels 'south' of the center of the image).
You're calling Thread.sleep(...) and likely on the EDT or Swing event thread (full name is the Event Dispatch Thread). This thread is responsible for all Swing painting/drawing and user interactions, and so sleeping it will only serve to freeze your entire GUI. Instead you should use a Swing Timer to allow you to swap a JLabel's ImageIcon.
So, briefly:
Don't call Thread.sleep(...) on the Swing event thread (Event Dispatch Thread or EDT).
Do use a Swing Timer to do your repeating delayed actions.
Don't make and add many JLabels. Just make and add one.
Do Swap the ImageIcon that the JLabel displays by calling setIcon(...) on the label.
Better (cleaner) to write if (busy == false) { as if (!busy) {
e.g.,
ImageIcon[] icons = {...}; // filled up with your ImageIcons
if (!busy) {
int timerDelay = 2000;
new Timer(timerDelay, new ActionListener() {
private int i = 0;
public void actionPerfomed(ActionEvent e) {
myLabel.setIcon(icons(i));
i++;
if (i == icons.length) {
((Timer)e.getSource).stop();
}
};
}).start();
}
I'm new to Java applet programming, so excuse me if this is a very basic question, but I've googled it extensively and have only found semi-related problems and solutions.
I'm writing a simple demonstration of some geometric algorithms, and when I repaint(), only some of my graphics primitives are rendered to the screen. Every time my applet redraws, a seemingly random subset of my lines and ellipses are painted. The only pattern to it is that the primitives that are rendered are always from the beginning of the drawing. I.E, sometimes it will draw primitives 0-2, sometimes 0-5, sometimes the whole batch.
I would like to point out that, as far as I can tell, this is not the classic "flickering" that can be solved with double-bufferring. To my understanding, flickering is when for short periods of time you can see a partially rendered applet before it finishes rendering. In my case, however, if it doesn't finish rendering, it never finishes unless I redraw() again and get lucky. I've tried double buffering:
public void update(Graphics g) {
Graphics offgc;
Image offscreen = null;
Dimension d = size();
// create the offscreen buffer and associated Graphics
offscreen = createImage(d.width, d.height);
offgc = offscreen.getGraphics();
// clear the exposed area
offgc.setColor(getBackground());
offgc.fillRect(0, 0, d.width, d.height);
offgc.setColor(getForeground());
// do normal redraw
paint(offgc);
// transfer offscreen to window
g.drawImage(offscreen, 0, 0, this);
}
But it doesn't seem to help at all. If it's useful, here's some pics of what's happening. This is what it's supposed to look like:
But most of the time it looks something like this:
or this:
Thanks in advance!
This isn't really how double buffering should work, no is it how the paint process works.
Don't override update.
Don't override paint of top level containers (like Applet/JApplet/Frame/JFrame) where possible
Use a "paint" panel onto which you can render, preferably something like JPanel. Swing components provide double buffering support
A double buffer should be painted on out side the paint cycle and only updated when needed, this makes the overall paint process faster as you're not having re-render the content unnecessarily.
When it comes time to update the buffer, render to a temp buffer first, this ensures that any repaints that might occur while you're updating aren't reflected back to the screen prematurely...
public class TestPaintGeometry {
public static void main(String[] args) {
new TestPaintGeometry();
}
public TestPaintGeometry() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new ShowPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ShowPane extends JPanel {
private GeometryPane geoPane;
public ShowPane() {
setLayout(new BorderLayout());
geoPane = new GeometryPane();
JButton redrew = new JButton("Redraw");
add(geoPane);
add(redrew, BorderLayout.SOUTH);
redrew.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
geoPane.redrew();
}
});
}
}
public class GeometryPane extends JPanel {
private BufferedImage buffer;
public void redrew() {
Path2D.Float path = new Path2D.Float();
int width = getWidth();
int height = getHeight();
int points = Math.max(10, (int) Math.round(Math.random() * 100));
for (int index = 0; index < points; index++) {
int x = (int) Math.round(Math.random() * width);
int y = (int) Math.round(Math.random() * height);
if (index > 0) {
path.lineTo(x, y);
} else {
path.moveTo(x, y);
}
}
BufferedImage tmp = createCompatibleImage(width, height);
Graphics2D g2d = tmp.createGraphics();
g2d.setColor(Color.BLACK);
g2d.draw(path);
g2d.dispose();
buffer = tmp;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (buffer != null) {
int x = (getWidth() - buffer.getWidth()) / 2;
int y = (getHeight() - buffer.getHeight()) / 2;
g.drawImage(buffer, x, y, this);
}
}
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
public static BufferedImage createCompatibleImage(int width, int height) {
return createCompatibleImage(width, height, Transparency.TRANSLUCENT);
}
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
}
This allows you deploy the GeometryPane to a JFrame or JAppelt as it's not constrained by the legacy of it's inheritance...