This page continues the construction of a 4-function calculator in GameEngine as part of a course on programming in Java (which starts here). If you have not yet worked through the first page of this construction, you might want to go back and do that.
-- or use the "Ask" feature in Zoom.After you have built your calculator in GameMaker and clicked the Build button, quit out of GameMaker (close its window) and in the BlueJ dashboard window, right-click the background behind the yellow boxes, which pops up a menu: choose "Add Class from File" and browse over to your (new) ".java" (in my case "CalcGE.java") file and open it. This creates a new yellow box in the dashboard, which you can double-click to open its text.
Verify that this stub version compiles and runs. It won't do anything except put up your calculator buttons and display. Close the window to terminate it. This verification is important, because it establishes the fact that the generated code is good (if the window does not open, you can try the ideas in the "Trouble-Shooting GameEngine" page, or else get help, this may be beyond your ability to fix). Later, after you have added your own code -- basically copying what I show you, this first time -- if it fails to compile, you know it was something you did. Don't feel bad, I make mistakes all the time, that's what the Backspace key is on the keyboard for
You will see some lines with dollar '$' signs in them. DON'T MESS WITH THE '$' LINES, GameMaker uses them to find where to insert its generated code. Think of GameMaker as your programming partner, you wouldn't want to mess up your partner's code, would you? I mean, you can and nobody gets hurt, except that GameMaker may tell you the file is corrupt and you lose all your work in it. Me, I'm too lazy to keep typing the same thing over and over. Outside the lines enclosed by and including the dollars, this is your program, do anything you want (but it still must be good Java if you want it to compile and run).
In particular, at the top of the file you will see all your widget names declared (between dollar lines); you can declare your own instance variables in the white space below the second dollar line. Mine looks like this after I added the three variables that need to persist between events:
public class CalcGE extends GameEvent {Some lines farther down there is a Startup method with a couple more dollar lines, where the generated variable(s) are initialized. Again, you may insert your own initialization code after the second dollar line. I added one line, to initialize the display to "0.0":
JavaGame myGame = null; // soon: reference to the GameEngine
GameWgt CalcGE = null; // reference to the game board widget
/// ($) Begin generated class variables (do not modify) ($..)
GameWgt ... (generated code here)
/// (..$) End generated class variables ($) (do not modify this line)
char optor = ' '; // added instance variables..
double res = 0.0;
String CalcWd = "";public String toString() {return "(CalcGE)";} //~toString
public void StartUp() {
if (myGame != null) myRips = myGame.FindListWgt("{CalcGE}");
/// [$] Begin generated StartUp code (do not modify) [$..]
if (myGame==null) return;
DisplayTx = myGame.FindListWgt("{DisplayTx}");
/// [..$] End generated StartUp code [$] (do not modify this line)
if (DisplayTx != null) {int tmp = DisplayTx.PutTextLn("0.0");}} //~StartUp
Most of the remaining methods are "stubbed out" (comments only).
You will be using one of these, and you need to remove the double slants
from the front of its lines (so they are no longer comments, but real code).
The methods you don't need you can let the defaults do their thing, and
either leave them commented out, or else remove those methods entirely.
How do you know which ones you don't need? We're coming to that.
The one method you need is ClickEvt. Every time the user clicks
on a button, this method is activated (called), and the whom parameter
will be a reference to the widget they clicked on. The obvious most straight-forward
way to do this is to make a long list of if-commands, comparing
each known widget to the clicked whom. When you find a match,
either you add that digit to the growing input number, or else if it's
a function key, you first look to see if you have input and a function
pending (and do it), then save the new function and wait for its input
value, or if it's a function key with no input value (like equal, but you
will have already done the pending function key) or Clear, then just do
it.
But there's an easier way to do this. All the buttons are Rectangle
widgets with identifier values you gave in GameMaker, so inside the ClickEvt
method, we can ask what identifier number is associated with the button
that got clicked, and we need only a few lines, one for digits, and a few
more for function keys:
public boolean ClickEvt(GameWgt whom, int vert, int horz) {and for the function keys, you can cast (convert) it to a character:
int tmp, whazzat = whom.GetInfo();
char btnchr = (char)whazzat; // cast the saved int to original charwhich is really convenient when deciding what to do with it, which happens next:
if ((btnchr == '+')||(btnchr == '-')||(btnchr == '*')
|| (btnchr == '/') || (btnchr == '=')) {
// got function key, see if we have one pending..
if (optor > ' ') if (CalcWd != "") { // do pending operation..
double inval = Double.parseDouble(CalcWd);
/// f. if command is '+' add
if (optor == '+') res = res+inval;
/// g. else if '-' subtract
else if (optor == '-') res = res-inval;
/// h. else if '*' multiply
/// i. else if '/' divide
... // and so on for each operator
} // end of doing pending op
if (btnchr == '=') optor = ' '; // already did it
else optor = btnchr;
CalcWd = ""; // prepare for input
} // end of got function key
/// e. accept a value (one digit at a time)
else if (whazzat <= 9) CalcWd = CalcWd + whazzat;
if (DisplayTx == null) return false;
/// a. display the current result
if (CalcWd != "") // we have partial input, display it..
tmp = DisplayTx.PutTextLn(CalcWd);
else tmp = DisplayTx.PutTextLn("" + res); // otherwise show the result
return false;} //~ClickEvt
Recall that CalcWd is the String you are accumulating
the input digits into and optor is a character variable, both
declared with the result value res at the class level (after the
second dollar line, but before the first method, the three
blue lines above) so they hold their value between events.
I also have three local variables, declared inside this method, temporary values for looking at the button identifier before deciding what to do with it, and one temporary to accept (and discard) the result from the widget method PutTextLn, which puts a text line into the result display widget DisplayTx -- you may have given yours a different name, use yours when you add this code to your copy of ClickEvt -- and returns the actual (pixel) width in case you need it (which you don't, not today). When the user is pressing digit keys, the partial input (in variable CalcWd) is displayed; otherwise we show the result res.
A couple of remarks about the green line. Recall that res is a (double) number, not text, but PutTextLn is expecting a String parameter. You can know this by searching the GameWgt class for the PutTextLn method, or by clicking the PutTextLn link, or else in BlueJ, when you hover the mouse over the method name, it will pop up the JavaDoc documentation for that method, and you can read it there. In any case, Java will automatically convert a double value to String in some places, but not here as a parameter; however, concatenating it to an empty String (constant, "") is one of the places where it will convert, so we do that before passing it as a parameter. PutTextLn returns an integer result. I think Java lets you ignore the result of a function call, but doing so is a bad habit that will result in bugs that the compiler could have caught, but does not. The Java designer(s) got that one wrong. So I put the result into an otherwise unused variable tmp.
Everything else here probably looks familiar from the text-based calculator you already did.
Does your program compile? If not, -- in Replit, you already know to look at the message in the black console panel; in BlueJ, look for the first red bar in the margin, or click on the errors message in the lower right corner of the program window. The message it gives you may be wrong (it often is), but it might help you to find the true problem. When you fix the first problem, others may also go away. The compiler is easily confused, it was written by a programmer just like you (only a little farther along) and they make mistakes too. If you can't fix the problem, get help (summon a mentor) that's what we're here for.
If all else fails, delete the class (from the pop-up menu in the BlueJ
dashboard, then go back to GameMaker and click Build again -- you
may need to make some trivial change (like check and uncheck a checkbox)
to get the OK button to reappear. Then start over with the fresh copy of
your program. Verify that the fresh copy compiles and runs before you add
anything. Keep testing after you add each line (you will need to add the
matching right brace for each left brace before the compiler will accept
it).
When it runs (after you have added your code and successfully compiled
it) you can click on digits and see them show up on the display. At least
that's what they are supposed to do. If not, go to the
next page and we'll work through the problem.
Once you have the digits working, you can click a function key followed by a number, and it will do the math. But if you start it up fresh and click the digits of a number without a preceeding function, that number is lost. That's A Bad Thing, never lose the user's data. Also, when the user clicks a function key, they must remember that they did that, there's no visual indication that they did it. We can do better.
In the next page we will look at these problems, and also program the Clear button and the decimal point.
<<Previous | ToC | Next >>
Revised: 2023 March 1