Lesson 16: Sprites and Sprite Sheets
A sprite is nothing more than an image that can be manipulated around on the computer screen. Sprites usually come accompanied with a background that is either transparent or opaque in color to use as transparency and are typically small in size with a known width and height dimension. More often than not sprites are used for character animation by showing a sequence of sprite cells in series. There are a number of different approaches to loading and displaying sprites. The first approach, as illustrated in the next example program, loads a number of individual images into an array to be displayed in order to create a walking character demonstration. The following code is included in your .\tutorial\Lesson16\ directory as WalkDemo1.BAS.
Figure 1: Sprite animation
The program above loads 6 individual images, walk1.PNG through walk6.PNG, and saves them to the Walker&() array as seen in lines 17 through 22 of the code. The PNG images do not contain an alpha channel so line 21 identifies the color to use for transparency in each image.
In the main loop of the program the images are simply displayed in sequence as the loop progresses. An integer variable named Sprite% is consistently incremented from 1 to 6 identifying the next sprite image to display. In lines 55 through 58 modulus division is used to increment the Sprite% variable every 5th frame. Since the main loop runs at 30 frames per second the character animation needs to be slowed down otherwise the character would look like The Flash dashing around. This effectively slows the character animation down to 6 frames per second while still allowing the main loop to run at 30 frames per second.
Lines 47 through 52 examines the direction the character is traveling in and either displays the sprite as loaded for right hand motion or flipped horizontally for left hand motion. By using _PUTIMAGE to flip the sprites the program effectively has 12 sprite images it can display instead of the original 6 loaded.
Loading individual images is perfect for games with a small number of sprites which helps to keep the asset file count low. However, for large projects that require many sprites, say for a game like Super Mario, there would be hundreds, perhaps even thousands, of individual image files to keep track of. This is where a sprite sheet can help.
A sprite sheet is a collection of individual sprite images contained in an ordered fashion on a larger image. By placing the individual images onto a larger sheet only one image needs to be loaded from disk. From there the individual images can be parsed out by using the _PUTIMAGE statement. The example program above has been modified to load the walking sprites from a master sprite sheet called walksheet104x156.PNG. The 104x156 in the name of the sprite sheet image file is used to identify the size of the individual sprite images contained on the sheet. This is nomenclature I have developed and used over the years while working with sprite sheets. The next example program is called WalkDemo2.BAS and is located in your .\tutorial\Lesson16\ directory.
The sprite sheet used in the example above
Each sprite on the sheet is 104 by 156 pixels in size and by using some simple math and the _PUTIMAGE statement each sprite can be copied and pasted to another internal image within the code. Lines 16 through 21 in the above example does this. Through each loop of the FOR...NEXT statement the location of the next image is calculated and copied into the appropriate Walker&() array index by the _PUTIMAGE statement. This method of loading one master image and then extracting each to a separate image still creates many images that need to be manipulated in code.
There is another way to use sprite sheets where the sprite sheet itself is the only image used. Whenever one of the sprites on the sheet need to be displayed the coordinates on the sheet can be referenced and _PUTIMAGE used to copy and paste directly to the screen. The next example code shows how this can be done. It is saved as WalkDemo3.BAS in your .\tutorial\Lesson16\ directory.
In this example the Walker() array is used to hold the coordinates of each individual image on the sprite sheet. When a sprite needs to be displayed these coordinates are referenced by _PUTIMAGE instead of using an individual image file. The _PUTIMAGE statements in line 57 and 61 show how this is done. With this example the only image ever loaded and referenced is the sprite sheet.
The method chosen to use when dealing with sprites and sprites sheets is completely up to the programmer. Personally I always lean toward the second example because I prefer to have all my sprites preloaded into RAM as individual images ready for use. However, this method does take a considerable amount of RAM when dealing with hundreds or thousands of sprites. You also need to keep in mind that all of those images need to be removed from RAM before ending the program.
There's a fourth method where images can be referenced from a sprite sheet without even using an array to hold the coordinates. Very large sprite sheets can have their individual sprites referenced by a number that can be used to calculate the x,y location of the sprite within the sheet. Figure 2 below shows a sprite sheet that contains 368 individual sprites.
Figure 2: That's a lot of sprites!
Note: The numbers have been placed on top of the individual sprites for reference only. The original sprite sheet named mario32x32.PNG does not contain the numbers.
Using this image as a guide any of the sprites can be referenced as a number from 1 to 368. The following example code shows how this can be done. It is saved as NumberedSheet.BAS in your .\tutorial\Lesson16\ directory.
Figure 3: Accessing sprites by number
Using code in this manner eliminates the requirements for any sort of an array or secondary images all together. The only image required is the sprite sheet itself. Lines 16 through 22 of the code performs some simple calculations to determine the X and Y coordinate locations of any sprite contained on the sheet.
There is one major drawback to this method however. The sprite sheet has to have sprites that are all the exact same size because the number of sprite columns needs to be known beforehand for the calculations to work. The number of columns is calculated from the width of the sprite sheet divided by the width of an individual sprite as seen in line 8 of the example code.
Sprite sheets are a great way to quickly and easily enter sprites and animations into your games. If you do a Google search for "Sprite Sheet" you'll find thousands of pre-made sheets to choose from or create your own with your favorite graphics program.
Something you may have been wondering up to this point is, "Why is the transparent color used always bright magenta (255, 0, 255)?" Simple, it's a garish eye hurting color that no game programmer in their right mind would ever use in their game. Honestly, that's why. That, and it's easy to remember (255, 0, 255).