But first things first. In this tutorial, we will cover the boilerplate concepts behind all games: variables, output, input, functions, loops, and flow-of-control. Don't worry if you don't know what any of those mean -- that's what the tutorial is for. Once we have discussed these fundamentals, we'll present the code for our first game: GuessIt.
All games rely on the following foundational concepts. More advanced games involve additional ideas, but every game needs these:
- variables -- named buckets that hold data
- output -- how we show the game to the user
- input -- how we accept data from the user
- functions -- how we re-use code from at different points in a program
- loops -- how we perform repeated tasks efficiently
- flow-of-control -- how we make the game choose one path among many
Variables are just chunks of computer memory that hold data for us. You can think of a variable as a bucket with a name. You can put things in the bucket and take them out again.
To define a new bucket, we use the 'var' keyword, followed by the name of the bucket:
Once you have defined a new bucket, you can refer to it by name elsewhere in your code. You can use operators like the equals sign to manipulate the data inside the bucket.
takes the text data inside the double quotes and sticks it in the bucket called myName.
You can combine the definition of the bucket and the assignment of its value into a single line, if you like:
Now the variable 'myAge' has a value of 49.
Variables can hold text data, numerical data, code, graphics, sound, and any combination of these things wrapped into an 'object' (which we will cover in later tutorials).
Variables can also contain lists of data. These lists are often called "Arrays", and you create a list by enclosing the entries in square brackets ('[' and ']') and separating them by commas:
You can also break this up over multiple lines:
That's a lot to remember, but don't sweat it. The most important thing is:
variables are buckets that hold data.
All games need to output data to the player(s). Our first games will run on a web page, so we will use HTML for output. We will rely on the "write line" command supplied by the document in which we are running:
Note that in HTML, the "string" of letters '<br>' (without quotes) is a "line break" that marks the end of a line of text. Use this to move down to the next line.
These will appear as two separate lines in the web browser. Without the line breaks, they would appear together on a single line, even though we used two different writeln statements.
Players need a way to affect the games they play, whether it's telling the dealer to "hit me" in Blackjack, or firing a weapon in Doom. In our case, we will be responding to keyboard events. To do this, we will rely on the "event listener" system supplied by the document in which we are running.
An "event" is a change in the computer environment that affects the document. This could be many things: a key press, a mouse click, a message telling the browser to resize itself. Web documents supply an "event listener" system to capture and respond to important events. To listen for a particular event, we will call the document's "addEventListener" code:
The above line tells the document to execute "...your code here..." whenever the user presses key down. Don't worry about the 'function(event)' bit -- we'll discuss that in the next section. For now, let's consider this concrete example:
This displays the message, "Someone pressed a key!" each time the user presses a key.
There are many other events to which the document can respond, like "keyup", "mousedown", "mouse", and "mousemove", just to name a few. We'll get to those when we need them. For now, "keydown" is good enough.
Sometimes, you want your game to ignore events to which it previously responded. You can accomplish this using the "removeEventListener" command:
This would undo the "addEventListener" we used above.
Often, you write a chunk of a code that you want to use over and over. For example, suppose you wrote a chess program. Games of chess can last many turns. You would hope that you could write the code to play a single turn, then simply re-use that code for each subsequent turn.
If this seems confusing, think of a function as a meat grinder: you put stuff in, you turn the crank, and something useful comes out. The "stuff you put in" is the "list of input data". The "turn the crank" bit is the "chunk of code" part. The "something useful comes out," well...we haven't covered that. It's called the 'return value'. A specific example will clear all this up. Consider a function that computes the area of a rectangle:
You send in a length and a width, it multiplies them together to find the area, then it 'returns' that area back to you.
Remember that addEventListener stuff? Recall that the function looked like this:
Notice that this function doesn't return anything (there is no 'return' statement at the end). That's fine. Not all functions return a value. Sometimes you use them just to produce output when the crank is turned.
One last note: you can store a function inside a variable. Doing so lets you execute (or call) the function using the bucket's name. This is handy shorthand for executing code!
Now, any time you want to display a message, you can use this:
print("Fred ate bread!");
This will send the data "Fred ate bread" to the above function and print it out, automatically appending a <br> so that subsequent messages appear on subsequent lines in the document.
Applying this to our area function, we get:
And we could write a short program as follows:
What do you think would be the output of this program?
Often, games need to rapidly repeat a task. For example, if you are drawing a square grid, you must print multiple horizontal and vertical lines. For something like tic-tac-toe, this isn't so bad, but for a game like Go, you wouldn't want to draw every line with a new command.
Loops allow programmers to efficiently perform duplicate commands. There are several kinds of loops, all of them follow the same philosophy: the programmer defines a loop condition during which the program repeats a chunk of code over and over. The programmer follows the loop condition by the code to be repeated:
The while loop looks like this:
Consider this example:
This probably looks confusing, especially this bit:
greets = greets + 1;
Clearly, this is nonsense! There is no number that equals itself plus one.
greets = greets + 1;
"Take the value in greets (currently 0), add one to it, and place this back in the greets bucket."
So, after executing this line the first time, greets' value changes from 0 to 1.
We then hit the bottom curly brace and bounce back up to the 'while' statement, where we check the loop condition. Is the value in greets less than 5?
Yep! So we once again execute the code inside the curly braces. While doing so, we again hit the line
greets = greets + 1;
but this time, greets contains '1'. So what happens? We add one to it, and store the resulting value (2) back in greets.
Then we hit the bottom curly brace, bounce back up to the top of the loop, and check the conditions. 2 is still less than 5, so we go around again...
...and finally we bounce back to the top, check the condition and see that greets is equal to 5, rather than less than 5, so the loop condition is false and we break out of the loop by instantly jumping past the last curly brace.
That might seem like a silly example: why would we want to print out the same message 5 times? So let's consider a more useful example. Remember my dogs?
Now we're using the while statement to iterate (or step through) a list of data. Notice that we use square brackets ('[' and ']') to access a single element of the list, and that the first element in the list is number 0. In other words:
contains the word "Hershey", and
contains the word "Koda".
While loops are handy, but after you've written a few, you'll see that most of them fall into the following pattern:
For example, our doggy-loop above would look like this when written as a for loop:
If you want to compress this even further, you could take advantage of the math operators '+=' or '++':
count += 1;
is shorthand for
count = count + 1;
So, the super-compressed version of our doggy loop would be this:
Finally, let's talk about "flow of control." That's just a fancy way of saying, "my game can execute one of several options at the appropriate time." For example, suppose you are making a dungeon crawler and you have AI monsters that can take one of several actions when encountering the player. You would use flow of control statements to pick one of those actions when an encounter occurs.
Let's look at an example by modifying our doggy loop:
if (myDogs[i] === "Hershey")
Reads as, "if the ith element in myDogs is equal to the word Hershey".
As you can see, this will print out:
Hershey is barking.
Koda is farting.
Which might seem like a fart joke, but is sadly the status quo with my black lab, who is currently practicing his "art" 6 feet from my desk.
You now have all the building blocks you need to make a simple game. For our first outing, we will write "GuessIt" -- a variation on the card game Acey Ducey. In that game, you flip two cards, then place a bet as to whether a third card, when revealed, will fall between the first two. In GuessIt, we use randomly-generated numbers from 0 to 9, but otherwise, the game is identical.
Take a look at the source code. We have broken it up into two files:
definitions.js -- in which we define all the functions and variables used in the game
commands.js -- which actually executes the commands defined in definitions.js to start the game
If you just loaded definitions.js, the game would never start, because the definitions don't do anything but fill buckets with values and functions.
If you just loaded commands.js, the program would throw an error because the commands rely on the buckets defined in definitions.js.
The code for GuessIt contains a lot of comments to help you understand what we're doing at each point. Some of it repeats what we have covered here, but in a more specific context. Once you are comfortable with what it's doing, try modifying the game. Here are some suggestions:
Change the background color by using the command:
document.body.style.backgroundColor = "gray"; // Or whatever common color name you want
Change the amount of starting cash given to the user
Change the payout formula
Or, the biggie:
Add a new eventListener when the player loses that resets her starting cash and allows her to play again
Good luck, and have fun!