Subscribe to Six's Game Programming Adventures        RSS Feed
-----

First Java game

Icon 10 Comments
I've been continuing my exploration of Java. I've come to find out I've got a bad C# accent. I guess it is due to the amount of time I've spent with C# over the past few years. I got core Java down quickly enough, being so similar to the other C-style languages and C# in particular. Now comes the daunting class of familiarizing myself with the huge amount of classes available. Swing in particular is jammed packed with all sorts of good stuff. Again, that is different from what I've gotten used to with C# but I'm figuring it out. This, however, isn't a Java vs C# discussion, it's about my first Java game. I'm not sure you can call it a game as there is no user interaction, just a circle bouncing around the screen.

From the reading I've done here on </dream.in.code> and around the net I see that most people use Timer and respond its event and overriding paint or paintComponent. Though I'm sure that is an excellent approach and that it works well it wasn't the way I wanted to attack game programming. I prefer the idea of a more traditional game loop. Here XNA has spoiled me as that is all done automatically for you. You just plug your logic into Update and your rendering into Draw. The approach I took was to use the Runnable interface and create a game loop in my run method. I saw others using J3DTimer to control timing, from Java 3D, but that has been deprecated and required a library outside of the JDK. After a little research I found that it had been replaced with System.nanoTime. I experimented a little and got something to work.

Let's get to the interesting part, some code. First step is to create a class that inherits from a JFrame to house your game. Then create a class that inherits from JPanel and implements the Runnable interface to add to the JFrame as a component. I'll create the JPanel first as it is required by the JFrame.

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

public class GamePanel extends JPanel implements Runnable {
    private boolean running;
    private Thread animator;
    private Image dbImage;
    private Graphics dbg;
    private long period;
    private int ballX, ballY;
    private int moveX, moveY;
    
    public GamePanel() {
        running = false;
        animator = null;        
        setBackground(Color.white);
        setFocusable(true);
        requestFocus();
        period = 1677777L;
        ballX = ballY = 0;
        moveX = moveY = 1;
    }
    
    @Override
    public void addNotify() {
        super.addNotify();
        startGame();
    }
    
    private void startGame() {
        if (animator == null || !running) {
            animator = new Thread(this);
            animator.start();
        }
    }
    
    @Override
    public void run() {
        long beforeTime, afterTime, timeDiff, sleepTime;
        
        running = true;
        
        beforeTime = System.nanoTime();
        
        while (running) {
            gameUpdate();       // Update game objects
            gameRender();       // Render game objects to back buffer
            paintscreen();      // Flip back buffer to window
        
            afterTime = System.nanoTime();
            timeDiff = afterTime - beforeTime;
            sleepTime = (period - timeDiff);
            
            if (sleepTime > 0L) {
                try {
                    Thread.sleep(sleepTime / 100000L);
                } catch(Exception ex) { }
            }
            
            beforeTime = System.nanoTime();
        }
        
        System.exit(0);
    }
    
    private void gameUpdate() {
        ballX += moveX;
        ballY += moveY;
        
        if (ballX < 0) {
            ballX = 0;
            moveX *= -1;
        }
        
        if (ballY < 0) {
            ballY = 0;
            moveY *= -1;
        }
        
        if (ballY >= JavaGame.GAME_HEIGHT - 25) {
            ballY = JavaGame.GAME_HEIGHT - 25;
            moveY *= -1;
        }
        
        if (ballX >= JavaGame.GAME_WIDTH - 25) {
            ballX = JavaGame.GAME_WIDTH - 25;
            moveX *= -1;
        }
    }
    
    private void gameRender() {
        if (dbImage == null || dbg == null) {
            dbImage = this.createImage(JavaGame.GAME_WIDTH, JavaGame.GAME_HEIGHT);
            dbg = dbImage.getGraphics();            
        }        
        
        dbg.setColor(Color.white);
        dbg.fillRect(0, 0, JavaGame.GAME_WIDTH, JavaGame.GAME_HEIGHT);
        
        dbg.setColor(Color.blue);
        dbg.fillOval(ballX, ballY, 25, 25);
    }
    
    private void paintscreen() {
        Graphics g = this.getGraphics();
        
        try {
            if (g != null && dbImage != null) {
                g.drawImage(dbImage, 0, 0, null);
                g.dispose();
            }
        } catch (Exception ex) {
            
        }
    }
}


Now, the JFrame class.

import javax.swing.*;

public class JavaGame extends JFrame {

    public static final int GAME_WIDTH = 800;
    public static final int GAME_HEIGHT = 600;
    private GamePanel panel;
    
    private JavaGame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(GAME_WIDTH + 5, GAME_HEIGHT + 25);
        setResizable(false);
        
        panel = new GamePanel();
        add(panel);
        
        setVisible(true);
    }

    public static void main(String[] args) {
        new JavaGame();
    }
}


I found some strange behavior though. When my ball reached what I thought was the bottom of my JFrame it was disappearing, the same with the right side. So I ended up adding a little padding. It seems more research is in order. I do have something that I can play around with a bit though and see what I can come up with.

10 Comments On This Entry

Page 1 of 1

stayscrisp Icon

28 December 2011 - 03:57 PM
Sweet, good to see you have something going :)

When checking for if your ball was reaching the boundary were you taking the balls radius into account. I see you are now, is that the padding you mention?
2

CasiOo Icon

28 December 2011 - 06:08 PM
Your GAME_WIDTH/HEIGHT is with the border on the JFrame. Instead you can use setPrefferedSize (on JPanel).

When using swing it is normal to use the swing timer as a game tick, and override the paintComponent(Graphics g) of JPanel. Remember that most parts of swing is not thread safe.

You could check the game Escape made by Notch (the creator of Minecraft). The sourcecode is publiched. He uses the JComponent class to draw his 3d as far as I remember, where he overrides the paintComponent method.
2

macosxnerd101 Icon

28 December 2011 - 10:59 PM
Even if you don't like Timer, you should still override the paintComponent() or paint() method to handle your drawing. The Java GUI is set up to pass the Graphics object as a parameter to those methods.

Also, since Swing isn't Thread-safe, you might look at using a SwingWorker for your game loop if you want to house it in the JFrame. If you look at the MVC pattern, you could use Threading in your controller and have the Thread invoke repaint() on the appropriate JComponent. As long as the Threading components aren't mixed directly with the GUI components, then you won't have to worry so much about the Swing and Thread-safety rule as you won't be operating on the event-dispatching Thread.

I'm glad to see you're enjoying Java so far for Game Programming! I'm looking forward to seeing more of your Java game development works! :)
2

k0b13r Icon

29 December 2011 - 03:27 AM
Hey, nice article, but you should carefully use Threads and Swing GUI.

Updating GUI from other thread than Event Dispatch Thread may lead you to some hard to find bugs in future, there's an article about this case:

http://java.sun.com/...s/threads1.html

(maybe I'm repeating macosxnerd101 here a bit ;)

Btw, I can't tell how you consider Swing a "packed with all sorts of good stuff" - for me, it's spawn of Satan :/
1

Raynes Icon

29 December 2011 - 04:52 AM
Swing is preloaded with a *lot* of useful components. The design of those components is another thing entirely.
1

SixOfEleven Icon

29 December 2011 - 05:30 AM
Thanks for the feedback guys. I agree with Raynes. There is a lot of useful stuff in Swing, using it is a different story all together.
0

SixOfEleven Icon

29 December 2011 - 05:54 AM

stayscrisp, on 28 December 2011 - 06:57 PM, said:

Sweet, good to see you have something going :)

When checking for if your ball was reaching the boundary were you taking the balls radius into account. I see you are now, is that the padding you mention?


The padding I mentioned was that the borders of the window seem to be included in the size so there is areas at the bottom and right where part of the ball would disappear. Adding pixels the width and height of the JFrame fixed the problem. I'm sure that my solution wouldn't be cross platform though. I tried using the setPreferedSize method on the JPanel but still had the padding. Will experiment and see if I can figure it out. I might just end up making it full screen. I'm going to look into using paint or paintComponent instead of the method I'm using for rendering. I got the method from Killer Game Programming in Java. Being an older book it might not take many things into account, such as the deprecated J3DTimer class.
0

cfoley Icon

29 December 2011 - 06:35 AM
You could always look into turning off the frame decorations or going fullscreen.
0

Programmist Icon

30 December 2011 - 10:49 AM
That's great Six. Have you heard of JavaFX? You might find it easier to develop GUIs and games using it instead of the old Swing and AWT classes. JavaFX now comes as a part of the latest Java 7 release (1.7_02). http://javafx.com/

And, if you ever decide to give Groovy a look, GroovyFX is (I think) a better, more concise way of developing JavaFX apps.
0

farrell2k Icon

30 December 2011 - 10:20 PM
The Event Dispatching Thread (EDT) knows nothing about your custom animation thread, so if the EDT trys to repaint your JFrame in response to an OS level or user level event while your custom animation thread is repainting your JPanel, you're probably going to have a deadlock on your hands. You really should either use a timer, or full-scree exclusive mode, if your platform supports it, so that you can just override paint() on the JFrame and do your rendering there.
1
Page 1 of 1

August 2014

S M T W T F S
     12
3456789
10111213141516
1718192021 22 23
24252627282930
31      

Recent Entries

Recent Comments

Search My Blog

0 user(s) viewing

0 Guests
0 member(s)
0 anonymous member(s)