|
Download
This Tutorial and All The Code File
Written by Jake Leveto
and Macboy
1 - Basics
2 - Loops and Arrays
3 - Simple Graphics and Keyboard Input
4 - Sprites
5 - Files
6 - Resources
7 - Quicktime
8 - Tile Engine
Credits
1 -
Basics
METAL is a BASIC programming
language. This chapter will teach you the following things. Also all code
is in italics
How to print,
get input from the user, and clear the screen
How to work with numbers
IF statements
Commenting
Our first program
will be very simple, it will ask the user for a number and check to see
if that number is greater then 10. It will then tell you the result. First
open up METAL and make a new file.
At the beginning
we need to add cls to clear the screen.
Now first we have
to get a number from the user, we use the INPUT command. Type this in
the code window
input "Enter a
number please" ; n1
input tells METAL
that you need the user to enter something. The words in quotations are
what the screen will display. You need a semi-colon in between "Enter
a number please" and n1. The inputted number will be stored as
n1.
Now we need to check
to see if n1 is greater then 10. We will use a if statement to check this.
Here is some code
if n1 > 10 then
PRINT "Your number is greater then 10"
end if
The if statement checks
if the inputted variable is greater then 10. If the statement is
true, then it will execute the code up until end if.
But what happens if
the number is less then or equal to 10? We need to use a else statement
to check that. here is the revised code.
cls
input "Enter a
number please" ; n1
if n1 > 10 then
PRINT "Your number is greater then 10"
Else
PRINT "Your number is less then or equal to 10"
end if
The
else statements will execute if the IF statement is false.
Now it is time to
run your program, go to the Project menu -> Run. You should see your
question, and the results of your input.
Commenting is used
to remind yourself about certain things. Comments are started with a '
symbol. Comments do not effect the way your code runs at all. We
will be using these more next chapter.
This concludes Chapter
1. You can download the chapter 1 source code files from the same site
you received this tutorial.
2 -
Loops and Arrays
Loops are a way of
making your code repeat itself over and over and over again. Arrays are
BIG variables that can store multiple numbers. Here is what will be covered
in this section
How to do a
do-loop
goto command
arrays
how to do a for-next loop
Say we need a program
that prints the word "HI" over and over again, it would take way to long
to copy and paste PRINT "HI" over and over again. Loops will repeat the
same thing over and over again, here is an example. Remember that a '
is just a comment and doesn't effect the code
cls
'Clears the screen
do 'This is where we will start our loop
PRINT "HI" 'Prints hi
loop 'Tells the program to go back to the "do" line and start over again.
Now
try to run the program, BUT WAIT!!! This program will go on forever, and
there is only 1 way to get out of it, hit Control-D to quit the program.
Now we will do another
loop, but it will stop after 10 times. goto tells the program to skip
to another part of the code. Look at the source code and you may understand
more
cls
do
x = x + 1 'This number will let us know when we have
printed 10 times
PRINT X 'Prints the current number we are on
if x = 10 then GOTO done 'If we are on our 10th time
we will skip to the line called done, which is below
loop
done: 'done is
the label that we goto, this is called a label. Labels must be followed
with a :
Say
we have a program where we need to map out a checkers board, we would
need a variable for each square on a 8 by 8 board. This is one possible
solution for the problem
checkers_1_2 = 0
checkers_1_3 = 0
checkers_1_4 = 0
checkers_1_5 = 0
checkers_1_6 = 0
checkers_1_7 = 0
checkers_1_8 = 0
checkers_2_1 = 0
checkers_2_2 = 0
etc....
Now we can already
see that this won't work, there would be far to many variables in this
program , and just imagine if we needed to keep track of a 128 by 128
board!!! We fix this using things called arrays. here is the code
to make an array
dim checkerboard(8,8)
That just made each
of the variables we need for our board, and there accessed like
this
checkerboard(5,6)
= black 'Assigns the 5th checker over and 6th down to black
Arrays may be confusing
at first, but they will be very useful soon
Now the next loop,
called for-next, is designed especially for arrays. Here is the
format for a for-next loop
for <somevariable>
= <starting number> to <ending number>
Each time the for-next
loops, the variable is increased by 1. Say we have a array that stores
10 random numbers, and we want to display them all, here is the code
cls
dim random numbers(10) 'array that will hold 10 random numbers
for x = 1 to 10
'loop that goes from 1 to 10. each time the loop goes through x = x+1
randomnumbers(x) = rnd 'the randomnumber(x) variable
now equals a random number rnd is just another command that makes a random
number
next
'now that we have
all the random numbers in an array we want to print them, so we make a
loop
for x = 1 to 10
print randomnumbers(x) 'See how simple this is to print
the numbers in an array
next
That
concludes chapter 2. As a challenge try to modify that last program
to print 100 random numbers, or add 1 to each random number.
3 -
Simple Graphics and Keyboard Input
Now you know how to
loop, make arrays, get some information from the user, but you still don't
have any graphics. In this chapter you will learn how to
Use the circle,circle,rect,line,
and poly command
Load an image into your program
Copyrect
Keyboard input
Say we want to make
a simple green circle with a red rim in our program. This is actually
a really easy task if we use the circle and circle command.
First you need to know how the coordinate system works, the top
left of your METAL window is ALWAYS 0,0. As you go right the first number
will increase, and as you go down the Y number will increase.
circle x,y,r OR fcircle
x,y,r is the way METAL does circles. circle makes a circle with the middle
of x,y and a radius of r. fcircle makes a filled in circle.
Colors are done by
the forecolor command. Whenever you print or make shapes they will always
be the color the the forecolor is. Here is the format
forecolor red,green,blue
. Each color has to range from 0 to 65535.
cls
forecolor 65535,0,0
circle 30,30,25
forecolor 0,65535,0
fcircle 30,30,24
Now you should experiment
with the rect,line, and poly command. Here are the formats for them
rect x1,y1,x2,y2
line x1,y1,x2,y2
poly x1,y1,x2,y2,x3,y3,x4,y4.....ect.....
You probably won't
use those commands as much as you will want to load a picture into your
program, and this is how you will do it
Say we want to make
a simple program that loads a picture specified by the user onto the screen,
here is how we do it
cls
picture$ = open dialog$ 'A command that allows the user to
find a file
get quicktime pict size picture$,x,y 'Finds out how big the
picture is, and stores those numbers in the variables x and y
load quicktime pict picture$,0,0,x,y 'Loads the image
onto the screen
You may have noticed
a small problem here, what if the screen is to small to fit the entire
image on it? We will have to resize the screen to fit the image onto it.
Here is the modified code
cls
picture$ = open
dialog$ 'A command that allows the user to find a file
get quicktime pict size picture$,x,y 'Finds out how big the
picture is, and stores those numbers in the variables x and y
resize console 0,0,x,y 'Resizes the screen to fit the
picture
load quicktime pict picture$,0,0,x,y 'Loads the image
onto the screen
This method works
for a program like this, but what if we a making a game that we need to
load hundreds of images per second onto the screen, it would be very inefficient
to load off the hard drive each time, so instead we will use the RAM on
the computer. We can store pictures just like numbers, except we have
to tell METAL to set aside space to store the picture in its RAM.
We also will eventually
need to know how to find out what keys the user is pressing, so the next
set of code will explain how to do that. But first here is the way that
copyrect is typed
copyrect sourcex1,sourcey1,sourcex2,sourcey2,destinationx1,destinationy1,destinationx2,destinationy2,0,sourcescreen,destination
screen
if you want to copy
to the console, the destination screen is a 0
cls
picture1$ = open
dialog$ 'A command that allows the user to find a file
get quicktime pict size picture1$,x1,y1 'Finds out how big
the picture is, and stores those numbers in the variables x and
y
picturescreen1 = init screen(0,0,x1,y1) 'a screen that will eventually
hold the picture in ram
set screen to picturescreen1 'now if we use the cls,circle,or load pict
command, we will draw to the screen picturescreen1 instead of the console
load quicktime pict picture1$,0,0,x1,y1 'Loads the image
onto the screen
'The below code
will do the same thing but for a new picture
picture2$ = open dialog$
get quicktime pict size picture2$,x2,y2
picturescreen2 = in it screen(0,0,x2,y2)
set screen to picturescreen2
load quicktime pict picture2$,0,0,x2,y2
do 'A loop that
will see what key is being pressed, and then copy a picture to the screen
keymap
scan 'A command that checks to see what keys are currently being pressed
if
keymap key("1") then 'If the user is pressing the 1 key then we will do
the following
resize
console 0,0,x1,y1 'Resizes the console to fit the picture
copyrect
0,0,x1,y1,0,0,x1,y1,0,picturescreen1,0 'Copies the rectangle 0,0,x1,y1
from the screen picturescreen1 to the console (represented by the last
0)
end if
if
keymap key("2") then
resize console 0,0,x2,y2 'Resizes
the console to fit the picture
copyrect 0,0,x2,y2,0,0,x2,y2,0,picturescreen2,0
'Copies the rectangle 0,0,x1,y1 from the screen picturescreen2
to the console (represented by the last 0)
end if
if
keymap key("q") then end 'if the user presses the q key then we will quit
loop 'loops the
main part of the program
This concludes chapter
3
4 -
Sprites
The sprite system
is a good way to make most games in METAL. The first program we will make
will have a simple sprite(included in the download) move around, and when
you click the mouse, he will become invisible. You will also be blocked
by another wall sprite. You will learn how to
Set up the
sprite system
Make a sprite
Move the sprite and check collisions.
First we need to set
up the program, here is the code to do that
resize
console (100,100,500,500) 'Resizes the console to start at 100,100 and
end at 500,500
background = init screen(0,0,400,400) 'A screen that will hold the background
render = init screen(0,0,400,400) 'This is the screen that the program
will draw all the sprites too
set screen to background 'sets the screen to background
cls 'clears the screen
load quicktime pict ":background.jpg" 'loads the background, the :before
background.jpg means its in the same folder as this program
set screen to render
cls
set sprite back
port to background 'tells metal this is the background
set sprite render port to render 'tells metal this is the screen to draw
the sprites on
set sprite mask color to 65535,65535,65535 'white is now transparent for
sprites
spritescreen =
init screen(0,0,64,32) 'this screen will store the sprite pictures
set screen to spritescreen 'sets metal to work with this screen
cls 'clears the screen
load quicktime pict ":sprites.pct" 'loads the pict
character = grab
sprite (0,0,32,32) 'The character is from 0,0 to 32,32
brickwall = grab sprite (32,0,64,32) 'The wall is from 32,0 to 64,32
characterx = 180
'This is where the character will start
charactery = 180
brickwallx = 250 'This is where the wall is
brickwally = 300
Now for main loop
do
keymap scan
' The next commands
will move the characters location around , and writes down what direction
you just moved
if keymap key("w") then charactery = charactery - 8
if keymap key("s") then charactery = charactery + 8
if keymap key("a") then characterx = characterx - 8
if keymap key("d") then characterx = characterx + 8
if keymap key("q") then end 'if the user presses q then we quit
set sprite character,characterx,charactery
'sets the character to the right position
if sprite collides
(character,brickwall) then 'Metal will check to see if the 2 sprites are
colliding
characterx = lastcharacterx
charactery = lastcharactery
set sprite character,characterx,charactery 'sets the character to the
new position
end if
set sprite brickwall,brickwallx,brickwally
render sprites
' this draws all the sprites that have been set
lastcharacterx
= characterx
lastcharactery = charactery
copyrect 0,0,400,400,0,0,400,400,0,render,0
loop
All the sudden everything
is coming together, but we have one problem, you can run off the
screen! This is easy to fix with this code
if
characterx < 0 then characterx = 0 'if we are off the left
of the screen, we will put ourself back on it
if charactery < 0 then charactery = 0 'if we are off the
top of the screen, we will put yourself back on it
if characterx+32 > 400 then characterx = 400 - 32 'remember
that the character is 32 pixels long, so we have to add this
if charactery+32 > 400 then charactery = 400 - 32 'remember
that the character is 32 pixels tall, so we have to add this
The sprite system
is a very good way to start making games. As a challenge, try makeing
this 2 players. And now for all the code put together...
resize
console (100,100,500,500) 'Resizes the console to start at 100,100 and
end at 500,500
background = init screen(0,0,400,400) 'A screen that will hold the background
render = init screen(0,0,400,400) 'This is the screen that the program
will draw all the sprites too
set screen to background 'sets the screen to background
cls 'clears the screen
load quicktime pict ":background.jpg" 'loads the background, the :before
background.jpg means its in the same folder as this program
set screen to render
cls
set sprite back
port to background 'tells metal this is the background
set sprite render port to render 'tells metal this is the screen to draw
the sprites on
set sprite mask color to 65535,65535,65535 'white is now transparent for
sprites
spritescreen =
init screen(0,0,64,32) 'this screen will store the sprite pictures
set screen to spritescreen 'sets metal to work with this screen
cls 'clears the screen
load quicktime pict ":sprites.pct" 'loads the pict
character = grab
sprite (0,0,32,32) 'The character is from 0,0 to 32,32
brickwall = grab sprite (32,0,64,32) 'The wall is from 32,0 to 64,32
characterx = 180
'This is where the character will start
charactery = 180
brickwallx = 250 'This is where the wall is
brickwally = 300
do
keymap scan
' The next commands
will move the characters location around , and writes down what direction
you just moved
if keymap key("w") then charactery = charactery - 8
if keymap key("s") then charactery = charactery + 8
if keymap key("a") then characterx = characterx - 8
if keymap key("d") then characterx = characterx + 8
if keymap key("q") then end 'if the user presses q then we quit
if
characterx < 0 then characterx = 0 'if we are off the left
of the screen, we will put yourself back on it
if charactery < 0 then charactery = 0 'if
we are off the top of the screen, we will put ourself back on it
if characterx+32 > 400 then characterx = 400 - 32
'remember that the character is 32 pixels long, so we have
to add this
if charactery+32 > 400 then charactery = 400 - 32 'remember
that the character is 32 pixels tall, so we have to add this
set sprite character,characterx,charactery
'sets the character to the right position
if sprite collides
(character,brickwall) then 'Metal will check to see if the 2 sprites are
colliding
characterx = lastcharacterx
charactery = lastcharactery
set sprite character,characterx,charactery 'sets the character to the
new posistion
end if
set sprite brickwall,brickwallx,brickwally
render sprites
' this draws all the sprites that have been set
lastcharacterx
= characterx
lastcharactery = charactery
copyrect 0,0,400,400,0,0,400,400,0,render,0
loop
5 -
Files
Everything you learned
up to now is pretty good for a simple program, but you need to learn how
to read/write from a file for stuff like high scores and saved games.
In this chapter you'll learn how to
Read/write
text files
Read/write binary files
Delete, copy, rename, and move files
Use the Macintosh file type and creator values
Let's say that we
want to create a record of everybody's name and age. To do this, we'll
use fwrite to write to the file and fread to get data from the file.
file = open file(path$)
is how to open a file for reading/writing.
close file file is how to close an open file
fwrite file, var1, var2, ... is how to write to a file
fread file, var1, var2, ... is how to read from a file
cls
record = open file(":Record") ' This tells MetaL to open a file called
"Record" in the program's folder.
fwrite record, "Joe", 15 ' Write names and ages to the file
fwrite record, "Jack", 14
fwrite record, "Bill", 18
' Add more items if you want here
close file record ' Close file "Record"
' Now let's open
it again and read the data
record = open file(":Record") ' Open file "Record"
repeat ' Repeat...until is a loop that repeats until the condition is
met
fread record, name$, age
? name$, age ' The comma between the variables adds a tab
until eof(record) ' The eof command checks to see if we're at the end
of the file, so this reads until the end of the file.
close file record ' Close the file
Now
let's say we want to write data to a file that has spaces in it. This
isn't possible with fwrite because it records data with spaces in between.
We need to use fprint and line input.
fprint file, data$
is how to write a line of data to a file
line input file, data$ is how to read a line of data from a file
cls
newrecord = open file(":New Record") ' Open a file called "New Record"
in the program's folder
fprint newrecord, "Joe Smoe" ' Write names and ages
fprint newrecord, 15
fprint newrecord, "Billy Bob"
fprint newrecord, 18
' Add more items if you want here
close file newrecord ' Close the file
' Open and read
the data
newrecord = open file(":New Record") ' Open "New Record" repeat
repeat ' Starts the loop
line input newrecord, name$ ' Read in name
line input newrecord, age$ ' Line input doesn't accept numerical variables
so we need a string (the ones that end with $)
age = val(age$) ' Convert the age$ string to a value
? name$, age ' Output name and age with a tab seperator
until eof(newrecord) ' Read until the end of file
close file newrecord
Now
what if we want (for some reason) to access the actual data fork of a
file (the binary data)? We can't use our text commands so we need to use
binary commands like these:
file = bopen file(path$)
opens a file for binary editing
byte write file, value writes a byte of data (0-255) to a file
value = byte read(file) reads a byte of data from a file
short write file, value writes 2 bytes of data (0-65,535) to a file
value = short write(file) reads 2 bytes of data from a file
int write file, value writes 4 bytes of data (0-4,294,967,296 or 2^32)
to a file
value = int read(file) reads 4 bytes of data from a file
For the simplicity
of this example we'll just demonstrate bopen, byte write and byte read.
cls
bfile = bopen file(":Binary File") ' Open "Binary File"
for i = 0 to 255 ' Write all the numbers from 0 to 255 to a binary file
byte write bfile, i
next i
close file bfile ' We use close file with binary files also
bfile = bopen file(":Binary
File") ' Open the file again
repeat
number = byte read(bfile) ' Read the number
? number;" "; ' Output the number and a space WITHOUT starting a new line
after it.
until eof(bfile) ' eof works with binary files too
close file bfile ' Close the file
All
that's great if you're using files as data storage, but what if you need
to do some other actions with files? We need to use the other commands
such as deleting, copying, renaming, and moving. Here's the commands we
use to do this:
delete file path$
to delete a file
copy file path$, newpath$ to copy a file to a new location
rename file path$, newname$ to rename a file
cls
alert note "This program will delete a file you specify." ' Display a
note
path$ = open dialog$ ' Let the user select a file with open dialog
if path$ = "" then end ' If the path is empty (user picked cancel) then
quit
delete file path$
' Delete the file
alert note "All done."
Now
let's copy and move a file.
cls
copy file ":Record", ":Record Copy" ' Copy file "Record" to "Record Copy"
in the same folder
' Now let's move
"Record" somewhere else
copy file ":Record", ":Record Backup" ' Copy file "Record" to "Record
Backup"
delete file ":Record" ' Delete the original so we "moved" the file.
That's
all pretty easy. Now let's rename an existing file.
cls
rename file ":Record Copy", ":Record" ' Rename file "Record Copy" as "Record"
Easy.
Now what if we want to get and set the Macintosh file type and creator?
We use this:
type$ = get file type$(path$)
gets the file type of a file
set file type path$, type$ sets the file type of a file
creator$ = get file creator$(path$) gets the file creator of a file
set file creator path$, creator$ sets the file creator of a file
In this next program,
we'll create a SimpleText text file using those commands.
cls
textfile = open file(":Text File.txt")
fprint textfile, "This is a SimpleText text file."
close file textfile
path$ = ":Text
File.txt" ' Make the path a variable so we don't have to type the whole
thing every time.
set file type path$, "TEXT" ' Set the type to "TEXT"
set file creator path$, "ttxt" ' Set the creator to "ttxt" (SimpleText)
if get file type$(path$)
= "TEXT" and get file creator$(path$) = "ttxt" then ' Check to make sure
everything went OK
else
alert error "There was an error." ' Display an error
end ' Quit
end if
Now
you know how to use the Macintosh file system in MetaL.
6 -
Resources
Earlier in this tutorial
you learned how to use simple graphics. Now what if you want graphics,
but not ones that take up a huge amount of space? Or what if you want
to do sounds? For instances like these you need a resource file. In this
chapter we'll learn to
Attach a resource
file to your program
Load PICT resources
Use cicons
Play sounds
First thing to do
when using resources is to attach the resource file made with ResEdit
or a similar program. The first way is the more common one.
] rsrcmerge path$
merges the resource file with your program so you don't need external
resources.
attach resources path$ attaches the resource file to your program while
it's running. This is useful if you only need resources in one instance
that might not happen anyways.
To use the resources
you need to use these commands:
load
pict rsrc id, x0, y0, x1, y1 loads a "PICT" resource
to the location specified
play rsrc id plays the "snd " resource specified
plot cicon id, x0, y0, x1, y1 loads a "cicn" resource to the location
specified
In our first example we'll be using rsrcmerge, load pict rsrc, and play
rsrc. We're going to say that PICT 128 is a picture of a bird (64x64)
and snd 128 is the sound of that bird.
] rsrcmerge ":Resources.rsrc"
' Merges the resource file - I always put ']' commands on the top
lines to make it easier to remember that they do stuff only during compilation.
cls
load pict rsrc 128, 0, 0,
64, 64 ' Loads PICT resource 128 to the coordinates specified
play rsrc 128 ' Plays snd resource 128
In this next example, we'll be demonstrating attach resources and plot
cicon. We're going to say that cicn 128 is some picture (32x32) that represents
that everything went OK. We're also going to learn how to access MacOS
cicons.
cls
input "Do you want to load
the resources? ";answer$ ' Ask the user if they want resources
answer$ = lcase$(answer$) ' The lcase() function converts a string to
all lowercase
if answer$ = "yes" or answer$
= "y" then ' If user said yes
attach resources ":Resources.rsrc" ' Adds the resource file
plot cicon 128, 0, 0, 32, 32 ' Loads cicn 128 into specified coordinates
else ' If user didn't say yes
plot cicon 0, 0, 0, 32, 32 ' Loads the MacOS stop sign into specified
coordinates
end if
Now, on the line plot cicon
0, 0, 0, 32, 32 we used cicn 0. Well what is cicn 0? cicn 0 is the
MacOS default dialog stop sign (the stop sign with a hand on it). The
MacOS message sign (the one with a speech bubble coming out of a guy's
mouth) is cicn 1. cicn 2 is the yellow warning sign. These are built in
and you don't need a resource file to access them.
That's the conclusion on the
Resources chapter of this tutorial. To learn more, refer to the MetaL
help file.
7 -Quicktime
Early on we learned
how to do simple graphics. We learned how to use resources instead of
external files. So far, not many problems have come up. But what about
QuickTime? Does MetaL have support for that? Of course. :) Now we'll learn
how to utilize these functions to:
Play back movies
Play external sounds/music
More in-depth explanation on how to load external pictures
How to check for QuickTime errors
First thing you should
do in programs that use QuickTime is initialize it with this line of code:
load quicktime
This makes sure it's loaded
at the beginning of your program so you won't have to wait while it loads
it later on.
Now we'll learn how to open,
play, and do some other stuff with movie files.
movie = open movie(path$) opens
a movie for viewing
set movie rect movie, x0, y0, x1, y1 sets the movie's viewing rectangle
get movie wh movie, width, height gets the movie's size
start movie movie begins playing the movie
stop movie movie stops playing the movie
close movie movie closes the movie
is movie done(movie) checks to see if the specified movie is finished
playing
movies task gives some CPU time to play the movie
We will use all of these commands
in the next example.
cls
load quicktime
filename$ = open preview
dialog$ ' Let user select a movie (with a preview box)
if filename$ = "" then end ' If user picked cancel then quit
themov = open movie(filename$)
' Open movie as "themov"
get movie wh themov, width, height ' Get movie's size
left = screen width/2-(width/2)
' Center MetaL console
top = screen height/2-(height/2)
right = screen width/2+(width/2)
bottom = screen height/2+(height/2)
resize console left, top, right, bottom ' Resize and center
system time ' Give system some CPU time to update other programs' content.
set movie rect themov, left,
top, right, bottom ' Set viewing rectangle
start movie themov ' Starts playing the movie
repeat ' Give CPU time to
play the movie until the movie is finished.
movies task
until is movie done(themov)
stop movie themov
close movie themov
Now, that code works fine for movies that will fit on the screen, but
what if the movie is larger than your screen size? Here's the revised
code:
cls
load quicktime
filename$ = open preview
dialog$ ' Let user select a movie (with a preview box)
if filename$ = "" then end ' If user picked cancel then quit
themov = open movie(filename$)
' Open movie as "themov"
get movie wh themov, width, height ' Get movie's size
if width > screen width
then ' If movie is wider than screen
num = width-screen height
width = width - num ' Scales it down
height = height - num ' Scales down height too for same proportions
end if
if height > screen height then ' Now check for the movie being taller
than screen
num = height-screen height
width = width - num
height = height - num
end if
left = screen width/2-(width/2)
' Center MetaL console
top = screen height/2-(height/2)
right = screen width/2+(width/2)
bottom = screen height/2+(height/2)
resize console left, top, right, bottom ' Resize and center
system time ' Give system some CPU time to update other programs' content.
set movie rect themov, left,
top, right, bottom ' Set viewing rectangle
start movie themov ' Starts playing the movie
repeat ' Give CPU time to
play the movie until the movie is finished.
movies task
until is movie done(themov)
stop movie themov
close movie themov
Now it works perfectly! Also,
to rewind or fast forward movies, use these commands:
rewind movie to start movie
rewinds the movie to the very beginning
rewind movie to end movie rewinds (fast forwards in this case) the movie
to the very end.
Now we'll learn to play movies
and sound. It's pretty much the same, but not as much code is required.
Here's how we do it:
cls
load quicktime
hide console ' This makes
the console invisible. We use this because there's no visual content in
our program.
filename$ = open dialog$
' Let user select a movie (or in this case, a music/sound file)
if filename$ = "" then end ' If user picked cancel then quit
themov = open movie(filename$)
' Open movie file as "themov"
' We don't need to get the size because we don't have a visual.
start movie themov ' Start
playing
repeat ' Give CPU time until
it's finished
movies task
until is movie done(themov)
stop movie themov
close movie themov
Perfect! Now we'll talk more
in-depth about how to load external pictures.
get quicktime pict size path$,
width, height gets a picture's size
load quicktime pict path$, x0, y0, x1, y1 loads a picture into the coordinates
specified
Now both of these commands
refer to loading "QuickTime pictures". What's the difference between QuickTime
pictures and regular ones?
QuickTime pictures are any
image type that QuickTime supports (i.e. JPEG, GIF, TIFF, PICT, etc.).
Regular pictures are only of the Macintosh type "PICT" and aren't compressed
much (and are quicker to load because we don't have to interact with QuickTime
AND the system).
cls
load quicktime
filename$ = open preview
dialog$ ' Let user select an image (with a preview box)
if filename$ = "" then end ' If user picked cancel then quit
get quicktime pict size
filename$, width, height
' Here's where we check
for the "too big" problem.
if width > screen width then ' If image is wider than screen
num = width-screen height
width = width - num ' Scales it down
height = height - num ' Scales down height too for same proportions
end if
if height > screen height then ' Now check for the image being taller
than screen
num = height-screen height
width = width - num
height = height - num
end if
left = screen width/2-(width/2)
' Center MetaL console
top = screen height/2-(height/2)
right = screen width/2+(width/2)
bottom = screen height/2+(height/2)
resize console left, top, right, bottom ' Resize and center
system time ' Give system some CPU time to update other programs' content.
load quicktime pict filename$,
left, top, right, bottom ' Load the picture
One more thing we really need to learn is how to detect errors. We do
this using the movies error function. If it's true (-1) then an
error occured doing something involved with QuickTime (opening a movie/picture,
playing a movie, etc.). Here's how to check and respond to an error:
if movies error then ' Check for error
alert error "A QuickTime error has occured. You may need a newer version
of QuickTime." ' Display alert
end ' Quit
end if
Well that pretty much sums
it all up for this chapter. Remember, if you need more help always check
with the MetaL online help. There's bound to be an explanation there.
Happy programming! :)
7 -Tile Engine
How to Build a
Tile Engine
To fully use this
tutorial you will need this
.sit archive with the finished code and the free tiles.
The Reason For
a tile Engine
OK first, do you know
what a tile engine is? If you do you can skip this paragraph. A tile engine
is a way to make a level using, well tiles. A tile is a picture of grass,
dirt, water, stone or whatever and is generally 32*32. It can actually
be any size and doesn't have to be square. We'll use 32*32 in this tutorial
for simplicity. A tile engine map looks like this:
data 2,2,2,2,2,2,2,2
data 2,2,3,3,3,3,3,2
data 2,3,3,3,2,3,3,2
data 2,3,3,3,2,2,2,2
data 2,2,2,3,2,2,3,2
data 2,2,3,3,2,2,3,2
data 2,2,3,3,3,3,3,2
data 2,2,2,2,2,2,2,2
This is a 8*8 tile map ware 3 is grass and 2 is stone. The tile engine
takes it and makes it look like this:

This may not look spectacular but this is how all 2D RPGs are mode (Except
maybe Blork Quest for the Blog). Even Final Fantasy 6 and Chrono Trigger
used a tile engine though it is hard to tell. he reason fore a tile engine
is to make level design esier and so save disk space. A level in a the
tile engine made with this tutorial is ~70 bytes. That's way smaller than
if it were a pict graphic. A pict graphic of the same sizer is 128k (128,000
bytes). Games do not always look like tile based games wen they really
are. Does this look like a tile based game?

(Chrono Trigger by
Squaresoft)
These games use a technique called Stamps but we'll get into that later.
A Basic Tile Engine*:
(pun intended)
'===== Load Map ====='
dim map(8,8)
for tempy=1 to 8
for tempx=1 to 8
read map(tempx,tempy)
next tempx
next tempy
'===== Main Loop ====='
mainloop:
'-=- Draw Map -=-'
for tempx=1 to 8
for tempy=1 to 8
if map(tempx,tempy)=2 then forecolor 40000,40000,40000 'Stone Grey
if map(tempx,tempy)=3 then forecolor 0,40000,0 'grass green
plot tempx,tempy 'put a dot at tempx,tempy of the current color
next tempy
next tempx
wait .1
goto mainloop
'===== Map Data ====='
data 2,2,2,2,2,2,2,2
data 2,2,3,3,3,3,3,2
data 2,3,3,3,2,3,3,2
data 2,3,3,3,2,2,2,2
data 2,2,2,3,2,2,3,2
data 2,2,3,3,2,2,3,2
data 2,2,3,3,3,3,3,2
data 2,2,2,2,2,2,2,2
This uses the same map as the map at the beginning of the chapter. Interesting:
Yes, Small: Yes, Fun: NO! You need to change a few things before it looks
good. But first a explanation of what goes on.
'===== Load Map ====='
This is just a comment to help you (and me) remember what is going
on in the code
dim map(8,8)
for tempx=1 to 8
for tempy=1 to 8
read map(tempx,tempy)
next tempy
next tempx
The dim map(8,8) makes map have 2 dimensions each
8 long. It would look like the map except all zeros if you could see what
it looked like. The for tempx=1 to 8 makes tempx
cycle 1 to 8. The tempy does the same but in the other dimension of the
map. This is a bit complicated to explain so don't worry if you don't
get it.
read map(tempx,tempy) reads the map data starting from the
first data number then the next and so on. It stores each number in the
map at tempx,tempy.
mainloop:
'-=- Draw Map -=-'
for tempx=1 to 8
for tempy=1 to 8
if map(tempx,tempy)=2 then forecolor 40000,40000,40000 'Stone Grey
if map(tempx,tempy)=3 then forecolor 0,40000,0 'grass green
plot tempx,tempy 'put a dot at tempx,tempy of the current color
next tempy
next tempx
wait .1
goto mainloop
Once aging we have the for tempx-tempy loops. This time it is to get the
data out of the map. This changes the current color to gray or green if
the map=2(stone) or 3(grass). Then it plots at tempx,tempy to put a dot
of the current color at the right place.
We can change it a little bit to make it more visible. Change plot
tempx,tempy to:
rect (tempx-1)*32, (tempy-1)*32, tempx*32, tempy*32
This draws a 32*32 rectangle of the current color from (tempx-1)*32 and
so on. We have the -1 behind the first temp to make the rectangles start
at 0 not 32. Try this to se what I mean:
rect tempx*32, tempy*32, (tempx+1)*32, (tempy+1)*32
I suppose you must be thinking "Neat! but the window is the wrong size".
This is what I use to fix that:
mapx=8
mapy=8
sw=screen width
sh=screen height
resize console (sw/2)-((mapx*32)/2),(sh/2)-((mapy*32)/2),(sw/2)+((mapx*32)/2),(sh/2)+((mapy*32)/2)
Put this at the beginning of your code. It will resize the console to
the size of the map. Also change the 8 in the for tempx=1...loops
to mapx and change the 8 in for tempx=1...loops
to mapy. You should also change:
dim map(8,8)
To:
Dim map(mapx,mapy)
This will make it easier in the future to change your map size in the
future. There! A working Tile engine!
Wait there is still one thing missing! What is a game without a player!
Add
px=3
py=3
To the start of your code.
Then add this between next tempx and wait .1 on line 32:
Forecolor 65535,65535,65535 'white
Rect (px-1)*32, (py-1)*32, px*32, py*32
There! a blinking white block for the player!
Time to fix the blinking! Add this after mapy=8:
Screen=init screen(0,0,mapx*32,mapy*32)
Now add:
set screen to screen
after mainloop:.
Finally add :
copyrect 0,0, mapx*32,mapy*32,0,0, mapx*32,mapy*32, 0,screen,console
The set screen makes the program draw the rects to screen the invisable
screen you made. Copyrect copys screen so the window(console).
Now all we need are the controls.
Add this between forecolor 65535,65535,65535 and Rect
(px-1)*32, (py-1)*32, px*32, py*32:
keymap scan 'check the keyboard
if keymap key("a") then px=px-1
if keymap key("d") then px=px+1
if keymap key("w") then py=py-1
if keymap key("s") then py=py+1
This is pretty well self explanatory. It checks the keys and if a, s,
d, or w are pressed it adds or subtracts to the appropriate variable(px
is a variable if you don't remember).
I know you can walk through the stone walls so let's fix that. Make a
data statement at the bottom of the code (under the last data statements)
like this:
Data 3, 0,1,0 ' number of tiles, whether tile 1 is walkable or not,
tile 2...
'1= solid 0=walkable
'tile 1=dirtr 2=stone 3=grass
Now put make this under the load level next tempy:
read numtiles
dim tiledata(numtiles)
for temp=1 to numtiles
read tiledata(temp)
next temp
This loads the tile data to tiledata()
Then change the player movement to this:
keymap scan 'check the keyboard
if keymap key("a") and tiledata(map(px-1,py))=0 then px=px-1
if keymap key("d") and tiledata(map(px+1,py))=0 then px=px+1
if keymap key("w") and tiledata(map(px,py-1))=0 then py=py-1
if keymap key("s") and tiledata(map(px,py+1))=0 then py=py+1
Now you can only move to the tiles that have a tiledata set to
0.
You also might want to add a quit because using ctrl-D every time gets
a bit annoying: Add this after if keymap key("S")...:
if keymap key("q") then end
quick simple and fast.
The code should now look like this:
px=3
py=3
mapx=8
mapy=8
Screen=init screen(0,0,mapx*32,mapy*32)
sw=screen width
sh=screen height
resize console (sw/2)-((mapx*32)/2),(sh/2)-((mapy*32)/2),(sw/2)+((mapx*32)/2),(sh/2)+((mapy*32)/2)
'===== Load Map ====='
dim map(8,8)
for tempy=1 to 8
for tempx=1 to 8
read map(tempx,tempy)
next tempx
next tempy
'===== Load Tiledata ====='
read numtiles
dim tiledata(numtiles)
for temp=1 to numtiles
read tiledata(temp)
next temp
'===== Main Loop ====='
mainloop:
set screen to screen
'-=- Draw Map -=-'
for tempx=1 to 8
for tempy=1 to 8
if map(tempx,tempy)=2 then forecolor 40000,40000,40000 'Stone Grey
if map(tempx,tempy)=3 then forecolor 0,40000,0 'grass green
rect (tempx-1)*32, (tempy-1)*32, tempx*32, tempy*32
next tempy
next tempx
'-=- Draw Player -=-
forecolor 65535,65535,65535 'white
'-Move player-'
keymap scan 'check the keyboard
if keymap key("a") and tiledata(map(px-1,py))=0 then px=px-1
if keymap key("d") and tiledata(map(px+1,py))=0 then px=px+1
if keymap key("w") and tiledata(map(px,py-1))=0 then py=py-1
if keymap key("s") and tiledata(map(px,py+1))=0 then py=py+1
'-Draw player-'
Rect (px-1)*32, (py-1)*32, px*32, py*32
copyrect 0,0, mapx*32,mapy*32,0,0, mapx*32,mapy*32, 0,screen,console
wait .1
goto mainloop
'===== Map Data ====='
data 2,2,2,2,2,2,2,2
data 2,2,3,3,3,3,3,2
data 2,3,3,3,2,3,3,2
data 2,3,3,3,2,2,2,2
data 2,2,2,3,2,2,3,2
data 2,2,3,3,2,2,3,2
data 2,2,3,3,3,3,3,2
data 2,2,2,2,2,2,2,2
Data 3, 0,1,0 ' number of tiles, whether tile 1 is walkable or not, tile
2...
'1= solid 0=walkable
'tile 1=dirtr 2=stone 3=grass
*Adding Graphics to this Program:
You might want to add better graphics to this because... Well it looks
terrible. You need a program like ColorIt! Or any drawing program that
can save files as Pict files. First make a folder in the same folder as
your game called "Data" then draw a dirt tile a stone wall tile and a
grass tile. You can just use the ones HERE
if you want. Make sure they are 32*32 pixels though if you draw them yourself.
Put them in the data folder and name the dirt "tile1", the stone wall
"tile2", and the grass tile "tile3".
Add this between load tile data and mainloop:
'===== load Tile Pictures ====='
dim tilepict(numtiles)
for temp=1 to numtiles
tilepict(temp)=init screen(0,0,32,32)
set screen to tilepict(temp)
load pict ":Data:tile"+str$(temp)
next temp
This will load the tile pictures screens inside tilepict().
Next change;
rect (tempx-1)*32, (tempy-1)*32, tempx*32, tempy*32
to
copyrect 0,0,32,32,(tempx-1)*32, (tempy-1)*32, tempx*32, tempy*32,0,tilepict(map(tempx,tempy))
, screen
You can delete the two if-forecolor lines above it.
<* Note: My computer was just crashed by MS word, the auto temp file
was too garbled and in turn crashed the computer again and so on. DON'T
USE MS SOFWARE IT IS SO BUGRIDDEN I WILL CRASH EVEN ON OSX>
*I had just finished explaining how to add player graphics. It made the
code look like this:
px=3
py=3
mapx=8
mapy=8
Screen=init screen(0,0,mapx*32,mapy*32)
sw=screen width
sh=screen height
resize console (sw/2)-((mapx*32)/2),(sh/2)-((mapy*32)/2),(sw/2)+((mapx*32)/2),(sh/2)+((mapy*32)/2)
'===== Load layer Graphic ====='
player=init screen(0,0,32,32)
set screen to player
load pict ":Data:player"
'===== Load Map ====='
dim map(8,8)
for tempy=1 to 8
for tempx=1 to 8
read map(tempx,tempy)
next tempx
next tempy
'===== Load Tiledata ====='
read numtiles
dim tiledata(numtiles)
for temp=1 to numtiles
read tiledata(temp)
next temp
'===== laod Tile Pictures ====='
dim tilepict(numtiles)
for temp=1 to numtiles
tilepict(temp)=init screen(0,0,32,32)
set screen to tilepict(temp)
load pict ":Data:tile"+str$(temp)
next temp
'===== Main Loop ====='
mainloop:
set screen to screen
'-=- Draw Map -=-'
for tempx=1 to 8
for tempy=1 to 8
copyrect 0,0,32,32,(tempx-1)*32, (tempy-1)*32, tempx*32, tempy*32,0,tilepict(map(tempx,tempy)),screen
next tempy
next tempx
'-=- Draw Player -=-
forecolor 65535,65535,65535 'white
'-Move player-'
keymap scan 'check the keyboard
if keymap key("a") and tiledata(map(px-1,py))=0 then px=px-1
if keymap key("d") and tiledata(map(px+1,py))=0 then px=px+1
if keymap key("w") and tiledata(map(px,py-1))=0 then py=py-1
if keymap key("s") and tiledata(map(px,py+1))=0 then py=py+1
'-Draw player-'
copyrect 0,0,32,32,(px-1)*32, (py-1)*32, px*32, py*32, 36,player,screen
copyrect 0,0, mapx*32,mapy*32,0,0, mapx*32,mapy*32, 0,screen,console
wait .1
goto mainloop
'===== Map Data ====='
data 2,2,2,2,2,2,2,2
data 2,2,3,3,3,3,3,2
data 2,3,3,3,2,3,3,2
data 2,3,3,3,2,2,2,2
data 2,2,2,3,2,2,3,2
data 2,2,3,3,2,2,3,2
data 2,2,3,3,3,3,3,2
data 2,2,2,2,2,2,2,2
Data 3, 0,1,0 ' number of tiles, whether tile 1 is walkable or not, tile
2...
'1= solid 0=walkable
'tile 1=dirt 2=stone wall 3=grass
More Maps!
Every tile based game can think of has more than one map (Except the old
Old OLD Pacman). Unless you want to make Pacman you will need more than
one map. Here's how:
Change the map loading to this:
read numlevels
dim map(numlevels,mapx,mapy)
dim mapdata(numlevels,4)
For temp=1 to numlevels
read mapdata(temp,1)
read mapdata(temp,2)
read mapdata(temp,3)
read mapdata(temp,4)
for tempy=1 to mapy
for tempx=1 to mapx
read map(temp,tempx,tempy)
next tempx : next tempy
next temp
Now it loads data we haven't added yet so go to map data and put
data 1
above the level. His is how many levels you have.
Next add
data 0,0,0,0
below data 1. This is what level is up, down, left,
and right.
Finally change the key controls to:
keymap scan 'check the keyboard
if keymap key("a") and tiledata(map(currentlevel,px-1,py))=0 then px=px-1
if keymap key("d") and tiledata(map(currentlevel,px+1,py))=0 then px=px+1
if keymap key("w") and tiledata(map(currentlevel,px,py-1))=0 then py=py-1
if keymap key("s") and tiledata(map(currentlevel,px,py+1))=0 then py=py+1
It now should work again. But what happened? It now loads as many
levels as you want.
To test this, copy level 1 and paste below level 1 so it looks like this:
'--- Area 1 ---'
Data 0,0,0,0
data 2,2,2,2,2,2,2,2
data 2,2,3,3,3,3,3,2
data 2,3,3,3,2,3,3,2
data 2,3,3,3,2,2,2,2
data 2,2,2,3,2,2,3,2
data 2,2,3,3,2,2,3,2
data 2,2,3,3,3,3,3,2
data 2,2,2,2,2,2,2,2
'--- Area 2 ---'
Data 0,0,0,0
data 2,2,2,2,2,2,2,2
data 2,2,3,3,3,3,3,2
data 2,3,3,3,2,3,3,2
data 2,3,3,3,2,2,2,2
data 2,2,2,3,2,2,3,2
data 2,2,3,3,2,2,3,2
data 2,2,3,3,3,3,3,2
data 2,2,2,2,2,2,2,2
Make Area 2 whatever you like but put a space in the wall 3 lines from
the top on the left side of level 2. Now make a space in the wall 3 lines
from the top on the right side on level 1. It should look like this:
'--- Area 1 ---'
Data 0,0,0,0 'up, down,left right
data 2,2,2,2,2,2,2,2
data 2,2,3,3,3,3,3,2
data 2,3,3,3,2,3,3,1
data 2,3,3,3,2,2,2,2
data 2,2,2,3,2,2,3,2
data 2,2,3,3,2,2,3,2
data 2,2,3,3,3,3,3,2
data 2,2,2,2,2,2,2,2
'--- Area 2 ---'
Data 0,0,0,0 'up, down,left right
data 2,2,2,2,2,2,2,2
data 2,1,1,1,1,1,1,2
data 1,1,1,1,1,1,1,2
data 2,1,1,1,1,1,1,2
data 2,1,1,1,1,1,1,2
data 2,1,1,1,1,1,1,2
data 2,1,1,1,1,1,1,2
data 2,2,2,2,2,2,2,2
change the up,down,left,right on level 1 to
data 0,0,0,2
and on level 2 to
data 0,0,1,0
Now the levels are almost connected! This tells the game that if
you walk off the right side of the map on level one you go to level 2
and that if you walk off the left side of level 2 then you go to level
1.
Connect the levels by changing the player movement. Change it to this:
if keymap key("a") and px>1 then if tiledata(map(currentlevel,px-1,py))=0
then px=px-1
if keymap key("d") and px<mapx then if tiledata(map(currentlevel,px+1,py))=0
then px=px+1
if keymap key("w") and py>1 then if tiledata(map(currentlevel,px,py-1))=0
then py=py-1
if keymap key("s") and py<mapy then if tiledata(map(currentlevel,px,py+1))=0
then py=py+1
This stops the program from goofing the transfer from one level
to another.
Now the LAST thing to add!!! Put this between copyrect... ...0,screen,console
and wait.
keymap scan 'check the keyboard
if keymap key("a") and px=1 then currentlevel=mapdata(currentlevel,3)
: px=mapx
if keymap key("d") and px=mapx then currentlevel=mapdata(currentlevel,4)
: px=1
if keymap key("w") and py=1 then currentlevel=mapdata(currentlevel,1)
: py=mapy
if keymap key("s") and py=mapy then currentlevel=mapdata(currentlevel,2)
: py=1
This actually moves you from level to level. It is similar to the
movement controls.
Conclusion and Advanced Tile Engine Stuff:
So you know how to make a basic tile engine*, how to make the player
move, how to make levels for it, how to add graphics and make more than
one level. what more is there to know? pun intended again. Here is a summery
of what more you could add:
Stamps
Well as I said in the beginning you could add stamps. Stamps are pictures
that can be anywhere on the document. Stamps are not bound to the same
32*32 pixel grid as the rest. Sometimes they are animated.
Animated Tiles
You could add animated tiles for stuff like moving water and boiling
lava.
Lightmap Support
This means object in your game can glow, clouds can cast shadows and
stuff like that. This is made using copyrect masked for a half transparent
white or black shadows or yellow glows. his is also over the head of the
average metal user.
NPCs
Non Player Characters are guys that walk around and talk to you, give
you stuff, get in your way and fight you. They can be good or bad. hey
sometimes can join you in your fighting party if the game is a RPG.
A Battle Engine
An engine just for battle. Usually works like Final Fantasy. You fight
badguys, use items, cast spells or tecs. and fight with weapons.
A Free Map
A map that is larger than the window. It follows the player around.
Cut Scenes
Self explanatory. The player and NPCs fight, talk and move while you
watch.
That pretty well covers all the stuff you can add to a tile engine. Good
luck and happy codeing.
This chapter was written by:
Joseph Duchesne
www.staronesw.com
Send any problems
, comments, thank yous or links to the demo of your game to:
JD@staronesw.com
Credits
|
|