Introduction: Sprites & Graphics on the Micromite Companion
The Micromite Companion Mini Computer Kit is capable of multiple screen and audio modes.
One of the most compelling of these modes is MODE 4: Sprite and Tile mode.
Mode 4 is capable of:
- Up to 16 16x16 sprites with 16 colors.
- Up to 63 8x8 tiles with 16 colors.
Mode 4 is controlled using flat ASCII files which contain the layout and pixel colors for all sprites and tiles. At the time of writing of this Instructable, there are two sprite file editors, however any of the ASCII files may also be loaded into the program EDITOR of the Micromite for changes.
These ASCII files are:
- .FNT = 8x8 Tile/Font file
- .SPR = 16x16 Sprite file
- .SCR = Screen map for Tiles
The Micromite Companion is already hardware capable of up to 256 colors and multiple color palettes. Software modification for mode4 is expected in the near future to include this expansion.
Step 1: The Sprite Format & Sprite Creation
The sprite file, (.SPR) is a 5k ASCII text file which contains 16 sprites, numbered 0-15.
In ASCII, the sprite looks like this:
' Sprite 0 #0000000000000000 #0000000000F00000 #FFFFF0000FFFF000 #0FFFFF000FF8EEE0 #00FFFFF00FFFEE00 #000FFFFF0FFF0000 #0000FFFFFF000000 #00FFFFFFFF000000 #0FFFFFFFFF000000 #000FFFFF00000000 #000000EE00000000 #0000EE0000000000 #0000000000000000 #0000000000000000 #0000000000000000 #0000000000000000
Each line of 16 pixels is preceded by a "#" hash-tag.
Each pixel of the 16x16 sprite is represented by an single character, 0-9, A-F.
These hexadecimal, single digit numbers determine the color of each pixel in the sprite.
In the case of the sprite above, the 0 is transparent, then 8, F, and E are used.
At the time of this Instructable, there are three ways to edit sprite ".spr" files:
- Load the sprite into the Micromite editor like a .BAS program.
- Edit the sprite on the Micromite Companion with SPREDIT Sprite Editor.
- Edit the sprites online with your web browser.
Compare the ASCII version of the sprite with the first image. They are the same sprite.
Step 2: Creating Fonts and Tiles
The font/tile file, (.FNT) is an 8k ASCII text file which contains 63 font/tile characters, numbered 0-62.
In ASCII, a font/tile character looks like this:
'' TILE 11 = A #000FFF00 #00FF0FF0 #0FF000FF #0FF000FF #0FFFFFFF #0FF000FF #0FF000FF #00000000
Each line of 8 pixels is preceded by a "#" hash-tag.
Each pixel of the 8x8 font/tile is represented by a single character, 0-9, A-F.
These hexadecimal, single digit numbers determine the color of each pixel in the character.
If you look carefully, you can see the upper case A in white "F" pixels.
Tips for creating great fonts/tiles:
While ANY character in your font/file .FNT file may be altered, it's generally a good idea to leave tile zero (the first tile) set as all 0's. This tile is used as "space" and may make your program look strange if it contains pixels.
- Tiles 1 - 10 are the numbers zero through nine.
If you want a scoreboard, then leave these alone.
- Tiles 11 - 36 are UPPERCASE characters A-Z.
If you want in game messages, then leave these alone.
- Tiles 38 - 62 are lowercase characters a-z.
These are the tiles frequently used for designing 8x8 graphics tiles.
At the time of this Instructable, there is two ways to edit font/tile ".FNT" files:
- Load the .FNT file into the Micromite editor like a .BAS program.
- A "work in progress" update to SPREDIT called GRPHEDIT can be used on the MMC.
Step 3: Creating Font/Tile Screen Maps
Once you've created your font/tile characters in the last step, you will probably want to create your own screen maps, (.SCR) files which will place the tiles a screen at a time. It is possible it display any single font/tile or group of font/tiles on the screen from BASIC, but screen maps allow you to design creatively.
The screen/map file, (.SCR) is a 1k ASCII text file which contains 31 lines of 28 characters, preceded by a #.
In ASCII, a screen/map character looks like this: (scroll page)
# # # # # # # # # # # # # # # # # # # # # # # # aaaabc # aaabc # bc # d bc # aaaaaaabc # bc # bc #aaaaaaaaaaaaaaaaaaaaaaaaaaaa
You'll note the use of the lower case characters a,b,c, and d. I've defined the lower case a as a brick. The letters b & c have become the left and right sides of my ladders, and finally the d is an 8x8 gold chest.
Compare the .SCR file with the image and you'll see the same map.
At the time of this Instructable, there is only one way to edit screen map ".SCR" files:
- Load the .SCR file into the Micromite editor like a .BAS program.
If you are having trouble picturing how the ASCII image above becomes the image, here's the characters a-d of my current font/file .FNT file.
'' TILE 38 = a = red brick #22222022 #22222022 #22222022 #00000000 #22022222 #22022222 #22022222 #00000000 '' TILE 39 = b = ladder left #00FF0000 #00FFFFFF #00FFFFFF #00FF0000 #00FF0000 #00FFFFFF #00FFFFFF #00FF0000 '' TILE 40 = c = ladder right #0000FF00 #FFFFFF00 #FFFFFF00 #0000FF00 #0000FF00 #FFFFFF00 #FFFFFF00 #0000FF00 '' TILE 40 = d = gold chest #0E0000E0 #00E00E00 #66666666 #6EEEEEE6 #6EEEEEE6 #6EEEEEE6 #6EEEEEE6 #66666666
Step 4: Mode 4 Screen Resolution
The visible screen resolution of Mode 4 is 224 x 256.
The means that you can have up to
- 28 8x8 tiles across the screen. (224/8 = 28)
- 32 8x8 tiles down the screen. (256/8 = 32)
Tile placement is 0 - 27 across the screen & 0 - 31 down the screen.
Sprites are visible when placed from 16 - 224 across the screen.
Sprite positions -16 and 250 are hidden spots on the left and right of the screen allowing you to smoothly move a sprite from on screen to off screen.
Sprites are visible when placed from 0 - 224 down the screen.
Sprite positions -16 and 250 are hidden spots on the top and bottom of the screen allowing you to smoothly move a sprite from on screen to off screen.
Step 5: Let's Talk About Basic Debugging
Before we jump into sprites and tiles, let's talk a little about basic debugging.
MODE 4, Sprite and Tile mode can be challenging, especially for new (and returning) programmers to BASIC. Because it primarily uses an I2C communication interface between the Micromite and the Propeller, old debugging tricks like embedding PRINT statements, TRON/TROFF commands or even just seeing the text ERROR message from the Micromite when it crashes are almost impossible to see.
You can open a window to all of the background console communication and text messages by adding an additional terminal to the RX/Gnd lines of your Micromite's breadboard. Using either a "Propplug", "FTDI Friend", or even an one of those very inexpensive "USB-to-ttl-serial" adapters, make connections between RX and Gnd/Vss as indicated in the first picture. While almost any FTDI-to-USB adapter will work, I'd recommend the purchase of the more expensive Propplug from Parallax.com as you'll also be able to update the Micromite Companion's firmware quickly, or even hack around in it's Spin source code.
Next install a copy of Putty on your PC, and set it's communication parameters to the COM port assigned to your USB-to-ttl-serial device with a baud rate of 38400. Now you'll have that extra window to debug your program using the TRON/TROFF (page 47 of the Micromite Manual) commands, print statements, and even program errors.
Step 6: Designing and Working With Sprites
Let's jump in by working with some sprites.
You'll want a couple files added to your Micromite Companion SD card before you begin.
Download the following to your SD card:
After saving these files to your SD card (and reviewing the instructions on the SPREDIT.BIN), boot up the Micromite Companion and CTRL-B SPREDIT to "Brun" the Sprite Editor. Once the sprite editor is up and running, press L and type DEMO.SPR to load the sample sprites into memory.
The sprite editor is capable of working with 16 sprite .SPR files. These sprites are numbered 0-15.
As you PAGE-UP/PAGE-DOWN through the sprites, you'll see you have three animated sprites, each consisting of four frames each to create animation.
Feel free to make some changes, exploring the functions of the sprite editor, then use S and type the name of the file (DEMO.SPR) to save your changes back to SD.
Next, press the reset button on your Micromite Companion and type EDIT to begin creation of your own BASIC program using these sprites.
All mode 4 BASIC programs should start with the following four lines:
I2C open 400,100 PC = &h42 I2C write PC,0,3,1,2,4 'Switch to mode4.mde DO WHILE ASC(a$) <> 42 : a$=INKEY$ : LOOP
These lines do the following:
I2C open 400,100 Opens the I2C communication channel
PC = &h42 Creates a variable PC which we'll use as "shorthand" later in I2C commands.
I2C write PC,0,3,1,2,4 Commands the Propeller to switch to mode 4 when the program is run.
DO WHILE ASC(A$) <> 42 : A$=INKEY$ : LOOP Tells the Micromite to wait for the Propeller to switch modes.
Next, insert several blank lines to create some work space which we'll come back to and create the following subroutines;
SUB LoadResource file$,cmd I2C WRITE PC,0,2,1,230 'Clear the I2C registers FOR X = 1 TO LEN(file$) 'Send filename I2C WRITE PC,0,2,x+1,ASC(MID$(file$,x,1)) : NEXT X I2C WRITE PC,0,2,1,cmd 'Initate Load 180 or 181 a$ ="": DO WHILE a$ <> "*" : a$=INKEY$ : LOOP : PAUSE 1000 END SUB SUB LoadSpr sprite,graphic,x,y,mirror,palette I2C WRITE PC,0,8,1,202,sprite,graphic,x,y,mirror,palette END SUB
There are several very handy subroutines which we'll provide you for your programs, but in the spirit of keeping things simple, let's start with the very first couple.
Next, go back to a line below the DO WHILE ASC(A$) <> 42..... and type in the following:
LoadResource "demo.spr",180 LoadSpr 1,4,100,175,0,0
The LoadResource subroutine will load your DEMO.SPR file into memory at first run of your program.
The LoadSpr subroutine will place a sprite 1,{sprite 4}, at screen position 100,175
Press F2 to run your program. Hit CTRL-C, then F4 to come back to the editor.
Try changing the variables 4, 100, and 175 and take and re-run to see the results.
The subroutine LoadSpr can be called repeatedly when you need to update the position of a sprite.
Consider the following example:
For x = 100 to 150 LoadSpr 1,4,x,175,0,0 pause 10 Next x
Generally, when you write a game, you'll have a "master game loop" in which your LoadSpr commands are updated at each pass of the loop, updating the positions of your sprites.
Step 7: Animating Sprites
Toward the end of the last page, we began moving our sprite balloon around the screen using a FOR/NEXT loop.
This provides the basic "life" for our sprite, but now let's take things a step further.
Let's do what Disney animators have done for years. By quickly flipping the sprite itself to additional frames, we can create the illusion of movement and animation. If you've peeked at the DEMO.SPR file, you actually have four different balloons, starting at sprite 4, each having a small advancement.
Add the following new subroutines to the bottom of your program under the LoadSpr command..
Sub Animate sprite,start,end,delay I2C WRITE PC,0,6,1,219,sprite,start,end,delay End Sub Sub MoveSpeed sprite,xdelay,ydelay,xinc,yinc I2C Write PC,0,7,1,220,sprite,xdelay,ydelay,xinc,yinc End Sub
These two new subroutines allow you to animate and move sprites in "auto-pilot" mode. These commands are perfect for situations when you need background action to happen automatically. Like the rolling logs in the game Pitfall, or the swimming sharks in the game Jungle Hunt.
Let's set our Balloon in motion.
Add the following commands below the LoadSpr 1,4,100,175,0,0 line in your program:
Animate 1,4,7,30 'Animate Balloon - frames 4-7, speed 30 MoveSpeed 1,3,5,1,-1 'Put the Balloon in motion
Our balloon is now animating sprite frames 4-7 at speed 30, and travelling across and upward!
The Animate command is probably very clear.
Animate Program Sprite, start sprite image, end sprite image, speed.
The MoveSpeed subroutine takes a little closer examination.
MoveSpeed Program Sprite, xdelay, ydelay, xinc, yinc
Xdelay and Xdelay can be set with speeds between 0 and 50. (0 being stopped, and 50 is slowest)
Xinc and Yinc is usually set to anything between -2..-1..1..2.
XINC, -1 is left of current position, 1 is right of current position by pixel
YINC, -1 is up 1 pixel from position, 1 down 1 pixel from current position.
The higher the numbers used in Xinc and Yinc, the faster the sprite will move.
Again, both of these commands are ideal when sprites need to be part of the background.
Notice how the balloon simply wraps around the screen when it reaches the edge?
Step 8: Animating Sprites Using Directed Animation
On the last page, we did some simple animation and movement using the Animate & MoveSpeed subroutines.
Let's take a moment to talk about directed animation.
Directed animation is what you'll use when moving the primary sprites in your program.
At the start of your program, you decided some a starting point for your "hero" sprite.
It'll look something like this:
player_x=100 player_y=200
You'll also need to set a sprite frame variable for your sprite to animate:
player_frame=4
Next, you'll begin your main program loop where the actual action will take place.
start: player_frame=player_frame+1 If player_frame > 7 then player_frame = 4 LoadSpr 1,player_frame,player_x,player_y,0,0 pause 100 goto start
This is the start of directed animation. Adding game controller code to update the player's x and y positions will start you on your way to creating a great game or demo.
Take a look at the typical flowchart for a small game I've attached to this page.
Step 9: Hiding Sprites From View
There are times when you need to remove sprites from view. Remember the "Screen Resolution" image from Page 4? There are invisible locations around the edges of the screen.
A handy way to hide sprites is place them into one of these areas.
Here's a subroutine which you can add to your program that will do just that.
Sub HideSpr sprite I2C WRITE &h42,0,5,1,202,sprite,0,0 End Sub
This subroutine may be called on to hide multiple sprites at this location.
Sprites will stack on top of each other nicely.
Sprites which have a higher sprite number will appear above those with lower numbers.
Step 10: Sprite Subroutines Summary
Here is a summary of the sub routines we've used to display, animate, and move sprites:
SUB LoadSpr sprite,graphic,x,y,mirror,palette I2C WRITE PC,0,8,1,202,sprite,graphic,x,y,mirror,palette END SUB Sub HideSpr sprite I2C WRITE &h42,0,5,1,202,sprite,0,0 End Sub Sub Animate sprite,start,end,delay I2C WRITE PC,0,6,1,219,sprite,start,end,delay End Sub Sub MoveSpeed sprite,xdelay,ydelay,xinc,yinc I2C Write PC,0,7,1,220,sprite,xdelay,ydelay,xinc,yinc End Sub SUB LoadResource file$,cmd I2C WRITE PC,0,2,1,230 'Clear the I2C registers FOR X = 1 TO LEN(file$) 'Send filename I2C WRITE PC,0,2,x+1,ASC(MID$(file$,x,1)) : NEXT X I2C WRITE PC,0,2,1,cmd 'Initate Load 180 or 181 a$ ="": DO WHILE a$ <> "*" : a$=INKEY$ : LOOP : PAUSE 1000 END SUB
Step 11: Working With Font and Tiles
Up to this point, we've done a lot of work with sprites. Let's shift gears and look at fonts and tiles.
You might be asking yourself, What's the difference?
- Sprites are 16x16 pixel objects which may be placed at any pixel position (X & Y) of the screen.
- Tiles are 8x8 pixel objects which may be placed at any 8x8 position of the screen.
In other words, you could use tiles as small sprites and move them around the screen, but the movement animation wouldn't be as nearly as smooth as actual sprites as they can only move across the screen at 8 pixels per movement. Remember tiles may be placed from positions 0 = 27 across the screen, and 0-31 down screen.
Tiles most useful for displaying text information to the player, scoreboards, or creating backgrounds for the sprite to live in. In the case of my Loderunner demo which you've seen screenshots of, the tiles are used for the creation of the bricks and ladders which my hero sprite runs and climbs on.
If you haven't downloaded a copy of GRPHEDIT.BIN and added it to your SD card of your Micromite Companion, you'll want to do so. It is an expanded version of SPREDIT.BIN which includes the ability to load font/tile .FNT files. Use (M) to switch from sprite edit mode to tile edit mode, then use (G) to select a 16x16 group of tiles to edit using the cursor keys. The idea here is to provide you an easy way to create 16x16 tile blocks (using 4 characters) to create larger, interlocking graphics for your background if desired.
You can edit any tile in the .FNT file, but generally it's a good idea to stay with the characters after the UPPERCASE characters, (lowercase a-z) as they leave text for information in your game, and numbers for your scoreboard.
There are two handy subroutines for displaying fonts and tiles on the screen.
SUB DisplayText textx,texty,color,text$ FOR x = 1 TO LEN(text$) : char = ASC(MID$(text$,x,1)) I2C write PC,0,6,1,200,textx+x,texty,color,char PAUSE 1 :NEXT x END SUB SUB DisplayChar charx,chary,color,char I2C write PC,0,6,1,200,charx,chary,color,char END SUB
The DisplayText subroutine will allow you to place a string or line of text at any X and Y position.
Examples:
DisplayText 10,5,6,"Hello World" Displaytext 10,5,6,A$
The DisplayChar subroutine does the same thing using a number.
Example:
DisplayText 2,1,5,"SCORE" DisplayChar 11,1,6,score
These routines are also very useful for debugging your programs.
Consider the following:
DisplayText 2,3,6,"SPRITE X" DisplayText 2,4,6,"SPRITE Y" DisplayText 11,3,6," " DisplayText 11,4,6," " DisplayText 11,3,6,STR$(player_x) DisplayText 11,4,6,STR$(player_y)
Step 12: Tile Detection
It's time to make the magic happen. Tile detection.
Tile detection allows us to "know" what fonts and tiles are placed a specific positions. Remember Mario Bros on the Nintendo? How does the game know when Mario is running on solid ground, or falling into a pit?
Again, we have a simple subroutine (actually a function) which will help us read the tiles and know what has been placed around us. Here's the routine:
Function Tiledetect(xpos, ypos) I2C WRITE PC,0,4,1,255,xpos,ypos Pause 1 I2C WRITE PC,0,1,&h0 I2C READ PC,0,1,TileDetect End Function
Tiledetect can be used to return the value of any tile at any X and Y position.
How sprites use Tiledetect:
Now, take a deep, cleansing breath because I'm going to show you how to make 16x16 sprite interact with the 8x8 tile world.
Take a look at image one that I've attached to this page. Notice how my sprite is actually standing mostly over two different 8x8 brick tiles. My sprite's actual position on the screen is around X-30, and Y-240. We need to convert these pixel positions into 8x8 sprite locations. Here's how we do it in BASIC.
INT(player_x/8) and INT(player_y/8+1)
30 / 8 = 3.76 (INT shortens the number to 3)
240 / 8 = 30
Dividing the pixel position of the sprite's X and Y coordinates gives us "very close" 8x8 tile positions for our sprite.
Many times the division of a number will create a fractional number, so INT will truncate an number to the next whole number less than or equal to the argument.
Let's quiz a little and see if you understand yet...
What if we need to know what tiles are below the sprite? Like the brick floor in Loadrunner?
INT(player_x/8) 'Sprite starting position.
INT(player_y/8+2) 'Sprite starting position +2 (2*8) below.
Sometimes it's handy to use the DisplayText & DisplayChar from the last page to place this information at the top of the screen while you are creating your game. It'll help you get a feel for what is happening while your sprite is moving around the screen.
Step 13: Putting It All Together
This Instructable has provided all of the basic tools needed to work with MODE 4 sprites and tiles.
It is this author's opinion that Sprite and Tile based games are more magical and compelling that the best modern 3D shooters because "retro" games depended on the player's mind and imagination to fill in the blanks. When I play Zork, I can see the underground cavern in my mind, and I guarantee the resolution of my imagination is much higher than Oculus Rift.
Here's a template of the opening code to switch modes and all of the subroutines I've discussed in this text. All that remains now is for you to open the door to imagination and adventure and write a great program.
Your quest awaits....
I2C open 400,100 PC = &h42 I2C write PC,0,3,1,2,4 'Switch to mode4.mde DO WHILE ASC(a$) <> 42 : a$=INKEY$ : LOOP REM ** YOUR GREAT GAME BEGINS HERE ** Sub LoadSpr sprite,graphic,x,y,mirror,palette I2C WRITE PC,0,8,1,202,sprite,graphic,x,y,mirror,palette End Sub Sub HideSpr sprite I2C WRITE &h42,0,5,1,202,sprite,0,0 End Sub Sub Animate sprite,start,end,delay I2C WRITE PC,0,6,1,219,sprite,start,end,delay End Sub Sub MoveSpeed sprite,xdelay,ydelay,xinc,yinc I2C Write PC,0,7,1,220,sprite,xdelay,ydelay,xinc,yinc End Sub Sub LoadResource file$,cmd I2C WRITE PC,0,2,1,230 'Clear the I2C registers FOR X = 1 TO LEN(file$) 'Send filename I2C WRITE PC,0,2,x+1,ASC(MID$(file$,x,1)) : NEXT X I2C WRITE PC,0,2,1,cmd 'Initate Load 180 or 181 a$ ="": DO WHILE a$ <> "*" : a$=INKEY$ : LOOP : PAUSE 1000 End Sub SUB DisplayText textx,texty,color,text$ FOR x = 1 TO LEN(text$) : char = ASC(MID$(text$,x,1)) I2C write PC,0,6,1,200,textx+x,texty,color,char PAUSE 1 :NEXT x End Sub SUB DisplayChar charx,chary,color,char I2C write PC,0,6,1,200,charx,chary,color,char End Sub