Sprite Magazine Object
May 9, 2000
by Robert Wingate
You've decided you want to write a shoot-em-up game. And of course, you want to give the player an unlimited supply of rapid-fire ammunition. Or maybe you want to throw endless softballs at a target, trying to dunk the guy into the water tank. Perhaps you need to funnel random groceries in and out of your game screen on a conveyor belt, while the player sorts them into the proper grocery bags. For each of these situations, you essentially want the effect of an infinite reserve of sprites with similar functionality, given the assumption that one bullet, softball, or grocery item uses one sprite. You need a sort of self-replenishing sprite cornucopia. How can you accomplish this? Easy! Just enable all 1000 sprites in the Movie Properties dialog box, assign 995 of them to your ammunition castMember, and hope you don't run out of sprites.
Not!
You can do it much more economically. What you really need is to remember a small range of sprites, and be able to know at any instant which ones are busy and which ones aren't. Then, when you need a new ammunition sprite, you can pull one from the pool of sprites not in current use. After that ammo is "spent", you can return it to the pool of empty sprites. You'll want to write a sprite "magazine" object.
With a sprite magazine in your toolbox, you can achieve the effect of unlimited, self-replenishing sprites, while in fact only using a few.
This article's demo movie uses two variations of the Sprite Magazine premise: one that handles sprites individually (the one we'll discuss here), and another that handles sprites in pairs. We use the first to generate "stars" for that warp-speed outer space effect, and the second to achieve dual photon-torpedo blasts. The star effect uses a total of five sprites, and the laser blasts only use four.
D7 Download for Mac or Windows.
Sprite Magazine 101
So let's write one. As it applies to this demo movie, we'll first set up a simple way to define a contiguous range of sprites to use for the stars. We'll use two sorted linear lists; one will hold references to empty sprites, and the other will hold references to sprites currently in use. We'll name them lsEmptySprites and lsSpritesInUse. We'll also want to remember the first and last sprites in the range. We'll wrap this information into object properties; more on that in a moment. For now, let's look at exactly what we're keeping track of:
-- Sprite Magazine Object
property lsSpritesInUse -- sorted linear list; sprites in current use
property lsEmptySprites -- sorted linear list; sprites currently waiting for use
property kFirst -- integer constant; first spriteNum in range
property kFinal -- integer constant; final spriteNum in range
So as an example, let's initialize our star sprite magazine to use sprites 3 through 7. We'll call the object instance 'oStarMag':
global oStarMag
on startMovie
oStarMag = new( script "SpriteMagazine", 3, 7 )
end
To begin, we pass the integer values that represent the sprite range to the object's 'new' method, and then initialize the lists. We'll fill the empty-sprite list with the entire range:
on new me, firstSprite, finalSprite
kFirst = firstSprite
kFinal = finalSprite
lsSpritesInUse = []
lsEmptySprites = []
repeat with i = kFirst to kFinal
lsEmptySprites.add( i )
end repeat
sort lsSpritesInUse
sort lsEmptySprites
return me
end
After instantiation, the lists would then hold these values:
lsSpritesInUse = []
lsEmptySprites = [ 3,4,5,6,7 ]
When we need a new sprite for the next star, we grab the lowest number from lsEmptySprites, and pop it into lsSpritesInUse. We use that value as our new sprite number, and assign it to the star castMember. Let's add a method for this capability:
on mStartUsing me
if lsEmptySprites.count = 0 then return #allSpritesCurrentlyInUse
spr = min( lsEmptySprites )
lsEmptySprites.deleteOne( spr )
lsSpritesInUse.add( spr )
return spr
end
Whenever the star sprite realizes that it has flown offstage, it sends a message back to the sprite magazine, telling it to put its sprite number back into the pool of empty sprites:
on mStopUsing me, whichSpr
lsSpritesInUse.deleteOne( whichSpr )
lsEmptySprites.add( whichSpr )
end
That's really all there is to it. For enhanced functionality, I've added methods that return either how many sprites are in use, or exactly which ones are being used:
on mCount me -- public accessor
return lsSpritesInUse.count
end
on mGetSpritesInUse me -- public accessor
return lsSpritesInUse
end
This parent script, despite being possibly the simplest on the planet, is particularly dear to me: I wrote it years ago, and it was the first OOP I ever wrote. It was the catalyst that really helped me to understand what OOP is and to clarify what it's useful for.
The reason is simple: while it would be easy to implement this functionality into your movie by using two global lists, what if you need multiple sprite magazines? What if your game requires not only unlimited stars and ammunition, but also another reserve for explosions that appear as a result of flying ammo? You could possibly accomplish all this using nested lists of sprite groups in your global lists, but it could quickly get cumbersome.
Faced with a situation like this, I realized that it would be far simpler if I encapsulated the lists and functions together into a single script, and have it return a pointer to itself whenever I needed another sprite reserve. And "click", the OOP light switched on.
Play a full-featured version of the game here (made for broadband; a hefty download).
Copyright 1997-2024, Director Online. Article content copyright by respective authors.