Your Own Java Game

[Preliminary]

Contents

Download GameEngine3.zip
Part #0: Appropriate Tools
  Design First
  Getting Started with the Java GameEngine
Part #1: Laying Out Your First GameEngine Game
Part #2: GameEngine Classes
  Collisions in GameEngine
Part #3: Keyboard Input
Part #4: Text Display
Part #5: Your Own Game

Related Documents

Your Own Java Game -- Step by step tutorial to build a simple "Pong" game (You are here)
Converting Your Game to JavaScript -- What you need to know to run your game in a web browser
The Itty Bitty GameEngine -- Overview
Class GameWgt -- The visual components of a GameEngine game
Overriding GameEvent -- The programmatic components of a GameEngine game

Part #0: Appropriate Tools

Commercial games are written by large teams, a dozen or more creative people, over the course of a year or more. You will not write that kind of game in one week.

Many, perhaps most, games are written in layers, where the rules of play and the actual playing and board design happen at a very high level, then these are interpreted in a game engine that is designed to make that high-level design happen easily and naturally at game speed. The result is that the game designers, the ones doing rules of play and look&feel, they can spend their effort thinking about what they want the game to be like, not how to make the pixels on the screen work. Other programmers do that low-level programming. There are several commercial game engines on the market, which you can use to design your own game according to their (low-level) rules. If you want a different underlying engine, buy a different product, or make your own. But it's a lot of work. The point is, the creative game designers don't need to do that work.

This week you get to do your own high-level game design without worrying about the low-level pixels, but you still get to work in Java. I wrote a simplistic game engine you can use and it does the low-level pixel stuff. But the game engine is itself written in Java, and you can actually go in and change it. But I wouldn't recommend that for your first couple games. Get some experience, then you will know what you want to change and maybe even what you don't need to change. Work at the highest level possible, you will waste a lot less time re-inventing tools other people did better.

For a complete description of GameEngine and how to use it, see the GameEngine web page. This is an early version of GameEngine and some of the user interface doesn't work properly because I ran out of time, but usually there are work-arounds described in the reference page(s).

Previously (in Hello) you wrote Java programs that ran in a subroutine called "main()" because in the old days of C and Fortran, that was the magic name that the operating system knew to call to run your program. Today interactive software is smarter than that, and yes there is still a main() to start things up, but then everything happens by means of events, and you will write code that responds to particular events like mouse clicks, keys types, or the passage of time. OOPS lets the system spell out the names and parameters for the subroutines (methods) that respond to each event type, and all you need to do it fill in the Java code to do whatever needs doing.
 

Design First

The most important thing in building any game (or any software) is to decide what it needs to do and how it should do it. If you just take off in a random direction, you will get a random game that isn't fun to play.

To give you an idea of how to design your program using GameEngine, I will walk you through the steps for a very simple video game, which we will call "Pong" because it resembles an early video game by that name. I saw the original game when it came out, so I have this idea in my head, but it helps to draw a picture of what you think it should look like:

Basically it's a simplified ping-pong game, a rectangular game table with a movable paddle at each end that slides up or down under user control, and a ball that moves back and forth. If it hits the paddle, it reverses direction and goes the other way. If it misses the paddle, it runs off the end, then comes back on going the other direction, but the other player's score goes up. We can give the ball some "English" if it hits the edge of the paddle. I think the original game has joysticks or something to move the paddles, but we will play it using a couple keys on each end of the keyboard to move each paddle.
 

Getting Started with the Java GameEngine

Download and unzip (if necessary) the GameEngine file (folder) into your BlueJ folder. Then drag GameEngine folder onto the BlueJ program icon there. This opens up a new BlueJ project for your game. You should see three yellow boxes named "JavaGame", "GameWgt" and "GameEvent" which are the three public classes for GameMaker. You can click the Compile button, and then choose the "main()" from the popup menu when you right-click JavaGame. This opens up a GameMaker window that looks something like this:
Take a little time to become familiar with what you see here. On the left side of the window is a (green) game board, which you can resize or change the basic color to anything you like in the right panel using the color box and the size entry panels. This is a preliminary version, but eventually you will also be able to drag the corner to resize it. You should give your game a name in the Name field (or leave the default for now). This name becomes the Java class name of your game, so you must choose a name that is a valid Java class name. [Sorry, the text entry fields do not let you select the whole text for retyping, you need to use the backspace key; Real Soon Now.]

Also in the right panel are some widget icons that you can click to select, and/or drag onto your game board. Here are a couple full-size screen shots of the tool panel, on the left with the text tool selected, and on the right the ball tool. You can click on any tool and change (some of) its parameters for subsequent usage, or you can click on any widget already on the board to reposition it by dragging, or else to change its color, position or size (and in the case of a Text widget, also its font and text) in place using the entry fields in the tool panel.

On the first line below the "BYOG" (Build Your Own Game) title is the name of the selected tool (or the type of widget already on the game board, if you selected that (the same name as the tool that created it) and a colored box showing the basic color of whatever tool or widget is selected, and maybe an OK button to accept any changes you made. You can double-click the color swatch to bring up a dialog, and you can type into it (this is Java, right?) the hexadecimal code for the color. When you click OK in the input dialog, the color swatch will change to that color. Any time you make any changes to the tool panel, an OK button will appear, which you can use to accept the changes you made. (Sorry, no way to cancel or undo your changes yet, but you can save a copy of the text file "GameMaker.txt" from time to time, then revert to a previous copy.)

Every widget has a name, and if you choose a valid Java variable name, that becomes the name of the variable you can use to manipulate that widget in your program. Below that are four entry fields, the vertical and horizontal position of a widget on the game board, and its size (tall and wide).

Next are five checkboxes, which control what kinds of events this widget will respond to. When the game board is selected, there is one more checkbox, which if checked, it means you can call up alert messages that the user must dismiss to continue, and input dialogs like the one you use to change the color. If a sprite widget or tool is selected, that space is occupied by another pair of input fields which you can use to preset the speed (positive is down and/or right). The text widget puts up some radio buttons so you can choose one of four simple fonts. Not so obvious, you can 2-click the text itself and it opens up an input dialog box for you to enter a line of text. Changes you make to the tools will be reflected in all subsequent widgets you create from them, or you can change the created widgets one at a time.

GameEngine will remember what you have done in a text file "GameMaker.txt" but you can start over by quitting out of BlueJ, deleting or renaming the "GameMaker.txt" file, then restarting JavaGame. After you have designed the appearance of your game, you can click on the Build button and it will create (or update, if it already exists) the Java file "GameBd.java" (or whatever you named your game). All the rest of your game development will be in Java inside this class. You do not want to be changing the three existing classes, at least not this week.
 

Part #1: Laying Out Your First GameEngine Game

Start off with a clean game board -- if you have already added widgets or made other changes, quit out of BlueJ and delete or move the "GameMaker.txt" file, then restart JavaGame -- and give it a name like "Pong". We will do a simple 2-player game in black and white, somewhat like the original Atari Pong game. Double-click the color swatch and type in "0x0" (no quotes) for a black background color in the entry dialog. [Sorry, the entry dialog doesn't let you select the whole text for retyping, you need to use the backspace key; Real Soon Now.] Click OK or type the Enter key to dismiss the dialog, then again to apply the new color to the game board. If you want your game board to be some other size than the default, this would be a good time to set it. Be sure to click the OK button to accept your change, if any. The OK button is replaced by a Build button when there are no unaccepted changes. You can click the Build button any time you want, but it makes more sense to do it after you are satisfied with the appearance.

Now we add a ball sprite (the bottom in the tools stack). Drag it to the middle of the game board. The original Pong game was pure black and white, so for historical consistency we will (if necessary, click on the ball you just dropped on the game board to select it, then) 2-click the color swatch and type in 0xFFFFFF for a white ball. Notice that we are using Java hexadecimal constants for the color entry (see "Useful Tech Issues: Color by the Numbers"). This is good practice for you: the first pair "FF" (255) is the red component, the next is green, the third is blue, all max (255) values, which is white (try different numbers and see what colors you get). With the ball still selected, click in the name field and type in a Java-compliant name (no period!) so that you can access the ball from your code; I used "theBall" but you can name it anything you want. You also want to check the Bump checkbox so that your code will know when the ball runs into a paddle or off the edge. In the velocity entry line, you might give the ball a constant horizontal velocity of 2.0 (pixels per frame, which is 10 frames per second, for a net ball velocity of 20 pixels per second. I added a slight downward drift 0.1 pixels per frame. Click OK to accept your changes.

Speaking of running off the edges, the GameEngine collision detector only reports collisions involving widgets that are tagged collidable, so to catch when the ball runs off the edge you need to reselect the game board (you can click on its name in the widget list) then check Bump and click OK. A collision in this game engine is defined when the rectangles of any two widgets that are checked Bump overlap, except that at least one of them must be a sprite so it can move. If the game board itself is checked Bump the collision is defined to be when the sprite runs off any edge. In the next chapter we will see how to code Java to respond to collisions.

Now we need two paddles, for which we will use rectangle widgets. You can drop both rectangles, one at each end, then go back and set their respective properties, or you can do them one at a time. I made my paddles 50 pixels tall and eight pixels wide. The left paddle I placed at the middle of the board (subtract 50 from the board height, then divide that in half, for the vertical position), eight pixels from the left edge, and the right paddle similarly at a horizontal position 16 pixels less than the width of the board (after you add on the 8-pixel paddle width, the right edge of the paddle ends up eight pixels from the edge). You can make your paddles any color you like, but I made mine white for historical authenticity. Give them good Java names like "LeftP" and "RightP" or anything you like, and click the OK button each time to accept your design.

Then click the Build button to create a "Pong.java" file (or whatever name you gave your game) in the BlueJ project folder, then close the "Build Game" (GameMaker) window. Back in the BlueJ control panel window with the three yellow boxes, choose the "Edit->Add Class from File..." menu and select your new Pong.java file. This makes a new yellow rectangle, which you can double-click to edit the code. Each time you do a new Build, you should close and re-open the Pong.java (or whatever game name you are working on) tab/window so BlueJ knows you made changes.

You can also choose "Delete" from the popup menu when you right-click your new yellow box, but it both removes it from BlueJ, and deletes the text file, so if you want to keep it, make a copy somewhere else first. It's OK to leave it there after you change the name of your game (and get another yellow box), it will not be a part of any game you build that you did not open it specifically. I would try not to delete your game file in a file browser like Windows Explorer or OSX Finder, it might confuse BlueJ; use the BlueJ Delete menu to do it.
 

Part #2: GameEngine Classes

In Java, a class is a named collection of data and subroutines (called methods) to act on that data. It is useful for collecting into a single place all of the things you might want to do with some data that should be thought of together, and to tell the compiler what you want to do with that data so it can help you not to make mistakes when you are doing it. Classes are like subroutines, they are a way to gather some code and the data it works on into a box and put a name on it. Your game will be one of those boxes (yellow in the BlueJ dashboard), with everything you know about your game in that class.

In GameEngine we have two basic classes, one "GameWgt" for the visible elements (sometimes called widgets) of the game board (including the board itself), and a second "GameEvent" to tell GameEngine how the game plays. There are several different kinds of widgets, so we have several subclasses, each one defining a different kind of widget. Because in Java everything is a class, we also have a larger class "JavaGame" for the whole program. You mostly will not be creating more widget types, but using the existing classes and the subroutines already there to work with them. You will, however, define your own GameEvent subclass. In this example, it's named "Pong". You don't change GameEvent itself, you change only your own named subclass. It keeps your code together without messing with working (GameEngine) code.

The most important benefit you get from OOPS (Object-Oriented Programming Systems) is a type-safe way for a program -- in this case, the GameEngine -- to call subroutines in other programs that have not been written yet -- that would be your game. By defining a class (GameEvent) with all the subroutines the main program (JavaGame) wants to call, then when your game subclasses GameEvent, all those subroutines that you override now become accessible. In the Bad Old Days of C and assembly language, you could still do that kind of thing, but there was no protection from easily made mistakes, and the whole computer crashed. Now with OOPS the compiler tells you about your mistake, and you fix it, no harm done (nobody knows but you, if you care).

For a detailed description of all the methods you can rewrite ("override") in your GameEvent subclass see "Overriding GameEvent". For now I'll discuss only the changes you need to make for Pong to work. First you can look at the code GameMaker generated for you, in the window you just opened.

You will notice some lines with dollar signs "$" on them. The GameMaker Build button looks for those lines when it inserts the generated code from your widget placement, so DO NOT MODIFY ANY LINE WITH "$" ON IT, or the next time you click Build, it will complain and ask you to delete your Java file. If you change the generated code between matching "$" tags, your changes will be lost the next time you click Build. Computers are stupid, and I didn't have time to make it smarter. You can be smart for both of us: Be careful with the generated code file.

Each of the widgets you gave a Java-compatible name to, there is a variable of type GameWgt at the front with that name, which is initially set to be a reference to an instance of the GameWgt class, populated with the data for that widget type. The game board itself is also a widget, but its name is used for the class name, so the reference variable has a prefix "my" as in "myPong". There is a generated line of Java code in the Startup() method to initialize each such variable. And there is a large text string in the GameList() method which is delivered to the game engine to tell it what widgets to create and where to put them. This is all described in the "Overriding GameEvent" page. If you do nothing else but compile this class and run it, it will put up your game board in a window, with the ball moving off to the right until it runs off the screen, because there is no code to tell GameEngine what to do when that happens, or what happens when it hits a paddle. Let's do that now.
 

Collisions in GameEngine

Scroll down to the Collided method. Notice that all three of its lines of Java code (after the JavaDoc block quote at the front) are commented out. You will uncomment them (remove the "//" from the front of each line). If this is the first time you opened this file, and the three lines are not commented out, you probably have the wrong file. Make sure it has your own game name on the tab at the top of the window.

The Collided method takes two parameters, both of them widgets. The first one "whom" is the sprite widget (the ball, it's moving) and the second one is what it bumped into (one of the paddles), or else null (no widget) if the ball ran off the edge. In both cases we will consider only the first thing the ball bumps into, so delete the middle line (of the three).

If the ball ran off the top or bottom edge, we want to reverse the vertical velocity for a ricochet effect; if it ran off the left or right edge, we want to reverse the horizontal velocity. First decide if this is a game board edge collision, but we need a local integer variable to put the ball position into, and another for the velocity, and a third to hold the window size:

public boolean Collided(GameWgt whom, GameWgt hitt) {
  int posn, velo, scrn = JavaGame.GameWinSize();
  if (whom==null) return false; // probably can't happen, but test anyway
  velo = whom.GetInfo();
  posn = whom.GetPosn();
  if (hitt==null) {

    }
return true;} //~Collided

The widget methods are described in the "Using Widgets" page. GetPosn() returns a single ("packed") integer with two 16-bit integer parts, the upper 16 is a (signed) vertical position, measured down from the top, and the lower 16 is the (signed) horizontal position, measured off the left edge of the game board. Vertical will go negative when the ball goes off the top, or greater than the board height (less the ball size, see "Useful Tech Issues: Edge Testing") when it goes off the bottom. Similarly, the horizontal position goes negative off the left, and greater than the board width (less the ball size) off the right. Negative is easy to test, let's do that first. You can extract the vertical position from the composite by shifting it right 16 bits (posn>>16), but for a negative test (see "Useful Tech Issues: Packed Numbers") that is unnecessary:
  int posn, velo, scrn = JavaGame.GameWinSize();
  if (whom==null) return false; // probably can't happen, but test anyway
  velo = whom.GetInfo();
  posn = whom.GetPosn();
  scrn = scrn - whom.GetSize();
  if (hitt==null) {
    if (posn<0) // off the top..

    }
} //~Collided

The velocity is stored as a packed integer accessed by GetInfo() and updated by SetInfo but we need to make sure a pair of consecutive off-edge collision reports don't confuse us, because it might take longer than one frame to get the ball back on screen. You can tear the 32-bit integer apart to make one half of it negative, then put it back together, or if you don't mind a 1-unit (in this case, less than 1%) error in the velocity (see "Useful Tech Issues: Imprecise Calculation"), you can do a "one's complement" much more simply by complementing the desired bits. The constant "-0x10000" is actually 0xFFFF0000 in a 32-bit architecture, but by doing it as a negative, it also works correctly in a 64-bit architecture:
  int posn, velo, scrn = JavaGame.GameWinSize();
  if (whom==null) return false; // probably can't happen, but test anyway
  velo = whom.GetInfo();
  posn = whom.GetPosn();
  scrn = scrn - whom.GetSize();
  if (hitt==null) {
    if (posn<0) // off the top..
      if (velo<0) velo = velo^-0x10000;

    }
  whom.SetInfo(velo); // put it back into the widget
} //~Collided

Now we do the same for the bottom, comparing the position to the window size:
  int posn, velo, scrn = JavaGame.GameWinSize();
  if (whom==null) return false; // probably can't happen, but test anyway
  velo = whom.GetInfo();
  posn = whom.GetPosn();
  scrn = scrn - whom.GetSize();
  if (hitt==null) {
    if (posn<0) // off the top..
      if (velo<0) velo = velo^-0x10000;
    if (posn >= scrn) // off the bottom..
      if (velo>0) velo = velo^-0x10000;

    }
  whom.SetInfo(velo); // put it back into the widget
} //~Collided


We now extract the low 16 bits of each number to test for going off the left or right edge. I started to do the same thing for our velocity value velo, but later I realized that we need to put both pieces back. Anyway, all we need it for is to see if it's negative, which is a simple 1-bit test, with the result saved in a new boolean variable mvlf because we eventually look at it four times:

  int posn, velo, scrn = JavaGame.GameWinSize();
  boolean mvlf;
  if (whom==null) return false; // probably can't happen, but test anyway
  velo = whom.GetInfo();
  posn = whom.GetPosn();
  scrn = scrn - whom.GetSize();
  mvlf = ((velo&0x8000) !=0); // true if ball is moving left
  if (hitt==null) {
    if (posn<0) // off the top..
      if (velo<0) velo = velo^-0x10000;
    if (posn >= scrn) // off the bottom..
      if (velo>0) velo = velo^-0x10000;
    scrn = scrn&0xFFFF; // now look at low 16 only..
    posn = JavaGame.SignExtend(posn);

    }
  whom.SetInfo(velo); // put it back into the widget
} //~Collided


The position can be negative, so it must be extracted by sign-extension (SignExtend), but the window size is always positive and the (slightly faster) truncation (&0xFFFF) is good enough. Now we make the same comparisons on the horizontal components:

public boolean Collided(GameWgt whom, GameWgt hitt) {
  int posn, velo, scrn = JavaGame.GameWinSize();
  boolean mvlf;
  if (whom==null) return false; // probably can't happen, but test anyway
  velo = whom.GetInfo();
  posn = whom.GetPosn();
  scrn = scrn - whom.GetSize();
  mvlf = ((velo&0x8000) !=0); // true if ball is moving left
  if (hitt==null) {
    if (posn<0) // off the top..
      if (velo<0) velo = velo^-0x10000;
    if (posn >= scrn) // off the bottom..
      if (velo>0) velo = velo^-0x10000;
    scrn = scrn&0xFFFF; // now look at low 16 only..
    posn = JavaGame.SignExtend(posn);
    if (posn<0) // off the left..
      if (mvlf) velo = velo^0xFFFF;
    if (posn >= scrn) // off the right..
      if (!mvlf) velo = velo^0xFFFF;}
  whom.SetInfo(velo); // put it back into the widget
return true;} //~Collided


This will now do something credible if you compile and run it: the ball will bounce around off the edges of the window, but ignoring the paddles. Let's add collision detection for the paddles:

public boolean Collided(GameWgt whom, GameWgt hitt) {
  int posn, velo, scrn = JavaGame.GameWinSize();
  boolean mvlf;
  if (whom==null) return false; // probably can't happen, but test anyway
  velo = whom.GetInfo();
  posn = whom.GetPosn();
  scrn = scrn - whom.GetSize();
  mvlf = ((velo&0x8000) !=0); // true if ball is moving left
  if (hitt==null) {
    if (posn<0) // off the top..
      if (velo<0) velo = velo^-0x10000;
    if (posn >= scrn) // off the bottom..
      if (velo>0) velo = velo^-0x10000;
    scrn = scrn&0xFFFF; // now look at low 16 only..
    posn = JavaGame.SignExtend(posn);
    if (posn<0) // off the left..
      if (mvlf) velo = velo^0xFFFF;
    if (posn >= scrn) // off the right..
      if (!mvlf) velo = velo^0xFFFF;}
  else if (hitt==LeftP) { // the ball hit the left paddle..
    if (mvlf) velo = velo^0xFFFF;}
  else if (hitt==RightP) { // the ball hit the right paddle..
    if (!mvlf) velo = velo^0xFFFF;}
  whom.SetInfo(velo); // put it back into the widget
return true;} //~Collided

Part #3: Keyboard Input

We need some way to move the paddles up and down. I chose a couple of keys on each end of the keyboard, 'A' and 'Z' on the left and 'K' and 'M' on the right. 'A' and 'K' move their respective paddles up maybe 5 pixels, 'Z' and 'M' similarly move them down. We need a widget to take the keyboard focus, so I picked the ball because we already have a reference to it. Near the end of the StartUp method (after the generated lines, because we need them to execute first) we insert a single line:
    /// [..$] End generated StartUp code [$] (do not modify this line)
      DefaultKeyFocus(theBall);
    } //~StartUp
Be careful not to change the (existing) line with "$"s on it.

The ball needs to be able to accept keystrokes, so save and close the Pong.java window and restart the GameMaker window, then select the ball, and check the Key checkbox, then OK and Build. When you re-open the Pong.java window your changes are still there, but one of the numbers in the GameList() method is different.

To capture keystrokes in your Java program you need to override some kind of keyboard listener. This happens inside the GameEngine, and those keystrokes are forwarded to the GameEvent class, which you have overridden, so all you need to do is provide code for its KeyFld method (or the others, if you are expecting enter/escape/tab or arrow keys). We replace the one-line stub with code to accept one of our four move-paddle keys:
public void KeyFld(GameWgt whom, char info) {
  if (info == 'A') MovePaddle(LeftP,-5);
  else if (info == 'Z') MovePaddle(LeftP,5);
  else if (info == 'K') MovePaddle(RightP,-5);
  else if (info == 'M') MovePaddle(RightP,5);} //~KeyFld
We don't really care which widget had focus when the character arrived (it was the ball, right?) and we don't care about any of the other possible keys that might be typed. Later you might consider recognizing escape to start a new game (you would override the GotEscKey method for that). Or you can just close the window and start over.

We still need a MovePaddle subroutine (method) to actually move the paddle. We could put the same code in all four places, but that's too much work. Not all programming languages require it, but it's a good habit to put the definition of your variables and subroutines before they are used. This subroutine takes a widget and a number of pixels (negative for up, positive for down) to move it. The paddles are just static rectangles, so there's no velocity; each time you hit the key the paddle moves five pixels in that direction. It's probably a good idea to check for going off-screen -- we did that with the ball, this is the same, except we test before moving too far. Something like this, just above the KeyFld method should do it:

public void MovePaddle(GameWgt whom, int howfar) {
  int posn, vnew, tall, scrn = JavaGame.GameWinSize()>>16;
  if (whom==null) return; // always a good idea to validate your data
  posn = whom.GetPosn();
  tall = whom.GetSize()>>16; // we don't need to know how wide it is here
  vnew = (posn>>16)+howfar; // this is the new position, if valid..
  if (vnew<0) return; // nope, off the top
  if (vnew+tall >= scrn) return; // nope, off the bottom
  whom.SetPosn(vnew,posn&0xFFFF);} // otherwise move the paddle to there


This game should now be playable. Try it. Oh wait, I wrote it to respond to capital letters only. You need to set the CapsLock key on the keyboard or it will not notice your keypresses. Or you can add code so that it recognizes either caps or lower case. When I did this, the ball seemed to move too slow. Do you know how to make it go faster? I decided that 8 pixels per frame is much more reasonable.
 

Part #4: Text Display

There's nothing here for scoring, let's add that. First we think about what it must do (design). If the ball goes off the right edge of the screen, the left player scores, and similarly if it goes off the left, the right player scores. We need a couple text widgets to display the score, and a couple integer variables to hold the current score. Note that the scores are numbers (integers) which we will be incrementing (adding +1), and the text widgets display text, not numbers. These are different data types, but Java (usually) converts readily from numbers to text. Going the other way is slightly harder, so we won't try. GameEngine has a parse function to do that, but we don't need to use it if we keep the scores as integer variables.

Back in GameMaker, we will add a couple text widgets to the board. If you just drag the black text over to the black game board, it will disappear. You can click on its name in the widget list, and the "marching ants" box will show where it is, but there's an easier way to do this. Click on the Text tool to select it, then change the color to white. Double-click the text on the tool and you can change the default "Text" to a zero. [Sorry, the entry dialog doesn't let you select the whole text for retyping, you need to use the backspace key; Real Soon Now.] For a score panel we'd like the numbers to be bigger, so also click the Large radio button and then OK. Now when you drag it to the game board, it's already the right color and size. You  still need to give it a Java-compatible name so your code can access the score. I chose "Lscore" and "Rscore" but you are not limited to my choices. Build it again.

Reopen Pong.java so we can add code to manage the score. First we need two integer variables declared near the top of the Pong class. They need to be different from the widget names, so I chose "LeftSco" and "RightSco" and initialized them to zero, like this:

  /// (..$) End generated class variables ($)
    int LeftSco = 0, RightSco = 0;
Every time the ball runs off the right end of the game board, the left score is incremented (Lscore++;) and similarly when it runs off the left end. Do you know where to do that? Right, the same place where we changed the direction of the ball. Don't forget to wrap braces around the two statements (which makes a single statement out of many) because the if-command only controls one statement:
    if (posn<0) // off the left..
      if (velo<0) {
        velo = velo^0xFFFF;
        RightSco++;}
    if (posn >= scrn) // off the right..
      if (velo>0) {
        velo = velo^0xFFFF;
        LeftSco++;}}
That counts the score, but it doesn't update the display. To do that you must set the text in the score widgets. First convert the number to text (see "Useful Tech Issues: Data Type Conversion") then set the text of the score widgets, after confirming that the reference is not null:
    if (posn<0) // off the left..
      if (velo<0) {
        velo = velo^0xFFFF;
        RightSco++;
        if (Rscore != null)
          Rscore.PutTextLn(JavaGame.CvInt2Str(RightSco));}
    if (posn >= scrn) // off the right..
      if (velo>0) {
        velo = velo^0xFFFF;
        LeftSco++;
        if (Lscore != null)
          Lscore.PutTextLn(JavaGame.CvInt2Str(LeftSco));}}
That might work in C (and unfortunately also in Java, where the compiler could but didn't help you to remember there's a result you are discarding, see "Useful Tech Issues: Data Type Conversion"). I always insist on using that result, even if it is only to put it into an otherwise unused variable, I called it "tmp" here:
public boolean Collided(GameWgt whom, GameWgt hitt) {
  int tmp, posn, velo, scrn = JavaGame.GameWinSize();
...
        if (Rscore != null)
          tmp = Rscore.PutTextLn(JavaGame.CvInt2Str(RightSco));}
...
        if (Lscore != null)
          tmp = Lscore.PutTextLn(JavaGame.CvInt2Str(LeftSco));}}
...


Finished! Well, you could add some "English" to the ball if it hits the corner of the paddle. Think of the "corner" as anywhere within about 8 pixels (you already have the ball size, use that) from the end -- if you make the region too narrow, the game will be unplayable: I know, because I made that mistake when I wrote my Tennis game 40 years ago (see "Tennis"). Basically you want to add some vertical velocity, down (increased) if it hits the lower end of the paddle, up (negative) if it hits the upper end. I'll leave that as an exercise. Read up on the sprite velocity in the "Using Widgets" page to get the encoding right.
 

Part #5: Your Own Game

After you understand Pong, you are ready to start on something a little harder. TicTacToe involves some heuristics and strategy for the computer to figure out how to play O against a human playing X, with a substantial emphasis on helping you to think it through yourself. That's what this is all about, isn't it? You being able to control the computer all by yourself. We started out with a harder version of TTT earlier in the time frame. That was a mistake. We try harder. Go to Tic-Tac-Toe.

After you have mastered TTT, you might be ready to define your own game. First and most important, think about what you want it to look like, and more important what it will do. Keep it simple, and talk your idea over with the instructor(s). They will know if it's too hard to finish in a week. We are working on getting some resources up to help you do this by the time you are ready to start.

It's all up to you...

You might start by reading the GameEngine web page for a better understanding of how it works.
 

JavaScript

After your game is running in BlueJ, you can click the "->JS" button that pops up after you Build, (if that button doesn't pop up, this features is not yet working; it should be on the next release) and give it the name and password you set up for Chomp (see "Remembering What You Told the Computer to Do") and it will upload the game to the internet where you can play it on any web browser. For more details see "Converting Your Game to JavaScript".
 

Tom Pittman
Rev: 2020 July 29