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-2024, Director Online. Article content copyright by respective authors.