Articles Archive
Articles Search
Director Wiki
 

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.

Gary Rosenzweig is the Chief Engineer, founder, and owner of CleverMedia, a game and multimedia development company in Denver, Colorado. He is the author of ten books on Macromedia Director and Flash, including his latest, Special Edition Using Director MX.

Copyright 1997-2024, Director Online. Article content copyright by respective authors.