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

1 - Basics

Jake Leveto

2 - Loops and Arrays

Jake Leveto

3 - Simple Graphics and Keyboard Input

Jake Leveto

4 - Sprites

Jake Leveto

5 - Files

Macboy

6 - Resources

Macboy

7 - Quicktime

Macboy

8 - Tile Engine

Joseph Duchense