<%@ EnableSessionState=true %> <% Option Explicit dim thisurl thisURL=Request.ServerVariables("PATH_INFO") & "?" & Request.ServerVariables("QUERY_STRING") const title="Java Games tutorial #3 - Creating a Stand-alone Graphics Application" %>
Current area: HOME -> Java Games tutorial #3 - Creating a Stand-alone Graphics Application

Java Games tutorial #3 - Creating a Stand-alone Graphics Application

Creating a Stand-alone Graphics Application

Last time, we promised we'd show you how to create a stand-alone graphics (or SAG) application from scratch. In this section, we're going to live up to that promise.

Overall, the process is much like the one we used to create the console app back in tutorial #2. However, there are a couple of new concepts and bits of code that will require some explaining.

Let's start off by creating a new, blank project in BlueJ. As usual, we're going to walk through the button-pushing and file-creating pretty quickly, then explain what we've been doing after we're finished.

Open BlueJ and select "New Project" from the "Project" menu. Enter something like "BattleshipSAG" when prompted for a project name.

Figure 1--Creating an SAG App from Scratch.

When the blank project browser appears, press the "New Class" button and enter "BattleshipApp" for the Class Name.

Figure 2--Creating a Blank BattleshipApp Class.

Your new class will appear in the Project browser as a tan box with gray hash marks. Double click this box and look at its code. Delete the extraneous bits until the file looks like this:

/**
* Write a description of class BattleshipApp here.

* @author (your name) 
* @version (a version number or a date)
*/

public class BattleshipApp
{
    // instance variables

    /**
    * Constructor for objects of class BattleshipApp
    */

    public BattleshipApp()
    {
    }
}

Just like we did with the console app, we need to add a static main method. This will act as the starting point for Java when we tell it to run the program. The SAG main looks exactly like it did in the console app:

/**
* Entry point for code execution.
*/
public static void main(String[] args) {
}

You'll probably want to change "(your name)" to your name, and "(a version number or date)" to a meaningful version number (like '0.1') or date. Also, you should write a description of the class where it says, "Write a description of class BattleshippApp here".

Hit the 'compile' button and make sure everything works OK up to this point. Once you've got the file compiling, we can start adding code to turn this into a valid SAG app.

But first, some theory.

Window to the World

Stand-alone graphics applications are windows programs--they live on the desktop, respond to mouse clicks, have close buttons and resize controls, etc. Because they have this extra functionality, we're going to have give Java more information about our program than we did with the Console app.

Roughly, here's a list of new data we'll have to supply:

  1. First, we have to tell Java that this is a windowed program.

  2. Second, we need to tell Java how big to make the window in which this program will run.

  3. Third, we need to add commands to pop our window up on the desktop.

When we've added these three elements to the BattleshipApp, we should be able to run it and have it appear on the screen.

Doing Windows

Satisfying the first requirement is pretty easy--we just have to say, "Hey, Java--put this program inside a window." To do that, just change the line

public class BattleshipApp

to

public class BattleshipApp extends JFrame

That's easy enough to write, but what does it mean?

To answer that, we need to touch upon a cornerstone of object oriented programming: inheritance. Just as 'inheritance' implies a relationship between parents and children in the real world, it suggests relationship between parent objects and child objects in an object-oriented programming language.

For example, in "real life", we might say "that girl inherited her father's eyes," meaning that her eyes look much like those of her father. In Java, we might say "BattleshipApp inherits its window behavior from the JFrame class," meaning that the BattleshipApp acts like a JFrame as far as its existence on the desktop is concerned.

Great. So what's a JFrame?

Simply put, it's a predefined class that comes with Java and has basic window functionality--it has a title bar, a close button, resize controls, the works.

So, when we say that BattleshipApp inherits from JFrame, what we really mean is that our game will have a title bar, close button, resize controls, etc.

We get all of these nifty features just by adding two little words to our class definition: extends JFrame. If it helps, you can mentally read 'extends' as 'inherits from'. Then our class definition would read:

public class BattleshipApp inherits from JFrame

and it couldn't be much clearer than that.

Make the change and recompile BattleshipApp. You should get an error. Don't worry, it's a small one.

Figure 4--Problems Compiling BattleshipApp.

The highlighted line tells us where the error occurred, and the status window at the bottom tells us what went wrong. Apparently, the compiler doesn't know what to make of the word JFrame.

This isn't a big problem. As we mentioned above, the JFrame object is a part of Java. More precisely, many classes came pre-packaged with the JDK version 2 that we installed in tutorial #1. All we have to do is tell the compiler in which package it will find the JFrame.

That's easy enough to do. The package is called "Swing". Swing contains a bunch of different objects that make creating Graphics User Interfaces (GUIs) very easy. To make all of these objects available to our program, we just have to include the following line at the top of the file (above the first curly brace):

import javax.swing.*;

Note that the '*' tells Java to import all the objects in the "Swing" package. We really only need the JFrame, so we could've written

import javax.swing.JFrame;

instead. However, we're going to need more Swing elements later, so it's easier just to include them all in one go.

Unfortunately, we can't stop there. Swing depends on objects found in another package: AWT. AWT stands for "abstract window toolkit", and forms the basis for most of Java's graphics and user input code.

To include the objects in the AWT package, we need to add the line:

import java.awt.*;

to the top of our class file.

After you add these lines, your file should look something like this:

/**
* The basic framework for a battleship game.

* @author Mark Kreitler

* @version 0.1
*/

 

import java.awt.*;

import javax.swing.*;


public class BattleshipApp extends JFrame
{
    // instance variables


    /**
    * Entry point for code execution.
    */
   
public static void main(String[] args) {
    }


    /**
    * Constructor for objects of class BattleshipApp
    */

    public BattleshipApp()
    {
    }
}

Now try compiling again. This time, everything should work out fine.

Size Matters

Now that we've told Java that we're a windowed program, we need to tell it how big we want to be when we show up on the desktop. In principle, this is easy to do--we just need to put the line

this.setSize(600, 500);

somewhere inside our object. Doing so will make our window 600 pixels wide and 500 pixels tall. No problem.

Except...where do we put this line?

Well, we want it to execute when Java creates our program window. Where does that happen?

That's a trick question. The answer is, "right now, that doesn't happen." In fact, we have to explicitly tell Java to create a new BattleshipApp object. If we don't do that, there will never be a window to receive the 'setSize' command.

Don't believe me? Try running the program (right click on the 'BattleshipApp' box in the project browser and select 'main(args[])' in the drop-down box). No window appears, because we never created one in code.

So, first things first, how do we create a new instance of BattleshipApp, and where do we put the code?

Recall that Java begins executing our code with the main method. You might guess that we'd want to create our window there. As usual, you'd be right.

The required code is very simple. Just add this line to the main method:

new BattleshipApp();

That tells Java to create an instance of class BattleshipApp.

Now all we have to do is set its size. You might be tempted to do it immediately after the 'new Battleship()' command. That's logical--but there's a better place. It's called the constructor, and we're going to spend a few minutes talking about this most special and important method.

Under Construction

You've probably noticed that, when BlueJ generates a class skeleton for us, it always includes a method whose name matches the class name. For instance, in BattleshipApp, there's a public method called BattleshipApp().

Coincidence? Nope.

Java recognizes methods whose names match the class name as constructors. These methods have a very special property--whenever you create an object using the new command, Java calls the constructor immediately after creating the object.

This is very handy. You can place code in the constructor that sets up the object for use (we usually call this initializing the object).

As an example, consider a simple 'Circle' class:

public class Circle {

    public float radius;

    public float circumference;

 

    // Circle constructor.

    public Circle(float r) {

        radius = r;

        circumference = 2 * 3.14159 * r;

    }

}

As you can see, the Circle has two instance variables--radius and circumference. We can create an instance of Circle as follows:

Circle c1 = new Circle(3);

When Java reaches this line, it immediately executes the code in the Circle's constructor, passing in a value of 3 for 'r'.

public Circle(float r) {

    radius = r;

    circumference = 2 * 3.14159 * r;

}

So, if we checked the value of c1.radius, it would be 3, and c1.circumference would equal 6 times pi, or about 18.9.

Especially alert readers probably noticed one other quirk of constructors--unlike other methods, they have no return type. In other words, most method definitions must follow the form

type methodName(type arg1, type arg2, ...) {

    // Method body goes here.

}

 

where type tells Java what kind of data the method produces (int, float, etc). Constructors have the form

className(type arg1, type arg2, ...) {

    // Method body goes here.

}

This isn't a problem for Java, because it knows that a constructor always produces a new object of the type associated with the class definition.

On the Road Again

Back to our problem. If we put the setSize command in BattleshipApp's constructor, Java will execute it when we create the new instance of BattleshipApp. That's the behavior we want, so let's give it a try:

/**
* Constructor for objects of class BattleshipApp
*/

public BattleshipApp()
{

    this.setSize(600, 500);
}

Here, the keyword this is optional. We use it to show that we're calling setSize on 'this' instance of BattleshipApp. You can leave it out if you like.

Compile and run the code. If you get compiler errors and you absolutely can't fix them, check your code against this working copy.

When you run this version, still nothing happens, even though we created an instance of  BattleshipApp. The problem is that making the object doesn't let us see it. We have to explicitly tell Java to show us the window.

Show Me the Window

Fortunately, that's very easy. Just add the following line to BattleshipApp's constructor:

this.show();

(immediately following the setSize line).

Compile and run this code.

Finally, a window!

Now that we've got a basic framework in place, we can start laying out the rest of our project.

This is where the fun begins...