Java School Assignment? Project Due Tomorrow? Chat LIVE With A Programming Expert!

Welcome to Dream.In.Code
Become a Java Expert!

Join 307,149 Java Programmers for FREE! Get instant access to thousands of Java experts, tutorials, code snippets, and more! There are 1,723 people online right now. Registration is fast and FREE... Join Now!




Java Game (Actually the most efficient way to repaint)

 
Reply to this topicStart new topic

> Java Game (Actually the most efficient way to repaint), No page flicking... you get all control... if you want

Rating  5
pbl
Group Icon



post 6 Jul, 2009 - 08:43 PM
Post #1


OK this is the basic in all page flickering and how to avoid flickering
First a basic concept:
- computer screens are refreshed 60 times a second in North America and 50 times a second in Europe
Java (and Swing) do a good job at it and refresh the screen the best way it can manage but some times it is nto the best way so sometimes, particularly in Games, you will need to do the job and help a little bit the Java GUI for best performance.

How to do it ? I'll teach you right away.
Sorry for my European and Asiatic friend, I'll do it the Obama way biggrin.gif

The idea is to draw what you have to draw on 2 buffers, wait for the part of the GUI that repaint the screen, and tell it from which buffer in memory it has to redraw the image

I have 3 classes here... feel free to cut & paste them

OK the first one in to create the main frame... you are use to it
and we will use a Canvas to do our drawing... Canvas are needed because we can apply a BfferStartegy to them
BuffeStartegy is the bumber of buffers we will use to draw "offscreen".
Here is a first glitch/trap: before applying a buffer strategy to a Component this component has to be visible so here is my first class
For this example we will use the stupid "bouncing ball" program:
CODE

import java.awt.BorderLayout;
import javax.swing.*;


public class GameFrame extends JFrame {
    GameFrame() {
        // frame description
        super("Bouncing ball");
        // our Canvas
        GameCanvas canvas = new GameCanvas();
        add(canvas, BorderLayout.CENTER);
        // set it's size and make it visible
        setSize(600, 400);
        setVisible(true);        
        // now that is visible we can tell it that we will use 2 buffers to do the repaint
        // befor being able to do that, the Canvas as to be visible
        canvas.createBufferStrategy(2);
    }
    // just to start the application
    public static void main(String[] args) {
        // instance of our stuff
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new GameFrame();
            }
        });
    }
}

To this JFrame we will add a Canvas, because Canvas support BufferStrategy
This class that extends Canvas will use a Timer to be recalled 60 times a second... this is everytime the hardware refresh the screen
60 times (in NorthAmerica) a second is every 16/1000 a second
There are no need to refresh the screen more often... if you do so
1) the human eye won't see it
2) you will ask the blitter to show something and then to show something else and only the last one will be displayed
CODE

import java.awt.*;
import java.awt.image.BufferStrategy;
import java.util.Random;

import javax.swing.Timer;

// OK this the class where we will draw
public class GameCanvas extends Canvas{
    // the initial position of the ball in the canvas that will be determine randomly
    int ballX, ballY;
    // the delta we apply to the x and y position at each repaint
    // here it is set to 1... would should have a larger value in "real" life
    // but for testing purpose that will give you the fastest possible value for a 1 pixel update
    int deltaX = +1, deltaY = +1;
    // the size of the ball in pixels
    final int BALLSIZE = 25;
    // a flag if repaint in progress (needed if our computation are to long)
    boolean repaintInProgress = false;
    // we use 2 pages to do our buffering
    // just for demo purpose we will change the background color everytime we repaint a frame
    // you will never do that in real life :-)
    Color[] back = {Color.YELLOW, Color.MAGENTA};
    // just used to determine on which buffer I am writting... will not be used in real life
    int page = 0;
    // a random object to determine where the initial position of the ball will be
    Random ran = new Random();
    // this is a Canvas but I wont't let the system when to repaint it I will do it myself
    GameCanvas() {
        // so ignore System's paint request I will handle them
        setIgnoreRepaint(true);
        // a random place to start the ball
        ballX = ran.nextInt(580);
        ballY = ran.nextInt(380);
        // build Chrono that will call me
        Chrono chrono = new Chrono(this);
        // ask the chrono to calll me every 60 times a second so every 16 ms
        new Timer(16, chrono).start();
    }
    
    // my own paint method that repaint off line and switch the displayed buffer
    // according to the VBL
    public void myRepaint() {
        // wasting too much time doing the repaint... ignore it
        if(repaintInProgress)
            return;
        // so I won't be called 2 times in a row for nothing
        repaintInProgress = true;
        // get actual Canvas size so I can check if I am out of bounds
        Dimension size = getSize();
        // test for all debordement possibilities on the X axis
        if(deltaX > 0) {
            if(ballX > size.width - BALLSIZE)
                deltaX = -deltaX;
        }
        else {
            if(ballX < 0)
                deltaX = -deltaX;
        }
        // check on the Y axis
        if(deltaY > 0) {
            if(ballY > size.height - BALLSIZE)
                deltaY = -deltaY;
        }
        else {
            if(ballY < 0)
                deltaY = -deltaY;
        }
        // update ball position
        ballX += deltaX;
        ballY += deltaY;
        // ok doing the repaint on the not showed page
        BufferStrategy strategy = getBufferStrategy();
        Graphics graphics = strategy.getDrawGraphics();
        // this is for testing purpose you would not do that in real life
        // we change the background color to that you will see that the page are flipped
        page++;
        page %= 2;
        // again testing purpose we flip backgound color every repaint
        graphics.setColor(back[page]);
        graphics.fillRect(0, 0, size.width, size.height);
        // now we draw the ball
        graphics.setColor(Color.BLACK);
        graphics.fillOval(ballX, ballY, BALLSIZE, BALLSIZE);
        if(graphics != null)
            graphics.dispose();
        // show next buffer
        strategy.show();
        // synchronized the blitter page shown
        Toolkit.getDefaultToolkit().sync();

        // ok I can be called again
        repaintInProgress = false;
    }
}

finally the Chrono class that recalls me 60 times a second
CODE

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/** Will be called at each blitter page */
public class Chrono implements ActionListener {

    GameCanvas gc;
    // constructor that receives the GameCanvas that we will repaint every 60 milliseconds
    Chrono(GameCanvas gc) {
        this.gc = gc;
    }
    // calls the method to repaint the anim
    public void actionPerformed(ActionEvent arg0) {
        gc.myRepaint();
    }

}

This is the fastest way you can refresh a screen in Java
Change the deltaX and deltaY the ball will go faster

The keypoints:
- you can inform a Canvas that it should not do it's own repaint
- you can overload that repaint()
- if you do it by hand call the toolkit synch() method to synchronize the VBL

Hope this helps
Go to the top of the page
+Quote Post


Register to Make This Ad Go Away!

pbl
Group Icon



post 9 Jul, 2009 - 07:37 PM
Post #2
If your computations take more than 1 milli second I produce a new version that performs calculations and the actual repaint() in 2 different threads

A little bit more efficient and if your calculation are really complex, this new version just skip a VBL (60 milliseconds) and repaint at the next monitor refresh

http://www.dreamincode.net/forums/showtopic113977.htm
Go to the top of the page
+Quote Post

bbq
Group Icon



post 11 Oct, 2009 - 10:37 PM
Post #3
Very nice tutorial pbl, i will keep this in mind for sure pirate.gif
Go to the top of the page
+Quote Post

marvin lumanas
*



post 15 Oct, 2009 - 12:40 AM
Post #4
QUOTE(pbl @ 6 Jul, 2009 - 08:43 PM) *

OK this is the basic in all page flickering and how to avoid flickering
First a basic concept:
- computer screens are refreshed 60 times a second in North America and 50 times a second in Europe
Java (and Swing) do a good job at it and refresh the screen the best way it can manage but some times it is nto the best way so sometimes, particularly in Games, you will need to do the job and help a little bit the Java GUI for best performance.

How to do it ? I'll teach you right away.
Sorry for my European and Asiatic friend, I'll do it the Obama way biggrin.gif

The idea is to draw what you have to draw on 2 buffers, wait for the part of the GUI that repaint the screen, and tell it from which buffer in memory it has to redraw the image

I have 3 classes here... feel free to cut & paste them

OK the first one in to create the main frame... you are use to it
and we will use a Canvas to do our drawing... Canvas are needed because we can apply a BfferStartegy to them
BuffeStartegy is the bumber of buffers we will use to draw "offscreen".
Here is a first glitch/trap: before applying a buffer strategy to a Component this component has to be visible so here is my first class
For this example we will use the stupid "bouncing ball" program:
CODE

import java.awt.BorderLayout;
import javax.swing.*;


public class GameFrame extends JFrame {
    GameFrame() {
        // frame description
        super("Bouncing ball");
        // our Canvas
        GameCanvas canvas = new GameCanvas();
        add(canvas, BorderLayout.CENTER);
        // set it's size and make it visible
        setSize(600, 400);
        setVisible(true);        
        // now that is visible we can tell it that we will use 2 buffers to do the repaint
        // befor being able to do that, the Canvas as to be visible
        canvas.createBufferStrategy(2);
    }
    // just to start the application
    public static void main(String[] args) {
        // instance of our stuff
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new GameFrame();
            }
        });
    }
}

To this JFrame we will add a Canvas, because Canvas support BufferStrategy
This class that extends Canvas will use a Timer to be recalled 60 times a second... this is everytime the hardware refresh the screen
60 times (in NorthAmerica) a second is every 16/1000 a second
There are no need to refresh the screen more often... if you do so
1) the human eye won't see it
2) you will ask the blitter to show something and then to show something else and only the last one will be displayed
CODE

import java.awt.*;
import java.awt.image.BufferStrategy;
import java.util.Random;

import javax.swing.Timer;

// OK this the class where we will draw
public class GameCanvas extends Canvas{
    // the initial position of the ball in the canvas that will be determine randomly
    int ballX, ballY;
    // the delta we apply to the x and y position at each repaint
    // here it is set to 1... would should have a larger value in "real" life
    // but for testing purpose that will give you the fastest possible value for a 1 pixel update
    int deltaX = +1, deltaY = +1;
    // the size of the ball in pixels
    final int BALLSIZE = 25;
    // a flag if repaint in progress (needed if our computation are to long)
    boolean repaintInProgress = false;
    // we use 2 pages to do our buffering
    // just for demo purpose we will change the background color everytime we repaint a frame
    // you will never do that in real life :-)
    Color[] back = {Color.YELLOW, Color.MAGENTA};
    // just used to determine on which buffer I am writting... will not be used in real life
    int page = 0;
    // a random object to determine where the initial position of the ball will be
    Random ran = new Random();
    // this is a Canvas but I wont't let the system when to repaint it I will do it myself
    GameCanvas() {
        // so ignore System's paint request I will handle them
        setIgnoreRepaint(true);
        // a random place to start the ball
        ballX = ran.nextInt(580);
        ballY = ran.nextInt(380);
        // build Chrono that will call me
        Chrono chrono = new Chrono(this);
        // ask the chrono to calll me every 60 times a second so every 16 ms
        new Timer(16, chrono).start();
    }
    
    // my own paint method that repaint off line and switch the displayed buffer
    // according to the VBL
    public void myRepaint() {
        // wasting too much time doing the repaint... ignore it
        if(repaintInProgress)
            return;
        // so I won't be called 2 times in a row for nothing
        repaintInProgress = true;
        // get actual Canvas size so I can check if I am out of bounds
        Dimension size = getSize();
        // test for all debordement possibilities on the X axis
        if(deltaX > 0) {
            if(ballX > size.width - BALLSIZE)
                deltaX = -deltaX;
        }
        else {
            if(ballX < 0)
                deltaX = -deltaX;
        }
        // check on the Y axis
        if(deltaY > 0) {
            if(ballY > size.height - BALLSIZE)
                deltaY = -deltaY;
        }
        else {
            if(ballY < 0)
                deltaY = -deltaY;
        }
        // update ball position
        ballX += deltaX;
        ballY += deltaY;
        // ok doing the repaint on the not showed page
        BufferStrategy strategy = getBufferStrategy();
        Graphics graphics = strategy.getDrawGraphics();
        // this is for testing purpose you would not do that in real life
        // we change the background color to that you will see that the page are flipped
        page++;
        page %= 2;
        // again testing purpose we flip backgound color every repaint
        graphics.setColor(back[page]);
        graphics.fillRect(0, 0, size.width, size.height);
        // now we draw the ball
        graphics.setColor(Color.BLACK);
        graphics.fillOval(ballX, ballY, BALLSIZE, BALLSIZE);
        if(graphics != null)
            graphics.dispose();
        // show next buffer
        strategy.show();
        // synchronized the blitter page shown
        Toolkit.getDefaultToolkit().sync();

        // ok I can be called again
        repaintInProgress = false;
    }
}

finally the Chrono class that recalls me 60 times a second
CODE

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/** Will be called at each blitter page */
public class Chrono implements ActionListener {

    GameCanvas gc;
    // constructor that receives the GameCanvas that we will repaint every 60 milliseconds
    Chrono(GameCanvas gc) {
        this.gc = gc;
    }
    // calls the method to repaint the anim
    public void actionPerformed(ActionEvent arg0) {
        gc.myRepaint();
    }

}

This is the fastest way you can refresh a screen in Java
Change the deltaX and deltaY the ball will go faster

The keypoints:
- you can inform a Canvas that it should not do it's own repaint
- you can overload that repaint()
- if you do it by hand call the toolkit synch() method to synchronize the VBL

Hope this helps

how can i copy its to my netbeans??
Go to the top of the page
+Quote Post

ts230
**



post 21 Oct, 2009 - 06:58 AM
Post #5
I like your idea. I will definitely use it from time to time.
Go to the top of the page
+Quote Post

pbl
Group Icon



post 30 Oct, 2009 - 02:41 PM
Post #6
QUOTE(marvin lumanas @ 15 Oct, 2009 - 12:40 AM) *

how can i copy its to my netbeans??

Just cut & paste it
Go to the top of the page
+Quote Post

Dophert
*



post 13 Nov, 2009 - 07:40 AM
Post #7
This is not the fastest way, but it's a lot better than just overriding the paint method.
Go to the top of the page
+Quote Post


Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 


Lo-Fi Version Time is now: 11/21/09 04:19PM

Live Java Help!

Be Social

Dream.In.Code RSS Feed Dream.In.Code LinkedIn Group Follow Us On Twitter Fan Us On Facebook

Java Tutorials

Reference Sheets

Java Snippets

DIC Chatroom

Bye Bye Ads

Monthly Drawing

Thumb Drive

Top Contributors

Top 10 Kudos This Month