11. Plenty of Monsters <<< Contents >>> 13. Beginning with Graphics
Our next program will be a game called Snake. You have probably already seen such a game before. A snake moves around the sheet, eating food, and it gets one piece longer with each gulp. We will improve the game. The snake will eat various kinds of food, and its pieces will vary accordingly to what it has eaten.
Create a new program called Snake (or Snake 2, if a program with this name already exists). An empty program opens. In a while, we will fill the program window with a fine new game. If you want, you can open the Snake program, which is prepared among Peters sample programs.
We will prepare the sheet by enclosing it
with a wall. From the item library, drag the Wall item
(from the [examples]\Cottage group) into the Global Variables and Functions window. Double-click the sheet
element to invoke the main sheet editor. Put the Wall item into the upper left corner.
We will look at a new sheet property. Each square contains, apart from an item picture, five logical flags and three numeric values. A logical value can keep a yes/no state, which can be tested. In our program, we will use the first flag to signalize that there is a wall on the square. We wont have to evaluate the item on the square, we will just ask the flag: "Is there a wall on the square?" We can even use several variants of walls.
The sheet editor toolbar contains (on the
right side) a drop-down box for types of editing. Select the Flag 1
function here. The appearance of the sheet changes; a little dark
red square
appears
in the upper left corner of each square. The little square is a
state indicator for the first flag in the square. Now it is off
(its state is "no"). Click the Editing
button on the toolbar.
This activates the square contents editing mode. Click the square
in the upper left corner with the Wall
item. The little square changes to light red color and a check
mark
appears. Flag 1
on the square is now on (its state is "yes"). Turn off the editing mode by clicking the Editing
button again.
Now we want to raise a wall around the
whole sheet. There is an easier way to do it than repeated
copying with the right mouse button. Click the Filling
button on the toolbar. Press and hold the left mouse button on
the wall item in the upper right corner of the sheet, and drag
the mouse to the upper right corner of the sheet. Release the
mouse button there. The part of sheet that you have selected by
this will be filled with the starting item. Fill the remaining
borders in a similar way. When you finish, click the Filling
button again to turn it off. You can also add walls inside the
sheet, e.g. like this:
Prepare food for the snake. Create five new items and draw different kinds of food into them. For example:
We will create the pieces of the snake. You can copy the food items and modify them like this:
In a similar way, create three items with pictures of poisons, and one piece of the snake with a picture of a skull:
Our last graphical task is to create the
snakes head. We will use Peter for this. Double-click the Peter
element in the Global
Variables and Functions window
to edit it. Click the Properties
button to open the window with sprite properties. Change the Delay Between Phases value from 55 to 165 (time between animation phases), change Phases per Step (0 =
immediately) from 8 to 0 (the sprite moves immediately), and change Standstill Phases from 1 to 2 and Moving
Phases from 4 to 0. Close the window by pressing Enter. The Peter sprite has four directions and two
standstill phases now.
Discard the first picture of Peter on the
upper left. Double-click the picture to edit it, and then draw a
snakehead looking to the right. Click the Previous Edit
button to return into the sprite editor. Using the right mouse
button, copy the first picture into the second picture in the
same row. Edit the head, so that the mouth is open. Copy both
pictures into the remaining rows for other directions. Turn the
heads on the pictures accordingly to the directions in the sprite
rows. You can do this by using the Edit
function. Finally, test
the sprite by clicking the Test
button.
We have several food items in our program, and we might want to add more of them in the future. With our present knowledge, we would have to handle each type of an item separately, which would not be very elegant. For this reason, we will learn how to use a new element a list.
At first, we will prepare the list,
although we dont know its function yet. From the program control
group, drag the
list
element into the Global Variables and
Functions window. Call it food list. When you expand it, you will see three elements.
Rename the first one, number
, to
5 number
of foods. The second element, index
,
should be renamed to food
index. The third one, index increment
,
will be called food
index increment. Add two new
items in the list, and call them food and food snake.
Now we will explain what is a list. A list
is something like a book. All pages in the book look similar, for
example each page contains one picture and one text, but all of
the pictures or texts are different from the rest. Our list will
contain the items of food types and snake pieces, and it will
make the items easier to use. Each page in the list will contain
a different picture of food and a different picture of a snake
piece. The first item in the list identifies the number of pages. In our
case, there are five types of food. The second item
is a number of the
current page in the list a list pointer (the numbering
starts with page 0). The third item
specifies the number of pages that the
pointer automatically moves with each access to the data in the
list.
We have to fill in the food list during the start of the program. It means that we have to specify, what item will be on which page. The commands for filling in the list are shown on the following picture. Put them into the beginning of the main program function.
Before we start to fill in the food list,
we will specify the initial page, from which we will start
filling in the list. We will set the food index
element to 0. We will use automatic increasing of page number,
and so we will set the food
index increment
to 1.
First, we will fill in the food item on all pages of the list. Gradually fill the food elements with individual food type items. The items are stored in the list, and after each item is set, the page number automatically increases by one. After the last item is set, the page pointer automatically moves to the beginning. We will start to fill in the food snake elements. The page number increases automatically again. When we finish, we set the automatic increasing of page numbers to 0, as we wont use automatic increasing in the program anymore.
If the player fails, that is if the snake hits the wall or eats poison, the game begins anew. For this, we will need to redraw the starting sheet of the program, without food and poison. We could clean it programmatically, but there is an easier way. We will save the main sheet into a variable of the sheet type, and simply restore the sheet in a new game by using this variable.
Create a new sheet variable (copy the sheet element from the Library of Variables and Functions window to the Global Variables and Functions window), and call it saved sheet of the game. Put a command for saving the main sheet to the beginning of the program:
The score will be displayed during the game. Create a numeric variable called maximum score, and set it to zero.
Create a new function called new game. The function will run at start, and so we will add it to the beginning of the program after the group for filling in the food list. It will be assembled accordingly to the following picture.
Create a logical variable called snake movement flag, a numeric variable called snake length, a numeric variable called next step counter and a function called display the number of snake pieces. The snake movement flag logical variable will be used to indicate the beginning of the snakes movement (when the game is started, the snake stays in its place, waiting for a key to be pressed). Set the variable in the new game function to "off". The snake length variable indicates the number of snake pieces, and it increases during the game. In the beginning of a new game, it will be set to 0. The next step counter counts 0.1-second time impulses, before the snake is moved one square (it decreases from 5 to 0). In the beginning of a new game, the counter will be set to 1, so that the first step is performed immediately after the first key is pressed.
The following command in the new game recalls the saved sheet. Use the same command as when saving the sheet, but reverse the order of the elements. The following commands set the snakes initial position. Turn off visibility for Peter, set his coordinates to X=9 and Y=8, turn him to the right, and make him visible again. In the end of the new game function, put the display the number of snake pieces function.
Now we will pay attention to the function that displays the number of snake pieces. Many games need to tell something to the player, e.g. the score. If we dont want to pay too much attention to displaying information, we can quickly and simply write them into the window title. We will inform the player about the length of the snake, and optionally also about the highest score. While displaying the number of pieces, we will stick to grammatical rules, and so we will distinguish between 1 piece and 2...3...4... pieces. Here are the contents of the function:
The snake length of 0 will be set in the beginning of each game. In this moment, we can display information about the author of the program, or later, about the maximum score.
The picture contains a new element window caption
(from the controls
group, dialogs
subgroup). The element is a text variable (it can be set or read)
representing the text in the window title bar. The merge of texts
element serves for connecting more texts into one. The connection
is made from the top to the bottom. Normal text can be specified
by typing it besides the text constant
element. The conversion
of number to text form
element converts
numbers to their textual representations.
The branches for other lengths of the snake are assembled in a similar way. Note that we can use one branch of the construction for more values of length.
A note on the window title usage: When a program starts, the name of its main function appears in its window title. If you dont need to change the window title during the program run-time, it is enough if you edit the main function name. The main function name in new programs is preset to the program name.
Now we will pay attention to the main
program loop. Put the conditional
repeating of commands element to the end of
the main function. Into the condition test, put the yes
element. This will create a never-ending cycle. The program
closing will be handled inside the procedure for keys as a
reaction to the Esc key. In the beginning of the main loop, we will
create the procedure for keys, then we will handle the
snakes movement, and finally we will handle the creation of
new items (food and poison). The last command in the loop will be
a waiting procedure, which will take care of the program timing.
You can insert the wait command into the loop now, and gradually,
we will put in the remaining procedures.
We will start with the creation of new
items. In front of the waiting, insert the conditional executing of
commands element, and call it creation of items. Its contents are on the following picture.
New things will be generated randomly, with a certain probability. For this reason, we will put a comparison of a random number with a constant into the condition test. By setting the constant to 0.03, we will ensure that a new item will be created during 3 out of 100 passes through the main loop. The probability of a creation of a new item is 3%.
We will create items on the sheet by using
Lucy again. First, we will put her onto a random square on the
sheet. We will use the width
of sheet element for her
horizontal position. This element will pass the value of 20, which is the width of the program sheet. It is
possible to specify the number directly, but it is better to work
with general numbers. This will save us from troubles if we
change something in the program in the future. We will pass the
width of sheet to the random
number
function, which will create a random
number between 0 and 19.999999999. The following function, integer part
,
will delete the decimal part of the number. This will create a
random integer between 0 and 19. These numbers represent the
horizontal coordinates of the first and last square on the sheet.
You surely remember that the squares are numbered from the bottom
left corner to the top and right, starting with zero. Similarly,
we will create a random vertical position.
After we set a random position, we will use a conditional command, which will test if the square is empty. If it is, the program will use another conditional command to decide between creating food or poison. We will compare a random number with the value of 0.1, which will ensure that poison will be created in 1 of 10 cases. In other cases, food will be created. All of this is on the following picture:
The following picture shows how a new
poison item will be created. We will create different types of
poison with different probability. Most often, we want the
toadstool, and we want the box of poison in least cases. We will
use a multibranch
control structure . The branching
expression will be a random integer between 0 and 5, created by using the integer part
and
random number
elements, and the number 6
.
The first branch will create a skull in 2 out of 6 cases (for the values of 0 and 1), which means that there is a 33% chance that a skull will be created. The second branch will give a smaller chance to the creation of the poison box 1 of 6 cases (for the value of 2), so the chance is 17%. In the remaining 3 cases out of 6, a toadstool will be created (for the values of 3, 4 and 5), which means the probability is 50% here.
The creation of the poison will be finished
by turning Flag
2 on. As you remember, we had used Flag 1
to
indicate that there is a wall on the square. Flag 2 will be used
to indicate that there is a poison on the square. This will make
our work easier later, as we wont have to test all of the
types of poison used in the program.
Food will be created by a different method. As you can see on the next picture, it is quite simple, because we can use the list of food types.
In the beginning, we will choose a random
page in the list, and we will take the food item from it. We will
set the food index to a random value between 0 and 4. These are the indices of the first and last page.
Instead of specifying the range of random values directly, we
will use an element representing the number of pages in the list:
. This will enable
us to add food types in the future without having to change the
method for their creation. We will keep the number of the food in
the numeric
value 1 (range 0 to 1023)
element for later use.
The following command will lay the food item on the position of
Lucy
. The last
command will turn Flag
3
on. Flag 3 will indicate that there is a
food on the square.
That is all for the creation of new items
you can test it now (be sure not to omit the waiting in
the end of the main loop). A snakehead opening its mouth should
appear in the middle of the sheet, and new items should be
appearing around it. You can close the program by clicking the button on the right side
of the window title bar.
We will continue by handling the snake
control. In the beginning of the main loop (in front of the
procedures for creating items), put a multibranch control
structure element and rename it
to snake
control. The method is on the
following picture.
This means that as the branching value, we
will use the key
input (does not wait for press)
function. The Left key will turn Peter (i.e. the snakehead) to the
left and turn on the snake movement flag. The other directions
will be similar. In the end, we will handle the Esc key it will use the function termination
command (from the program
control
group) to quit the
function. As it is the main program function, it will quit the
whole program.
Test the program. You can use the cursor keys (arrows) to turn the snakehead, and the Esc key to quit the program.
Our next task is to put the snake into movement. We will choose approximately 2 steps per second as the snake speed. For the sake of handling keyboard input and items generation, the main program loop works a little faster (18 passes per second). For this reason, we will use a counter for the next step, which will move the snake during every ninth pass.
The structure for snake movement is on the following picture. Put it behind the structure of snake control, but before the generation of new items. In the beginning of the structure, you can see a test, which checks if the snake is moving (this is because in the beginning of the game, the snake stays in one place until the first direction key is pressed). When the snake moves, the counter for the next step decrements (decreases by one). When the counter reaches zero, the snake can move one step. First, we will set the next step counter again. We have chosen the number 9, which represents 2 steps per second (the basic time unit of the computer is 55 milliseconds, which equals to 18 units per second).
Before the snake moves, we will test whether there is an obstacle in front of it, which it cannot pass. Such an obstacle is a wall, but also the snake itself (that is, one of its pieces). We will use Lucy for this test. We will put her onto Peters position, turn her in his direction, and make a step forward.
You surely remember that we have indicated
walls by Flag 1 .
For the sake of simplicity, we will assign this flag also to the
squares with the snake pieces. If the flag is turned on, it means
that there is a wall or a snake piece in front of the snake, and
the snake passes away. We will play a sound of a hit and animate
the snakes death. We will pay attention to this later. In
other cases, the snake can move forward.
In the snake movement, we will begin with Peter the snakehead. In approximately the middle of the structure of the movement, you see Peter making a step forward. The following branching structure stores a number, representing the direction of the last square, on the new square. Later, this number will help us find the way to the end of the snake.
We will keep the number representing the
direction to the previous square as numeric value 2 .
When the previous square is to the right, we will use the number 0, when it is up, we will use number 1, number 2 will represent the left direction, and when the
square is to the bottom, we will use the number 3. The structure for the previous square on the left
is shown on the picture, the remaining directions are similar.
Let us return to the beginning of the structure for moving the snake one step forward. We have left Lucy on the next square, where she tested the state of flag 1. Now we will turn her back and move her one step, which will get her to the same position as the snakehead. We can add a sound for the snakes step.
What follows is a cycle that moves the snakes pieces, as you see on the following picture. The number of the passes of the cycle is given by the snakes length. In the beginning, Lucy is on the position of the head. Each square with the head or a piece of the snake keeps a number representing the direction to the previous square. The number is a number of quadrants (i.e. quarters of a turnaround) that Lucy must turn counter-clockwise from the zero angle (zero angle equals the direction to the right).
First, Lucy turns to the previous square.
Using the following command, she carries the item from the
previous square to the square she stands on. This way, she moves
a snake piece one step forward. The next command turns on Flag 1 . As
we know, this flag indicates a wall or a snake piece. The last
command makes Lucy go one step forward, i.e. to the following
square towards the end of the snake.
After all of the passes through the cycle, Lucy remains on the square behind the new end of the snake. This is valid even if the snake length is 0, when no pass through the cycle is performed. What follows is moving the snakehead one step forward and saving the direction to the previous field, which has already been described. We will replace the contents of the square behind the snake with an empty square (which will be performed by Lucy), and turn off the flag indicating that there is a piece of the snake on the square. If a food or a poison is found, we will add a new snake piece later.
The snake is moved. Now we will pay
attention to the contents of the square on the new position of
the snakehead. First, we will test, if there is food on it. Food
is indicated by flag 3, so we will test Flag 3 .
The structure for food is on the next picture.
If there is food on the square, we will
delete it by laying down an empty square, and turn off Flag 3. The numeric variable 1 of the square stores the
number of the food from the food list. We will take numeric value 1 of the square and use it as the food index to get
the appropriate snake piece for the type of food. That means that
we will take the appropriate snake piece from the list and lay it
down on Lucys position. As we know, Lucy was left at the
square behind the end of the snake, where we had put an empty
square for a while. This creates a new snake piece with a food
inside. We will turn on Flag
1 to indicate a snake piece, and increase
the length of the snake. Finally, we can play a gulping sound and
show the new snake length.
The structure for poison is similar, as you see on the following picture. Poison is indicated by Flag 2. If we find it on, we will clear the square and turn this flag off. We will perform similar steps to the structure for food. We will always use the same new piece, without respect to the type of poison. We will also turn on flag 1 on Lucys position, increase the snake length, play a gulping sound, and display the new length. Only this time, this will be finished by the death of the snake.
Our last task is to create the function for the snakes death. Its contents are on the next picture.
First, the length of the snake is stored as the new maximum score. After a short pause, there is a wheezy sound. The death will be indicated by redrawing all of the snake pieces to pieces with a skull. After the redrawing and a short pause, we will clear the pieces of the snake (this will indicate the process of dying). There is a short pause again, and then a new game starts.
After the beginning of a new game, it is important to add a command for flushing the key buffer out. Players who get absorbed in the game often still press keys when the game is being ended. This could cause an accidental start of a new game.
The following pictures show the redrawing of the snake pieces to death and the clearing of the pieces. Again, we use the directions to the previous squares stored in numeric value 2 of the squares. During that, we animate the snakehead to turn around (the snake is dizzy). In the first case, the snake turns clockwise; in the other, it turns counter-clockwise.
Now you can start the program and test it. Have fun with your first real game.
11. Plenty of Monsters <<< Contents >>> 13. Beginning with Graphics