Showing posts with label Programming java applet games. Show all posts
Showing posts with label Programming java applet games. Show all posts

Tuesday, October 27, 2009

Programming java applet games part 5 - Java 2d Graphics and Graphics2D class

After some days without any updated finally i have found some time to continue my tutorials. The next part is about the Java and 2d graphics. Here i will represent  the Java classes that you will need to implement the Asteroids java applet. First of all i must present you the Graphics2D class. Graphics2D extends the base class Graphics and provide methods to render enhanced graphics. So if this Graphics2D is so strong why it has not replace the standard Graphics class? The answer is simple; it is just for compatibility reasons with the Java 1.1 and previous version that use the standard Graphics class. So if you use this class for your games then they will run only on browsers that support versions of Java greater than 1.1.
Now lets see the first code snippet
public void paint(Graphics g) {
      Graphics2D g2d = (Graphics2D)g;  // make a reference
}
as you can see we just make a Graphics2D reference on the standard Graphics object that jvm passes automatically to paint method. The features that Graphics2D provides are Colors patterns like gradient fills, fill patterns from images, transparency, local fonts access, new Pen styles, 2d affine transformations.
Java provides classes that represent 2d Objects like triangles, ellipses, polygons etc. For out game everything will be represented by this games. The player object will be represented by a triangle, asteroids will be polygons, bullets will be lines. So we will use the classes that Java already implemented for us.

The first class that we will see is the Polygon. You can find full documentation about the class here http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Polygon.html. When you want to create a polygon you just define a Polygon variable and the add points into it.  
Polygon p = new Polygon();
for( int i = 0; i < NUM_OF_POINTS ; i ++ ) {
    p.addPoint( x[i], y[i] );
}

at this snippet of code i assume that there are two arrays x,y that contains the polygon coordinates and a NUM_OF_POINTS int variable with the size of these arrays. To draw this polygon just code
g.draw( p );
or
g.fill( p );
The first one will draw only the outer lines of the polygon will the second will draw a polygons and the inside area.
Ok, for the rendering phase of out game we can use the Polygon class to draw triangle for the user spaceship, polygons for the asteroids and lines for the bullets. What we can do for the update position of our objects?
Java provides classes that implements affine transformations . Τhese transformations are  translate, rotation, scaling, and shearing. With combinations of these transformations we will implement the object movements on the screen. The meanings of translation is to move polygon points to certain coordinates , the  rotations rotates the points , and scaling change the size of the objects. Shearing stretches unevenly the points and will be not used for our game. When we want to move an object make translate transformations onto Graphics2D object and then make the draw for example
g.translate(15.0, 32.0);
g.draw( p );
Thats it for now, play with these classes and in the next tutorial (i hope that it will be soon) i will use these classes into Asteroids game to draw objects and move them.

As allways have nice coding time :-)

Sunday, September 20, 2009

Programming java applet games part 4 - The Game Menu


The next step for our game will be the implementation on the Game Menu. This is the place where a game gives to players the available options ie Start Game,Option, Exit etc. In our game we will have only two options a) Start Game, B) Show Keys. We do not have an Exit option becaouse we write an applet game and we don't need this.
To implement the menu we need to change the run method to run more than one different "game loops", one for eatch game state.
public void run() {
        try {
            boolean exit = false;
            int menuOption = 0;
            while ( true ) {
                menuOption = getMenuOption();
                if ( menuOption == 0 ) {
                    gamePlayLoop();
                } else {
                    printKeys();
                }
            }
        } catch ( InterruptedException ex ) {
            Logger.getLogger( AsterpodsApplet.class.getName() ).log( Level.SEVERE, null, ex );
        }
    }
Let me explain the above code. The while statement changed to run forever because that we do not have an exit option. Then we define tree different game loops seperated in different methods.
a) int gerMenuOptions()
Contains the code to draw the game options and handle the user choices. The returned value is the user choice.
b)void gamePlayLoop()
the main game loop, it will contain all the action and it will be implemented later
c)void printKeys()
This method just prints key info

Now lets dig into this methods by starting with getMenuOptions
//the loop that will run while the player is on game menu
    private int getMenuOption() throws InterruptedException {
        long lastTime;
        int retValue = 0;//this will contain the players choice
        while ( true ) {
            lastTime = System.currentTimeMillis();
            drawMenu( retValue );
            if ( this.up_pressed == true ) {
                retValue = 0;
            } else if( this.down_pressed == true ) {
                retValue = 1;
            } else if ( this.space_pressed ) { // if it is the space that pressed then the user made his choice
                consumeTyped();
                break;
            }
            consumeTyped();
            repaint(); //rapaints the applet
            lastTime = System.currentTimeMillis() - lastTime;
            Thread.sleep( GAME_LOOP_MAX_TIME - lastTime );
        }
        return retValue;
    }
getMenuOption uses the method
void drawMenu( int option );
to draw the menu Options on the screen and check the user input to return game choices and change the state of the game. The user choice will be returned with the press of the space key while the up and down arrows change the choice.This is implemented by this
 if ( this.up_pressed == true ) {
                retValue = 0;
 } else if( this.down_pressed == true ) {
                retValue = 1;
 } else if ( this.space_pressed ) { // if it is the space that pressed then the user made his choice
                consumeTyped();
                break;
 }
  consumeTyped();
For this method except the up,down etc switches that i used in the previous tutorial to track user input i use extra switches up_presse, down_pressed etc. The difference between these switches is that the *_pressed versions will be used  to track that the keys pressed once and we do not care if they are still pressed.
The method
void consumeTyped()
is used to clear the *_pressed switches.

I will go out of the getMenuOptions and i will go to printKeys without explain the drawLogo becouse i want to let this method for the end of this tutorial.
private void printKeys() throws InterruptedException {
        long lastTime;
        while ( true ) {
            lastTime = System.currentTimeMillis();
            drawKeys();
            if ( this.space_pressed ) { // if it is the space that pressed then the user made his choice
                consumeTyped();
                break;
            }
            repaint(); //rapaints the applet
            lastTime = System.currentTimeMillis() - lastTime;
            Thread.sleep( GAME_LOOP_MAX_TIME - lastTime );
        }
    }
printKeys Method is identical to getMenuOptions with the difference that tracks only of the space key and calls the drawKeys to print the proper messages on the screen.

The interessting part of this part of the series is methods
void drawLogo(), void drawKeys() and void drawMenu( int retValue )
 private void drawLogo() {
        backBuffer.setColor( Color.black );
        backBuffer.fillRect( 0, 0, WIDTH, HEIGHT );
        color += nextInc;
        if ( color > 255 ) {
            color = 255;
            nextInc *= -1;
        } else if ( color < 100 ) {
            color = 100;
            nextInc *= -1;
        }
        //color = 250;
        Color c = new Color( color, color, color );
        backBuffer.setColor( c );

        //prints logo on the center
        String logo = "Asteroids - the rebirth";
        int strLength = backBuffer.getFontMetrics().stringWidth( logo );

        //the >> 1 is equals to /2
        backBuffer.drawString( logo, (WIDTH - strLength) >> 1, 140 );
    }

    //prints the key controls for the game
    private void drawKeys() {
        drawLogo();
        backBuffer.setColor( Color.WHITE );

        String[] lines = new String[ 6 ];
        lines[ 0] = "Press the Up arrow to accelerate";
        lines[ 1] = "Press the Down arrow to decelerate";
        lines[ 2] = "Press the Left arrow to turn left";
        lines[ 3] = "Press the Right arrow to turn right";
        lines[ 4] = "Press the Space to fire";
        lines[ 5] = "Press the Escape to exit";

        for ( int i = 0; i < lines.length; i++ ) {
            int strLength = backBuffer.getFontMetrics().stringWidth( lines[i] );
            backBuffer.drawString( lines[i], (WIDTH - strLength) >> 1, 170 + i * 15 );
        }

        String exitMessage = "Press space to go back";
        int strLength = backBuffer.getFontMetrics().stringWidth( exitMessage );
        backBuffer.drawString( exitMessage, (WIDTH - strLength) >> 1, 170 + lines.length * 15 + 30 );
    }

    private void drawMenu( int retValue ) {
        //System.out.println( "ret " + retValue );
        drawLogo();
        backBuffer.setColor( Color.WHITE );

        String start = "Start Game";
        int charHeight = backBuffer.getFontMetrics().getHeight();
        int strLength1 = backBuffer.getFontMetrics().stringWidth( start );
        backBuffer.drawString( start, (WIDTH - strLength1) >> 1, 175);

        String keys = "Show keys";
        int strLength2 = backBuffer.getFontMetrics().stringWidth( keys );
        backBuffer.drawString( keys, (WIDTH - strLength2) >> 1, 195 );

        String space = "Press space to choose";
        int strLength3 = backBuffer.getFontMetrics().stringWidth( space );
        backBuffer.drawString( space, (WIDTH - strLength3) >> 1, 235 );

        if( retValue == 0 ){
            backBuffer.drawRoundRect( ( (WIDTH - strLength1) >> 1 ) - 5, 160, strLength1 + 10, charHeight + 6, 10, 10 );
        } else {
            backBuffer.drawRoundRect( ( (WIDTH - strLength2) >> 1 ) - 5, 180, strLength2 + 10, charHeight + 6, 10, 10 );
        }
    }
 as you can see in everyone of these methods i use an object backBuffer to draw strings on the screen.
So what type is this object?
The answer is simple it is an instance of  the class Graphics that as i mention in the privious tutorials is used to draw strings, shapes and images on the screen. Ok the decleration and initialization of the backBuffer are
private Graphics backBuffer; 
Image offscreenImage;

private void createSecondBuffer() {
        offscreenImage = this.createImage( WIDTH, HEIGHT );
        backBuffer = offscreenImage.getGraphics(); }
When you want to print something on the screen you never print directly on the Graphics object that is passed as parameter on the paint method. If you do it you will get the flickerring effect.
 What is this?
i will let wikipedia to explain

Flicker is visible fading between cycles displayed on video displays, especially the refresh interval on cathode ray tube (CRT) based computer screens. Flicker occurs on CRTs when they are driven at a low refresh rate, allowing the screen's phosphors to lose their excitation (afterglow) between sweeps of the electron gun.
If you want to see live what flicker is just write the above code without the use of backBuffer but rather than that print dirrectrly to applet's Graphics object. Flickering occurs when the screen refreshes the same time that the Graphics object gets updates. So to transpass this we make every update in a single step inside the print method
public void paint( Graphics g ) {
        g.drawImage( offscreenImage, 0, 0, this );
}
This method is known as Double buffering and you must allways use it if you want to make a playable game.

I will close this article with the fade out/in effect. If you compile the code that i just wrote you will see the logo of the game to fade in and out. This effect is implemented by this
backBuffer.setColor( Color.black );
backBuffer.fillRect( 0, 0, WIDTH, HEIGHT );
color += nextInc;
if ( color > 255 ) {
        color = 255;
        nextInc *= -1;
} else if ( color < 100 ) {
        color = 100;
        nextInc *= -1;
}
//color = 250;
Color c = new Color( color, color, color );
backBuffer.setColor( c );

//prints logo on the center
String logo = "Asteroids - the rebirth";
int strLength = backBuffer.getFontMetrics().stringWidth( logo );
Ιτ uses the attribute color to calculate the color that will be used to draw the logo at the next frame.  When the color become greater than 255 (is the upper value) it starts to decrease will it increase again whet it becomes less than 100. Finally the getFontMetrics().stringWidth( String param ) returns the width of a given string with the specific font on the screen.

Below is the complete code of the class AsterpodsApplet as it just updated
package games.applet.Asteroid;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author alexm
 */
public class AsterpodsApplet extends Applet implements Runnable, KeyListener {

    private static final int WIDTH = 640;
    private static final int HEIGHT = 480;
    private static final int GAME_LOOP_MAX_TIME = 50;
    private Thread mainLoop;
    private boolean gameOver = false;
    //keyboard switches
    private boolean up;
    private boolean down;
    private boolean left;
    private boolean right;
    private boolean space;
    private boolean esc;

    private boolean up_pressed;
    private boolean down_pressed;
    private boolean left_pressed;
    private boolean right_pressed;
    private boolean space_pressed;
    private boolean esc_pressed;
    //Back buffer components
    private Graphics backBuffer;
    Image offscreenImage;

    @Override
    public void init() {
        mainLoop = new Thread( this );
        up = false;
        down = false;
        left = false;
        right = false;
        space = false;
        esc = false;

        //create the back buffer
        createSecondBuffer();
        //enabling the key listening
        addKeyListener( this );
    }

    //creates a second buffer to eliminate the flickering
    private void createSecondBuffer() {
        offscreenImage = this.createImage( WIDTH, HEIGHT );
        backBuffer = offscreenImage.getGraphics();
    }

    @Override
    public void start() {
        mainLoop.start();
    }

    @Override
    public void stop() {
        gameOver = true;
    }

    @Override
    public void update( Graphics g ) {
        paint( g );
    }

    @Override
    public void paint( Graphics g ) {
        g.drawImage( offscreenImage, 0, 0, this );
    }

    public void run() {
        try {
            boolean exit = false;
            int menuOption = 0;
            while ( true ) {
                menuOption = getMenuOption();
                if ( menuOption == 0 ) {
                    gamePlayLoop();
                } else {
                    printKeys();
                }
            }
        } catch ( InterruptedException ex ) {
            Logger.getLogger( AsterpodsApplet.class.getName() ).log( Level.SEVERE, null, ex );
        }
    }

    //the loop that will run while the player is on game menu
    private int getMenuOption() throws InterruptedException {
        long lastTime;
        int retValue = 0;//this will contain the players choice
        while ( true ) {
            lastTime = System.currentTimeMillis();
            drawMenu( retValue );
            if ( this.up_pressed == true ) {
                retValue = 0;
            } else if( this.down_pressed == true ) {
                retValue = 1;
            } else if ( this.space_pressed ) { // if it is the space that pressed then the user made his choice
                consumeTyped();
                break;
            }
            consumeTyped();
            repaint(); //rapaints the applet
            lastTime = System.currentTimeMillis() - lastTime;
            Thread.sleep( GAME_LOOP_MAX_TIME - lastTime );
        }
        return retValue;
    }

    private void printKeys() throws InterruptedException {
        long lastTime;
        while ( true ) {
            lastTime = System.currentTimeMillis();
            drawKeys();
            if ( this.space_pressed ) { // if it is the space that pressed then the user made his choice
                consumeTyped();
                break;
            }
            repaint(); //rapaints the applet
            lastTime = System.currentTimeMillis() - lastTime;
            Thread.sleep( GAME_LOOP_MAX_TIME - lastTime );
        }
    }
    //this is used to make the fade effect
    private int color = 100;
    private int nextInc = 5;

    private void drawLogo() {
        backBuffer.setColor( Color.black );
        backBuffer.fillRect( 0, 0, WIDTH, HEIGHT );
        color += nextInc;
        if ( color > 255 ) {
            color = 255;
            nextInc *= -1;
        } else if ( color < 100 ) {
            color = 100;
            nextInc *= -1;
        }
        //color = 250;
        Color c = new Color( color, color, color );
        backBuffer.setColor( c );

        //prints logo on the center
        String logo = "Asteroids - the rebirth";
        int strLength = backBuffer.getFontMetrics().stringWidth( logo );

        //the >> 1 is equals to /2
        backBuffer.drawString( logo, (WIDTH - strLength) >> 1, 140 );
    }

    //prints the key controls for the game
    private void drawKeys() {
        drawLogo();
        backBuffer.setColor( Color.WHITE );

        String[] lines = new String[ 6 ];
        lines[ 0] = "Press the Up arrow to accelerate";
        lines[ 1] = "Press the Down arrow to decelerate";
        lines[ 2] = "Press the Left arrow to turn left";
        lines[ 3] = "Press the Right arrow to turn right";
        lines[ 4] = "Press the Space to fire";
        lines[ 5] = "Press the Escape to exit";

        for ( int i = 0; i < lines.length; i++ ) {
            int strLength = backBuffer.getFontMetrics().stringWidth( lines[i] );
            backBuffer.drawString( lines[i], (WIDTH - strLength) >> 1, 170 + i * 15 );
        }

        String exitMessage = "Press space to go back";
        int strLength = backBuffer.getFontMetrics().stringWidth( exitMessage );
        backBuffer.drawString( exitMessage, (WIDTH - strLength) >> 1, 170 + lines.length * 15 + 30 );
    }

    private void drawMenu( int retValue ) {
        //System.out.println( "ret " + retValue );
        drawLogo();
        backBuffer.setColor( Color.WHITE );

        String start = "Start Game";
        int charHeight = backBuffer.getFontMetrics().getHeight();
        int strLength1 = backBuffer.getFontMetrics().stringWidth( start );
        backBuffer.drawString( start, (WIDTH - strLength1) >> 1, 175);

        String keys = "Show keys";
        int strLength2 = backBuffer.getFontMetrics().stringWidth( keys );
        backBuffer.drawString( keys, (WIDTH - strLength2) >> 1, 195 );

        String space = "Press space to choose";
        int strLength3 = backBuffer.getFontMetrics().stringWidth( space );
        backBuffer.drawString( space, (WIDTH - strLength3) >> 1, 235 );

        if( retValue == 0 ){
            backBuffer.drawRoundRect( ( (WIDTH - strLength1) >> 1 ) - 5, 160, strLength1 + 10, charHeight + 6, 10, 10 );
        } else {
            backBuffer.drawRoundRect( ( (WIDTH - strLength2) >> 1 ) - 5, 180, strLength2 + 10, charHeight + 6, 10, 10 );
        }
    }
    //the loop that will run while the player fights the asteroids

    private void gamePlayLoop() throws InterruptedException {
        long lastTime;
        while ( gameOver == false ) {
            lastTime = System.currentTimeMillis();
            checkUserInput();
            runAI();
            updateUser();
            updateEnemies();
            repaint(); //rapaints the applet
            lastTime = System.currentTimeMillis() - lastTime;
            Thread.sleep( GAME_LOOP_MAX_TIME - lastTime );
        }
    }

    private void checkUserInput() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void runAI() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void updateUser() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void updateEnemies() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void consumeTyped() {
        up_pressed = false;
        down_pressed = false;
        left_pressed = false;
        right_pressed = false;
        space_pressed = false;
        esc_pressed = false;
    }

    public void keyTyped( KeyEvent e ) {       
    }

    public void keyPressed( KeyEvent e ) {
        System.out.println( "key pressed " + e.getKeyCode() );
        if ( e.getKeyCode() == KeyEvent.VK_UP ) {
            up = true;
            up_pressed = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
            down = true;
            down_pressed = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
            left = true;
            left_pressed = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
            right = true;
            right_pressed = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_SPACE ) {
            space = true;
            space_pressed = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
            esc = true;
            esc_pressed = true;
        }
    }

    public void keyReleased( KeyEvent e ) {
        System.out.println( "key releashed " + e.getKeyCode() );
        if ( e.getKeyCode() == KeyEvent.VK_UP ) {
            up = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
            down = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
            left = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
            right = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_SPACE ) {
            space = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
            esc = false;
        }
    }
}

Have a nice coding time

Wednesday, September 16, 2009

Programming java applet games part 3 - Game loop

ma
Lets go to the next step by demonstrating the basic structure of every game, The game loop witch from a programming standpoint is the main component of any game that you have ever played. The game loop is the routine that manages control every game event, user input, graphics and sound output to produce the software that we all like. Most of the software that we use does not produce any output unless the user give a command. For example an image editor will not print not even a line unless you do not click on a canvas. Games does not follows this model, the show must go on even if the user will not press not even a key. The game loop handles to produce events and actions without user reaction. A typically game loop have to looks like
while( isGameOver() == false ) {
  checkUserInput();
  checkAI();
  updateGameAvatars();
  resolveAvatarCollisions();
  renderGraphics();
  playSounds();
} 
Every game is almost based on this idea, the difference between games is in the code that is needed for this methods. Off course today we have multitasking operating systems with multi-core CPUs and many games user threads to increase the performance by execute some of these methods in parallel. As you can imagine much of the code that must be run at AI is independent from the check for user input and we can implement them using threads. As CPUs technologies focus more and more to multiply the cores that exist on a CPU chipset the usage of parallel algorithms in computer programming will continue to be increased, These days even game consoles like PS 3 and XBOX 360 have multi-core CPUs.
Now lets see how we can implement the game loop in a Java applet.  Applets are event driven components. that means that by their nature needs user and browser input to interact. There are specific methods that are called by the browsers during the lifetime of an Applet.
init();  
This is where programmers put initialization code for the applets and they are called firts by the browser. You can see init as the default constructor of a class.
start();  
This method is called when starts the execution of an applet and after its initialization
stop(); 
Called by the browser or applet viewer to inform this applet that it should stop its execution. It is called when the Web page that contains this applet has been replaced by another page, and also just before the applet is to be destroyed. An applet should override this method if it has any operation that it wants to perform each time the Web page containing it is no longer visible.
destroy(); 
Called by the browser to inform this applet that it is being reclaimed and that it should destroy any resources that it has allocated. The stop method will always be called before destroy. It contains the clean up code of our applet.
update();
Is called when an area of the the applet screen needs to be redrawn 
paint();
Is called when you need to re-render the 100% of the applets area.  For extended reference of this methods you must read the javadocs

After all these, it is time to define our Applet

package games.applet.Asteroid;
import java.applet.Applet;
import java.awt.Graphics;
public class AsterpodsApplet extends Applet {
    public void init() {
    }
    public void start() {
    }
    public void stop() {
    }
    public void update( Graphics g ) {
        paint( g );
    }
    public void paint( Graphics g ) {
        // your code here;
    }
}

As i said Applets are event driven and so everything  are executed after  events (ie user just pressed a key, user moves the mouse etc ) and in a single thread. So to escape this fact and run  the game loop continuously we will create a separate thread.  It is not my goal to make a tutorials about threads so for now just accept the simple code that i post and you can read some thing aboyt java Threads at the javadocs

http://java.sun.com/j2se/1.3/docs/api/java/lang/Thread.html
 

For now just accept that every applet that needs to run a thread must implement the Runnable interface.  Ok this is not true but just accept it.  Runnable interface needs only one method to be implemented the

public void run();
This is where our main loop belongs
package games.applet.Asteroid;

import java.applet.Applet;
import java.awt.Graphics;

public class AsterpodsApplet extends Applet implements Runnable {

    private Thread mainLoop;
    private boolean gameOver = false;

    @Override
    public void init() {
        mainLoop = new Thread( this );
    }

    @Override
    public void start() {
        mainLoop.start();
    }

    @Override
    public void stop() {
        gameOver = true;
    }

    @Override
    public void update( Graphics g ) {
        paint( g );
    }

    @Override
    public void paint( Graphics g ) {
        // your code here;
    }

    public void run() {
        while ( gameOver == false ) {
            checkUserInput();
            runAI();
            updateUser();
            updateEnemies();
            repaint(); //rapaints the applet
            Thread.yield();
        }
    }

    private void checkUserInput() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void runAI() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void updateUser() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void updateEnemies() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }
}
So the start and stop methods start and stop the game loop. One important thing in game loop is to produce accurate timing. Every time that a game loop is executed consumes different CPU time. I will not explain why this happens but trust my it happens. To avoid the time hazards we need to  specify a variant time time window for the mainLoop thread to sleep to balance the time lose. There are two ways to consume time
a) Pause the game every time by calling currentThread.sleep(int)
b) Calling System.currentTimeMillis() to keep track of the time changes.
I will use both of them :-)
So lets say that we want to have maximum 20 frame per second, we need our game loop to last about 50 milliseconds. We can do this by calculating the time that a loop has consumed and then by sleeping 50 - loopTIme
So the run method changes to
 public void run() {
        long lastTime; 
        long lostTime;
        while ( gameOver == false ) {
            try {
                lastTime = System.currentTimeMillis();
                checkUserInput();
                runAI();
                updateUser();
                updateEnemies();
                repaint(); //rapaints the applet
                lastTime = System.currentTimeMillis() - lastTime;
                Thread.sleep( GAME_LOOP_MAX_TIME - lastTime );
            } catch ( InterruptedException ex ) {
                Logger.getLogger( AsterpodsApplet.class.getName() ).log( Level.SEVERE, null, ex );
            }
        }
    }
The last words for today, i will try to explain how to get input from keyboard. One way to accept keyboard input is to implement the KeyListener interface and this can be done by implement the methods
public void keyTyped( KeyEvent e ) ;
is called  when a key is typed
public void keyPressed( KeyEvent e );
is called  when a key is pressed
public void keyReleased( KeyEvent e );
is called  when a key is released

These methods as you can see the parameter e is an instance of the class KeyEvent. This is a class that contains information about the key that is typed/pressed/released. As always there is extended information about this class at the javadoc
http://java.sun.com/j2se/1.3/docs/api/java/awt/event/KeyEvent.html

in our game we need to track the arrow keys, the space key and the z. So the code for keyPressed and keyReleased switch on and off boolean variables variables. So the applet finaly is 


package games.applet.Asteroid;

import java.applet.Applet;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author alexm
 */
public class AsterpodsApplet extends Applet implements Runnable, KeyListener {

    private static final int GAME_LOOP_MAX_TIME = 50;
    private Thread mainLoop;
    private boolean gameOver = false;
    private boolean up;
    private boolean down;
    private boolean left;
    private boolean right;
    private boolean space;
    private boolean z;

    @Override
    public void init() {
        mainLoop = new Thread( this );
        up = false;
        down = false;
        left = false;
        right = false;
        space = false;
        z = false;
    }

    @Override
    public void start() {
        mainLoop.start();
    }

    @Override
    public void stop() {
        gameOver = true;
    }

    @Override
    public void update( Graphics g ) {
        paint( g );
    }

    @Override
    public void paint( Graphics g ) {       
    }

    public void run() {
        long lastTime;
        long lostTime;
        while ( gameOver == false ) {
            try {
                lastTime = System.currentTimeMillis();
                checkUserInput();
                runAI();
                updateUser();
                updateEnemies();
                repaint(); //rapaints the applet
                lastTime = System.currentTimeMillis() - lastTime;
                Thread.sleep( GAME_LOOP_MAX_TIME - lastTime );
            } catch ( InterruptedException ex ) {
                Logger.getLogger( AsterpodsApplet.class.getName() ).log( Level.SEVERE, null, ex );
            }
        }
    }

    private void checkUserInput() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void runAI() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void updateUser() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    private void updateEnemies() {
        throw new UnsupportedOperationException( "Not yet implemented" );
    }

    public void keyTyped( KeyEvent e ) {
    }

    public void keyPressed( KeyEvent e ) {
        if ( e.getKeyCode() == KeyEvent.VK_UP ) {
            up = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
            down = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
            left = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
            right = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_SPACE ) {
            space = true;
        }
        if ( e.getKeyCode() == KeyEvent.VK_Z ) {
            z = true;
        }
    }

    public void keyReleased( KeyEvent e ) {
        if ( e.getKeyCode() == KeyEvent.VK_UP ) {
            up = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
            down = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
            left = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
            right = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_SPACE ) {
            space = false;
        }
        if ( e.getKeyCode() == KeyEvent.VK_Z ) {
            z = false;
        }
    }
}


So until next time, have a nice coding time my friends

Tuesday, September 15, 2009

Programming java applet games part 2 - Game design

Ok, as i said my goal is to create a simple java applet game. To be accurate i want to create a clone of the famous game Asteroids. If you have not played it take a look



In this step i will so you how to design the basic classes of a game and how to create figures with simple shapes using the java class Graphics

First of all you will need to specify our basic classes. For sure we will need a class for the player. Our player will have a number of lives, score, position on the screen, movement speed, direction and a state (alive, about to die, dead). In every different state there different things that must be drawn on the screen and different reactions to user input. If you dismiss the lives all there attributes belongs to the asteroids too. So we can encapsulate all these attributes into an abstract class, let's call it GameAvatar. The GameAvatar instances have specific functions
public abstract void getHit();
The getHit methods is used when a GameAvater gets a hit (a bullet, or a crash)

public abstract void update();
The update method is used when we need to update the state, position etc of a GameAvatar.
public abstract void paint( Graphics g );
The paint method whenwe have to draw the GameAvatar on the screen

So the complete source code of GameAvatar is

package games.logic.Asteroid;

import java.awt.Graphics;

/**
 *
 * @author alexm
 */
public abstract class GameAvatar {
    public static int ALIVE_STATE = 0;
    public static int DEAD_STATE = 1;
    public static int DYING_STATE = 2;

    //screen coordinates
    protected int xPos;
    protected int yPos;
    //the spachip direction
    protected int direction;
    //the spaship velocity
    protected int velocity;
    //the player state
    protected int state;

    public int getxPos() {
        return xPos;
    }

    public void setxPos( int xPos ) {
        this.xPos = xPos;
    }

    public int getyPos() {
        return yPos;
    }

    public void setyPos( int yPos ) {
        this.yPos = yPos;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection( int direction ) {
        this.direction = direction;
    }

    public int getVelocity() {
        return velocity;
    }

    public void setVelocity( int velocity ) {
        this.velocity = velocity;
    }
   
    public int getState() {
        return state;
    }

    public void setState( int state ){
        this.state = state;
    }

    public abstract void getHit();

    public abstract void update();

    public abstract void paint( Graphics g );

}
The Player must implement the abstract methods of GameAvatar and add new functionality. The extra method that we need is
public void fire() ;
This method is used to fire a bullet from the spaceship

So we have

package games.logic.Asteroid;

import java.awt.Graphics;

/**
 *
 * @author alexm
 */
public class Player extends GameAvatar{
    //number of lifes
    protected int lifes;
   
    public Player(){
        lifes = 3;
        xPos = yPos = direction = velocity = 0;
    }

    public int getLifes() {
        return lifes;
    }
   
    public void setLifes( int lifes ) {
        this.lifes = lifes;
    }

    public void fire() {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

    @Override
    public void update() {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

    @Override
    public void paint( Graphics g ) {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

    @Override
    public void getHit() {
        throw new UnsupportedOperationException( "Not supported yet." );
    }
       
}
And last but not least the initial source code for the Asteroid class

package games.logic.Asteroid;

import java.awt.Graphics;

/**
 *
 * @author alexm
 */
public class Asteroid extends GameAvatar{

    //the size of the asteroid
    int size;

    public Asteroid(){
        size = 3;
    }

    public Asteroid( int s ){
        size = s;
    }

    public void fire() {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

    @Override
    public void update() {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

    @Override
    public void paint( Graphics g ) {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

    @Override
    public void getHit() {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

}

The Asteroid class must have an attribute size to decide what will be happend whan an Asteroid will be hit.

So that is for today. We will see how we can use these classes to make a game and how to render some graphics in the following steps
When the tutorial will be finished i will upload all the source code and the game frodownload so keep in touch

Monday, September 14, 2009

Programming java applet games part 1 - Short intro to applets

Java applets is one of the best  ways to start teach yourself about game programming and they are related to my previous article about browser games.


We will start to discuss the basics about Java applets and step by step we will built a complete game. If you are an experienced programmer probably you will learn nothing new here but you can still read this in case you will find something interesting or to propose improvements.

To understand this tutorial i except that you are already a little bit familiar with the computer language  java but i need you to know only the basics (what is the jdk a class etc) and you have to know how to use a text editor like notepad or kate. Of course the best approach is to use an IDE (integrated development environment) such as Netbeans (god bless these developers).

OK its time to write the first snippets of code. Lets build the first applet

import java.applet.Applet;
import java.awt.Graphics;

public class HelloGamers extends Applet {
    public void paint (Graphics g ){
         g.drawString("Hello gamers", 10, 10 );
    }
}

The above code does not do anything special. It just prints a text on the screen of your web browser. Save the above code in a file named HelloGamers.java as java demands. In this example we use classes from the packages java.applet and java.awt to implement our simple applet. The class applet is the base that must be extended by every class that needs to be an applet.
So every  applet needs to be written like that

public class SomeApplet extends Applet

The class Graphics contains methods to drow objects (like Strings) on the web browser Screen

If you are not familiar with the term method do not panic, they are just the functions of a class. Some years ago in the Object oriented programming languages (like Java) we have stopped to use the term function and member variable and we say method and attributes. So lets proceed with the next important thing

  public void paint (Graphics g)

Every Applet has a paint method. This is the method that is called by the jvm to draw things on the screen. So every applet we create must override this method to display what our applet needs to display. Paint takes only one parameter, the Graphics object what can print with.


To print string Graphics has the method drawString so

  g.drawString("Hello gamers", 10, 10 );

prints a string on the coordinates 10, 10, Coordinates 0,0 point to the upper left corner of the area that is reserved for the applet.

So lets embed out applet into an html file to see it with the web browser.  Compile the java class with the compiler

javac HelloGamers.java

as you know the above command creates a .class file actually this command will create the HelloGamers.class. To embed the applet open a text editor and type the html snippet

Save the file with a name that you want and close the file

<HTML>
<BODY>
<APPLET CODE="HelloGamers.class" WIDTH=200 HEIGHT=200>
</BODY>
</HTML>

So that was the first step, now you can read the javadoc of class Graphics to see the functions that provide  and play with.
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Graphics.html

Have fun and wait for the next tutorial