Finalizing Seaman for GameMaker
<<Previous | ToC
| Next >>
Spoiler Alert!
Some people say you can only learn to program by programming. Others say
you can learn better by looking at other people's code. I think it takes
both. So if you want to learn Java best, you should try to do your own
version of Seaman before scrolling down to see my version on this page.
But the motivation is yours alone, you alone decide how into this you want
to get.
There are several little issues that need to be cleaned up before your
Seaman can run properly. We'll look at them now in case and while you are
contemplating whether to write before looking.
Input Widget
The GameEngine only sends keyboard input to widgets that are defined to
be able to accept that input, and that have keyboard focus at the time
of input. For Seaman we don't care about focus or differentiating who has
it, we just want to see the keystrokes. In GameEngine we accomplish this
purpose relatively painlessly. We still need some widget to be designated
as accepting keyboard input, so the GameEngine can give its reference to
KeyFld.
We'll let that be the game board, and we do it in GameMaker. Save and close
your Seaman class window, and right-click the GameMaker rectangle in the
BlueJ
dashboard to run its main(). It will re-open the same setup you
had when we left it. If not already selected, click on the game name in
the widget list, then check the Key checkbox, then hit Enter on the keyboard
twice, once to click OK (and accept your change)
and the second time to reBuild your Java template -- don't worry,
if you only entered code outside those "$" line regions, your new code
is safe. To be extra safe, before starting up GameMaker, make a copy of
your Seaman.java (or whatever you called it) in another folder. Bad Things
Happen, so "Save early, save often."
Then double-click your Seaman class rectangle again. You can go back
and forth like this as often as necessary as you think of (or remember)
things better doen in GameMaker or in Java. I don't think BlueJ
knows about GameMaker rewriting its java files, so I always close and re-open
them.
Back in the Startup() method, you need to tell the GameEngine
that the game board is the default recipient of keystrokes. The first line
of the method, before the method's first "$" line, is where it makes an
assignment to the class copy of the game board widget, which has a name
something like "mySeaman" (or whatever you called your game).
Add one more line there, either before that first "$" line or else after
the second,
DefaultKeyFocus(mySeaman); // or whatever your game
is called
Enter Input
We wrote all of our input event handling in the single predefined method
KeyFld,
but GameEngine only sends printable characters to KeyFld. In particular,
to see the enter key input in the same method, we need to forward it from
GotEnterKey
to KeyFld, like this:
public boolean GotEnterKey(GameWgt whom) {
KeyFld(whom,'\n');
return false;} //~GotEnterKey
Greetings
When I first ran my program, I had several programming errors. Three of
them prevented compiling, so obviously I had to fix them (no need to bore
you with the details). A couple more relate to the user interface. If you
go back and look at our English program
design, there's a line that tells the user what's going on. Maybe you noticed;
I forgot. So my program started up with nothing there. I added a couple
lines in the Startup method to correct that problem:
if (Hmsg != null)
zx = Hmsg.PutTextLn("Seaman: choose a word...");} //~StartUp
You probably gave your message widget a different name than I did. Use
the name you chose. Obviously, after the first player has chosen their
word, this message becomes obsolete. I added a couple more lines like it
within the section of code where we set playing=true
if (Hmsg != null)
zx = Hmsg.PutTextLn("Playing Seaman: guess a letter...");
Termination
The other problem occurred at the end. Maybe you were more clever than
I, but when my program got to the end and just stopped (exited the program)
-- which was fine, except the user didn't have any time to see any clever
message I displayed. I decided I need another state variable (if we had
a number instead of a boolean for playing, we could just assign
another value, and I usually do that). Because this is a state variable
(tells the program what "state" the program is in, that is what part of
the game is active, in this case, the game ended and we are merely hanging
on until the user has finished reading our G'bye message) it needs to be
defined out at the class level, next to playing. I called mine
"ended" because when it's true, it means the game ended. Initialized
false, and set to true when either the body parts are fully visible,
or else the word is fully guessed. The first thing into the KeyFld
method, before we do anything else,
if (ended) System.exit(0); // quit out of this program
That way any keystroke after the message goes up ends the program. In the
next
page we can think about restarting and playing again.
My KeyFld method
This is my code for the (predefined) method KeyFld. If your code
works, you can look at mine for entertainment. If some part of your program
fails, you can compare what you have to what I did to see what's different.
If you didn't try, you can look at my code for entertainment. But like
riding a bicycle, you learn by doing.
public void KeyFld(GameWgt whom, char info) {
boolean gotit = false;
int zx;
if (ended) System.exit(0); // quit out of this program
if (!playing) {
if (info < 'a') {
if (Hmsg != null)
zx = Hmsg.PutTextLn("Seaman: guess
a letter...");
playing = true;
sofar = nLetts;} //~if
else {
guessword[nLetts] = info;
dashes[nLetts].HideSho(true);
nLetts++;}} //~else //~if
else if (info >= 'a') if (info <= 'z') { // (if playing
is true)...
for (int nx=0; nx<nLetts; nx++) { // try
each letter
char letter = guessword[nx];
if (letter == info) { // do all
this..
gotit = true;
whom = dashes[nx];
if (whom != null) {
whom.HideSho(true);
zx = whom.PutTextLn(""+info);}
// PuTxLn needs String
sofar--;} //~if
} //~for
if (!gotit) {
wrong++;
switch (wrong) { // show the body
part for wrong..
case 1:
if (LefLeg
!= null) LefLeg.HideSho(true);
break;
case 2:
if (RitLeg
!= null) RitLeg.HideSho(true);
break;
case 3:
if (Spine
!= null) Spine.HideSho(true);
break;
case 4:
if (Shoulder
!= null) Shoulder.HideSho(true);
break;
case 5:
if (LefArm
!= null) LefArm.HideSho(true);
break;
case 6:
if (RitArm
!= null) RitArm.HideSho(true);
break;
case 7:
if (Head
!= null) Head.HideSho(true);
if (Face
!= null) Face.HideSho(true);
break;}
// this brace closes the switch
} //~if
whom = Hmsg;
if (whom == null) return;
if (sofar == 0)
zx = whom.PutTextLn("You got it!");
else if (wrong >= 7)
zx = whom.PutTextLn("Better luck
next time");
else return;
ended = true;} //~if (playing is true)
} //~KeyFld
In the next page we'll look at some graphical
extras and other improvements you might be interested in.
<<Previous | ToC
| Next >>
[2022 November 19]