Building a Game: Sprite Invaders: Part 2
March 15, 2000
by Gary Rosenzweig
This is the second part in a three-part series of how to make the Sprite Invaders game. Complete code for this game can be found in my new book, "Advanced Lingo for Games," which is now available online and in stores.
Last week we learned how to make the invaders themselves move back and forth and down. This week, we will add a player's ship at the bottom of the screen, allow it to move back and forth, and allow it to fire at the invaders.
To accomplish all of this, we will add two new behaviors and modify the invaders' behaviors slightly. The first new behavior is the one that controls the ship. This should be a graphic placed at the bottom of the screen. Figure 1 shows the invaders and the ship.

A sample movie is available for download in Mac or PC format. This is a Director 7 movie.
The behavior to control the ship will start off by having two parameters set when the behavior is dropped on to the sprite. These two properties will control the speed that the ship can move, and the number of ticks that must pass between the firing of shots.
property pSpeed
property pFireDelay
property pLastFireTime
on getPropertyDescriptionList me
list = [:]
-- how many pixels the ship moves at a time
addProp list, #pSpeed, [#comment: "Speed", #format: #integer, ¬
#default: 5]
-- how many ticks to wait before allowing another bullet
addProp list, #pFireDelay, [#comment: "Delay Between Shots (ticks)", ¬
#format: #integer, #default: 30]
return list
end
The reason that a delay is needed between shots is that if the player can fire as fast as he or she wants, then the invaders don't stand a chance. The player can just mow them down. The delay makes it more challenging.
When the behavior begins, it initializes the pLastFireTime property, which is used to space continuous shots.
on beginSprite me pLastFireTime = 0 end
Most of the work is done by the on exitFrame handler. It uses the keyPressed() function to determine if the arrow keys are pressed or not. It will move the ship in the proper direction, and also restrict its movement so that it stays on the screen.
In addition, if the spacebar is pressed, and enough time has passed since the last shot, then a new bullet is fired. This is simply done by sending a fire message to sprite 6, where the first of the bullet sprites is located.
on exitFrame me
-- left arrow
if keyPressed(123) then
x = sprite(me.spriteNum).locH
x = x - pSpeed
if x < 20 then x = 20
sprite(me.spriteNum).locH = x
end if
-- right arrow
if keyPressed(124) then
x = sprite(me.spriteNum).locH
x = x + pSpeed
if x > (the stage).rect.width-20 then x = (the stage).rect.width-20
sprite(me.spriteNum).locH = x
end if
-- check spacebar, plus check to make
-- sure did not fire last frame
if keyPressed(SPACE) then
-- space, fire
if the ticks < pLastFireTime + pFireDelay then exit
sendSprite(6, #fire, sprite(me.spriteNum).loc)
pLastFireTime = the ticks
end if
end
When sprite 6 gets the signal to fire a bullet, the first thing that the sprite's behavior does is to check to see if it is already moving on the screen. If so, then the pMoving property will be TRUE. In that case, the fire message is simply passed on to the next sprite. That next sprite will also have the same behavior placed on it, so it will attempt to fire. If it is busy too, then the message keeps going until a non-busy sprite gets the message.
Here is the beginning of the code for the bullet behavior. This should be placed on a bank of sprites starting with sprite 6. Each sprite should have a bullet graphic in it, and should be positioned so that it is not visible on the screen initially.
property pSpeed
property pMoving
on getPropertyDescriptionList me
list = [:]
addProp list, #pSpeed, [#comment: "Speed", #format: #integer, ¬
#default: 16]
return list
end
on beginSprite me
reset(me)
end
on reset me
sprite(me.spriteNum).locV = -100
pMoving = FALSE
end
on fire me, loc
-- got signaled to fire
if pMoving then -- busy, send to next sprite
sendSprite(sprite(me.spriteNum+1),#fire,loc)
else
sprite(me.spriteNum).loc = loc
pMoving = TRUE
end if
end
If a bullet is moving, then the on exitFrame will handle that movement. It simply moves the sprite upward. It checks to see if the sprite is above the top of the screen. If so, then the sprite can be reset. Now it will be ready to be used again for the next time the player fires.
on exitFrame me
if pMoving then
-- move bullet up
y = sprite(me.spriteNum).locV
y = y - pSpeed
sprite(me.spriteNum).locV = y
if y < 0 then -- reached top of screen
pMoving = FALSE
else
didIHit(me) -- check for hit
end if
end if
end
The on exitFrame handler also calls on didIHit, which checks to see if the bullet hit any of the invaders. Since the invaders are in sprites 30 to 53, a loop uses the intersects function to see if the bullet hit anything. If so, then a hit message is sent to the invader that was hit. The bullet sprite is then recycled.
on didIHit me
repeat with i = 30 to 53
-- loop through invader sprites
if sprite i intersects me.spriteNum then
-- see if it hit
sendSprite(sprite i, #hit)
-- get rid of bullet
sprite(me.spriteNum).locV = -100
pMoving = FALSE
end if
end repeat
end
The invader behavior from last week now has to be modified to accept the hit message. This new handler will change the invader's member to an explosion graphic and update the Stage so that this explosion is visible for the rest of the frame. It will then remove the sprite altogether.
-- called by bullet behavior to notify of a hit
on hit me
sprite(me.spriteNum).member = member("Invader Hit")
-- change to hit graphic
updateStage
-- show now, since sprite will disappear next frame
sprite(me.spriteNum).memberNum = 0
pHit = TRUE
end
One final modification needed to make sure that the invader knows it is dead is to add this line to the beginning of the on exitFrame handler:
if pHit then exit
All that this does is to abort the invader's movement if the invader no longer exists.
The game is now taking shape. The ship can move back and forth and destroy invaders. The invaders, undaunted, proceed downward. Next week, we will have the invaders fire back, and also add scoring.
More information about Gary Rosenzweig's new book, "Advanced Lingo for Games", can be found at http://clevermedia.com/resources/bookstore/book4.html. It can be purchased there, or in your local bookstore.
Copyright 1997-2025, Director Online. Article content copyright by respective authors.