Feelsbadman
Feelsbadman

Reputation: 1165

JAVA - Graphics2D dispose() method not disposing filled objects

I'm trying to learn Graphics2D library while building small games such as 2D car games, platformer games and so on... Today I decided to make a space-based game and obvoiusly in space you need stars. So I decided to use fillOval() to draw small circles which would give the effect of night-sky. BUT, whenever I try to move the stars or any other object, the older frames just stay there and a bunch of layers are drawn one after another. I thought that dipose() method would be enough... but it seems like it aint.

What options do I have to fix this problem?

Here's a preview of what it looks like when i run the code.

enter image description here

Main.java (irrelevant code filtered out)

    package asteroids;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;


public class Main extends Canvas implements KeyListener {

    private boolean gameOver;
    private BufferStrategy backBuffer;  
    private Dimension dimension = new Dimension(Config.WINDOW_WH[0], Config.WINDOW_WH[1]);
    private List<Star> stars = new ArrayList<Star>();
    private HashMap<Integer,Boolean> keyDownMap = new HashMap<Integer, Boolean>();
    private Ship ship;


    public Main() {
        // Initialize Window
        initWindow();
        addKeyListener(this);

        this.createBufferStrategy(2);               
        backBuffer = this.getBufferStrategy();

        // init variables
        gameOver = false;

        // Generating stars
        generateStars();

        // Init spaceship
        ship = new Ship(25,36,"ship.png");

        // Init loop
        gameLoop();

    }

    public void initWindow(){
        JFrame window = new JFrame("Asteroids"); 
        setPreferredSize(dimension); 

        window.add(this);           
        window.pack();  
        window.setResizable(false);
        window.setVisible(true); 
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setBackground(new Color(54, 71, 99));


        window.requestFocus();

    }

    public void update() {

        if(keyDownMap.containsKey(KeyEvent.VK_ESCAPE)){
            gameOver = false;
            System.exit(0);
        }

        /*for(Star s: stars) {
            s.update(keyDownMap);
        }*/
        this.ship.update(keyDownMap);

    }


    public void render(){
        Graphics2D g = (Graphics2D) backBuffer.getDrawGraphics();

        // Stars
        for(Star s: stars) {
            g.fillOval(s.posx - (s.width/2), s.posy - (s.height/2), s.width, s.height);
        }

        // Spaceship
        g.drawImage(
                this.ship.getImage(), 
                this.ship.posx, this.ship.posy, 
                this.ship.width, this.ship.height, null);


        g.dispose();
        backBuffer.show();

    }

    public void generateStars() {
        for(int i = 0;i < 20;i++) {
            int starX = new Random().nextInt(Config.WINDOW_WH[0]-10)+5;
            int starY = new Random().nextInt(Config.WINDOW_WH[1]-10)+5;
            stars.add(new Star(starX, starY));
        }
    }


    public void gameLoop(){
        while(!gameOver){
            update();
            render();
            try{ Thread.sleep(20);}catch(Exception e){};
        }

    }

    public static void main(String[] args) {

        new Main();
    }


    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyPressed(KeyEvent e) {
        keyDownMap.put(e.getKeyCode(), true);

    }

    @Override
    public void keyReleased(KeyEvent e) {
        keyDownMap.remove(e.getKeyCode());

    }


}

Star.java

package asteroids;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.HashMap;
import java.util.Random;
public class Star {

    int width, height;
    int posx, posy;


    /** CONSTRUCTOR **/
    public Star(int x, int y) {
        int rand = new Random().nextInt(Config.STAR_SIZES.length);
        width = Config.STAR_SIZES[rand];
        height = Config.STAR_SIZES[rand];
        posx = x;
        posy = y;


    }

    public void update(HashMap keyDownMap) {
        if(keyDownMap.containsKey(KeyEvent.VK_LEFT))
            this.posx += 1;

        if(keyDownMap.containsKey(KeyEvent.VK_RIGHT))
            this.posx -= 1;

        if(keyDownMap.containsKey(KeyEvent.VK_UP))
            this.posy += 1;

        if(keyDownMap.containsKey(KeyEvent.VK_DOWN))
            this.posy -= 1;



    }


}

Ship.java

package asteroids;

import java.awt.Image;
import java.awt.event.KeyEvent;
import java.util.HashMap;

import javax.swing.ImageIcon;

public class Ship {

    public int posx, posy;
    public int width, height;
    private Image image;

    public Ship(int w, int h, String img) {
        this.posx = Config.WINDOW_WH[0]/2;
        this.posy = Config.WINDOW_WH[1]/2;
        this.width = w;
        this.height = h;
        this.image = new ImageIcon(getClass().getResource("/"+img)).getImage();
    }

    public Image getImage() {
        return this.image;
    }

    public void setPosx(int x) {posx = x;}
    public void setPosy(int y) {posy = y;}
    public void setImg(Image img) {
        this.image = img; 
    }

    public void update(HashMap keyDownMap) {
        if(keyDownMap.containsKey(KeyEvent.VK_LEFT))
            this.posx += 1;

        if(keyDownMap.containsKey(KeyEvent.VK_RIGHT))
            this.posx -= 1;

        if(keyDownMap.containsKey(KeyEvent.VK_UP))
            this.posy += 1;

        if(keyDownMap.containsKey(KeyEvent.VK_DOWN))
            this.posy -= 1;

    }

}

Config.java

import java.awt.Color;

public class Config {

    // MAIN CANVAS SETTINGS
    public static int[] WINDOW_WH = {500, 500};
    public static String WINDOW_BG_IMG = "";
    public static Color WINDOW_BG_CLR = new Color(40, 42, 45);

    // OBJECT SETTINGS
    public static int[] STAR_SIZES = {10,5,8,3,7};
}

EDIT:

After I tried to add a background image g.drawImage(this.bg,0,0,Config.WINDOW_WH[0], Config.WINDOW_WH[1], null); before drawing stars and the ship, it doesn't happen anymore. But im wondering if this is a fix or just a bad practice for avoiding the un-cleared frames.

Upvotes: 0

Views: 1127

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347214

BUT, whenever I try to move the stars or any other object, the older frames just stay there and a bunch of layers are drawn one after another.

Yes, that's how painting works. Think of the Graphics context like a physical canvas. Each time you paint on it, you are adding more more content to it. The API won't clear it for you.

There advantages to this, if you're performing an additive painting process, you may not wish to refresh it on each pass, but in your case, you're going to have to clear it each time.

I thought that dipose() method would be enough... but it seems like it aint

No, dispose releases any internal resources the context might be holding, freeing up memory. Having said that, you shouldn't dispose of a context you did create, as it can prevent future operations from been applied.

If you take a look at the example from the tutorials it clear shows the Graphics context been clear/prepared for each frame...

Graphics g = bufferStrategy.getDrawGraphics();
if (!bufferStrategy.contentsLost()) {
    g.setColor(COLORS[i]);
    g.fillRect(0,0,bounds.width, bounds.height);
    bufferStrategy.show();
    g.dispose();
}

I would also recommend having a look at the example in the JavaDocs which presents a basic loop operation

Upvotes: 1

Related Questions