Tutorial #2: More on Sprite Editing

I had originaly planned to write a 2d tutorial as tutorial #2, but as I was writing it, I realized there were still several important aspects of sprite editing that weren't covered yet are essential to making a decent 2d engine. So consider this the second, more advanced counterpart to tutorial #1.

O.k. So you now have your picture in memory, which you did using the GET statement, and your predefined array to hold it in. That array however, is eniffecient and memory wasting. Why should you worry about memory you ask? Because Qbasic's memory limits are rather low, and if you try to use too many arrays you'll get an OUT OF MEMORY error. When I first started Marconian Dreams, I used the previous method, declaring a 20x20 array to hold each tile. Being as I have over 40 tiles, this quickly gets gobbles up memory. Hmm, we need a better way. After some investigating and a lot of postings on alt.lang.basic, I discovered BLOAD and BSAVE. These handy fuctions save both array space and frustration. You don't have to use DATA statements to load and save the picture, and you don't have to worry about writing a jumble of number coded colors to a data file. So here's how they work.
As in the tutorial before, you first need to draw a picture on the screen, either using DATA statements or otherwise. Then again as before, you have to GET the picture with the GET statement. Now here's where the difference come in. Instead of writing the colors to a data file, and then reading them into an array, do this...

DEF SEG = VARSEG (TileName(0))
BSAVE FileName$, VARPRT (TileName(0)), 404

What was all that? Well basically what it does, is figures out where the variable is stored in the computers memory, points to that spot, and saves what's there for however many bytes specifies. Of course, you replace TileName with the name of the tile you assigned with the GET statement, and FileName$ you replace with the name of the file. The 404 part at the end of the BSAVE statement is the amount o Bytes to save. Basically, the rule of thumb for that number is to use a large number, and if you start running into memory problems, make it smaller (be aware though that if the number is too small, you won't save the whole picture). 404 is about twice the space needed to save a 20 x20 tile, so you can judge how much space your tile or picture needs from there. So lets look at another example now. Say your TileName is RedCross, you want to save the picture to a file named RedCross.bsv (bsv because it's a bsv file, although the extension can be anything you want) and the tile is a 40x40 tile (which is about 4 times as big as a 20x20 tile). Here's the code you would use...

DEF SEG = VARSEG (RedCross(0))            
BSAVE "RedCross.bsv", VARPRT (RedCross(0)), 1212 

Again, this first points to the beginning of the variable RedCross with the DEF SEG = VARSEG line, and then writes the first 1212 bytes of RedCross to the file RedCross.bsv. Ok? Everything clear? Good.

Now that we've saved our cross to a file, how do we load it up again? We use BSAVE's counterpart, BLOAD to load the file, using the same procedure before. First we point to the beginning of RedCross using DEF SEG = VARSEG (RedCross(0)) then we load the file by using the BLOAD statement...BLOAD "RedCross.bsv", VARPRT (RedCross(0)) except this time we don't use the trailing number because we've already defined how big the array was when we declared it. Confused? It'll all be clear once you see the example.

'Program to load RedCross
DIM RedCross(1212) AS INTEGER      'Here we are setting up the array for RedCross
DEF SEG = VARSEG (RedCross(0))     'Point to the beginning of RedCross
BLOAD "RedCross.bsv", VARPRT (RedCross(0))  'Load RedCross into memory

See it's actually pretty easy. Now what if you want to put the tile somewhere now that you've got it into memory? You go back and read the first Tutorial!! Or for those of you who are lazy or have slow modems, you use the PUT statement to put it somewhere. This example loads RedCross into memory, then PUTs it on the center of the screen.

'Program to load RedCross and PUT it on the center of the screen
SCREEN 13                          'We can't do graphics in SCREEN 0!
DIM RedCross(1212) AS INTEGER      'Make an array for RedCross 
DEF SEG = VARSEG (RedCross(0))     'Point to the beginning of the array
BLOAD "RedCross.bsv", VARPRT (RedCross(0))  ' Load RedCross into memory
'Now we put it on the screen...
PUT (170,100), RedCross, PSET      'Put RedCross at coords (170, 100) and overwrite
                                   'anything else on the screen (that's what PSET does)

Voila!! Only one more thing you need to know about Sprite editing, the important subject of masks. Try this. Draw a tile, (it doesn't matter what size it is) and then draw a character the same size as the tile (it can be any character, the more detailed the better, but if you aren't creative a stick figure will do.) Now that you have a character, and a tile, use the PUT statement to PUT the tile on the screen, and then use another PUT statment to put the character on the screen. What happened? If you used PSET at the end of the PUT statment, then your character overwrote the tile, which isn't good of you're trying to make a game (you can't erase a tile every time you take a step!) and if you didn't use PSET, the colors of your character got all messed up. The solution to this annoying and often asked about problem? Create a mask. Masks use a neutral palette color so when you put your character on the tile, he doesn't overwrite it, and the colors don't get messed up. It simply and perfectly puts your character on top of the tile. So how do we make a mask? There are several methods but the one I find easiest and most effecient involoves using the POINTstatement. The POINT statment returns the color of a pixel at a specified coordinate. So if you have a blue dot in the middle of the screen and you did this,
x = POINT (170,100)

x would = 32 (the qb color for blue). What you want to do is get rid of all that black around your character, so it doesn't show up when you put him or her or it (sorry, gotta be p.c.) on the tile. So when the POINT returns 0 (black,) PSET 255. If POINT returns anything else, PSET0. This will create a mask for your character. If you notice, using the method above any black in your picture is erased, so if you intentionally want black, use color 16 instead of 0 (they're the same thing.) That way the black will stay in your picture. As usual it will all make more sense after you see an example.

'Program to make a mask for an already drawn character
DIM Character(202)              'lets assume a 20x20 character
DIM CharacterMask(202)          'An array for the mask of Character
DEF SEG = VARSEG(Character(0))  'points to the beginning of Character
BLOAD "charactr.bsv", VARPRT(Character(0))  ' loads Character into memory
'Now we put Character in the upper left hand corner, so we can alter it for a mask
PUT (0,0), Character, PSET    'make sure Character overwrites anything else 
'Now here's where POINT and PSET come into play
FOR y = 0 TO 19                                 'The tile's length is 20
   FOR x = 0 TO 19                              'The tile's width is 20
    IF POINT (x, y) = 0 THEN                    'Checks to see if the pixel is black 
          PSET (x, y), 255                      'makes all black turn to 255
          ELSE PSET (0)                         'If it isn't black, make it black
    END IF
'Now that the Character has been altered to a mask, we have to save the altered version,
'but first we have to get it into memory. We will save it as Charactr.msk (dos only allows
'a max of 8 letters, + extension) 
GET (0,0)-(19,19), CharacterMask         'Get the Mask into memory 
DEF SEG = VARSEG (CharacterMask(0))      'point to the beginning of CharacterMask
BSAVE "charactr.msk, VARPRT (CharacterMask(0)), 202

There. Now we have the mask all ready to go. Now we need to know how to put the mask on top of the character so it doesn't mess up the colors of the tile underneath. It's easy just do this (assuming you've loaded all the necessary tiles into memory)
PUT (0,0), RedCross, PSET
PUT (0,0), CharacterMask, AND
PUT (0,0), Character, XOR
And there ya go. Now that you're a master of Sprite editing, try writing a program that makes the shole process similar. A good sprite editor should have

If you can do all that, you'll be able to make sprites effeciently and easily. Or you could just download my editor, which has all the above features and more. I'll have it up sometime, I swear!!