|
Want to thank me for making this book available for free? Just buy Special Edition Using Macromedia Director MX
Advanced Lingo For Games
| |
| Making the Game
Behaviors are necessary for the frame, paddle, ball, and bricks. Frame BehaviorIn this game, the frame behavior doesn't do as much as in the previous two chapters. It delegates collision detection to the ball sprite, because it is the only object that collides with anything. The frame behavior is mostly concerned with working with the score and the number of lives, or balls, remaining. property pNextLevelFrame property pEndGameFrame property pStartLives global gScore, gLives The on getPropertyDescriptionList handler allows you to set the number of lives that the player starts with. This should only be set for the first level. A value of 0 for the other levels should be used as a signal not to reset the lives and score. Figure 12.2 shows the Parameters dialog box that results.
Figure 12.2
The frame behavior Parameters dialog box.
on getPropertyDescriptionList me
list = [:]
-- number of lives to start with, or 0 if not first level
addProp list, #pStartLives,[cc]
[#comment: "Starting Lives",[cc]
#format: #integer,[cc]
#default: 0]
-- next level
addProp list, #pNextLevelFrame,[cc]
[#comment: "Next Level Frame",[cc]
#format: #marker,[cc]
#default: #next]
-- game over
addProp list, #pEndGameFrame,[cc]
[#comment: "End Game Frame",[cc]
#format: #marker,[cc]
#default: #next]
return list
end
When the frame starts, it determines if the number of lives and the score should be reset.
on beginSprite me
-- if pStartLives is not 0 then set gLives, reset score
if pStartLives <> 0 then
gLives = pStartLives
gScore = 0
end if
showScore(me)
end
The frame behavior is in charge of incrementing the score and also displaying it.
on addPoints me, n
gScore = gScore + n
showScore(me)
end
on showScore me
text = ""
put "Score:"&&gScore&RETURN after text
put "Lives:"&&gLives after text
member("score").text = text
end
When a ball goes past the bottom of the Stage, the "on endLife" handler in the frame behavior is used to subract the life, send the reset message, and also determine if the game is over.
-- missed ball, lose life
on endLife me
gLives = gLives - 1
if gLives < 0 then
go to frame pEndGameFrame
else
showScore(me)
sendAllSprites(#reset) -- send reset to ball
end if
end
When all the bricks are gone, the "endLevel" message is sent to the frame behavior. The purpose for this is to keep the "pNextLevelFrame" property as a frame behavior property rather than separating it from "pEndGameFrame". on endLevel me go to frame pNextLevelFrame end And, of course, the on exitFrame handler keeps the frame looping. on exitFrame go to the frame end Ball BehaviorThe ball sprite behavior is the main behavior in this game. It is in charge of the movement of the ball, and determining if the ball hits anything. The ball sprite behavior checks to see if the ball hits a brick, a wall, or the paddle, and if it falls off the bottom of the screen. Each occurrence has its consequence. property pMoveX, pMoveY -- momentum property pOrigMoveX, pOrigMoveY -- original momentum property pOrigLoc -- original location property pLoc -- current location property pSpeed -- vertical speed property pRadius -- radius of ball property pPaddleSprite -- paddle sprite property pMaxHitSlope -- slope to use when ball hits edge property pPaddleSound, pWallSound The ball behavior has a long list of parameters that can be set when the game is being created. Each of these can vary on different levels because the ball sprite exists individually on these frames. The starting momentum is recorded in the "pOrigMoveX" and "pOrigMoveY" parameters. This is used to start the ball off at the beginning of the level, and is used to start a new ball after the player misses one. The channel number for the paddle sprite is also asked for. The "pMaxHitSlope" determines how steep the angle at which the ball will fly off will be when it hits the paddle. This value is equivalent to the horizontal speed of the ball. The parameter "pSpeed" determines the vertical speed of the ball. Two sounds are also asked for. You can use the same sound for the ball hitting the wall or the paddle, or you can come up with two different sounds. Figure 12.3 shows the resulting Parameters dialog box.
Figure 12.3
The ball behavior Parameters dialog box.
on getPropertyDescriptionList me
list = [:]
-- direction of initial movement
addProp list, #pOrigMoveX,[cc]
[#comment: "Move Horizontal",[cc]
#format: #integer,[cc]
#default: 1]
addProp list, #pOrigMoveY,[cc]
[#comment: "Move Vertical",[cc]
#format: #integer,[cc]
#default: -1]
-- paddle sprite
addProp list, #pPaddleSprite,[cc]
[#comment: "Paddle Sprite",[cc]
#format: #integer,[cc]
#default: 5]
-- how much of an angle will ball fly off at if the
-- ball hits the edge of the paddle
addProp list, #pMaxHitSlope,[cc]
[#comment: "Maximum Hit Slope",[cc]
#format: #float,[cc]
#range: [#min: 1.0, #max: 5.0],[cc]
#default: 3.0]
-- vertical speed of ball
addProp list, #pSpeed,[cc]
[#comment: "Ball Speed",[cc]
#format: #float,[cc]
#range: [#min: 1.0, #max: 8.0],[cc]
#default: 2.0]
-- hit paddle sound
addProp list, #pPaddleSound,[cc]
[#comment: "Hit Paddle Sound",[cc]
#format: #string,[cc]
#default: ""]
-- hit wall sound
addProp list, #pWallSound,[cc]
[#comment: "Hit Wall Sound",[cc]
#format: #string,[cc]
#default: ""]
return list
end
The ball sprite starts off by recording the original position of the ball and getting its radius for future reference in other handlers. It then calls "on reset" to set the location and momentum of the ball. -- record original location, get radius on beginSprite me pOrigLoc = sprite(me.spriteNum).loc pRadius = sprite(me.spriteNum).width / 2 reset(me) end -- start momentum and current position on reset me pMoveX = pOrigMoveX pMoveY = pOrigMoveY pLoc = pOrigLoc end The on exitFrame handler changes the position of the ball and then checks to see if any collisions occur. Four handlers are called to determine collisions. Those handlers directly follow the on exitFrame handler in the script. -- move ball and check for impact on exitFrame me pLoc = pLoc + pSpeed*point(pMoveX,pMoveY) checkHitSides(me) -- hit sides of screen checkHitPaddle(me) -- hit paddle checkMissedBall(me) -- fell through bottom sprite(me.spriteNum).loc = pLoc -- set new location checkHitBricks(me) -- hit a brick end Notice that you can multiply structures like points and rects by regular numbers. The result is the same type of structure, but with each value inside it individually multiplied by the number. So multiplying point(5,9) by 2 results in point(10,18). The "on checkHitSides" handler determines if the ball has hit the top, left, or right sides of the screen. It does a lot more that just detect it, actually. It also computes how much past the edge the ball went{1} and resets its position to match where the ball should be in real life{2}. It then reverses the momentum in that direction.
-- see if the ball hit the left, right or top of screen
on checkHitSides me
amountPastTop = 0 - (pLoc.locV - pRadius){1]
if amountPastTop >= 0 then
if pWallSound <> "" then puppetSound pWallSound
pLoc.locV = pLoc.locV + amountPastTop{2}
pMoveY = -pMoveY
end if
amountPastSide = 0 - (pLoc.locH - pRadius){1}
if amountPastSide >= 0 then
if pWallSound <> "" then puppetSound pWallSound
pLoc.locH = pLoc.locH + amountPastSide{2}
pMoveX = -pMoveX
end if
amountPastSide = (pLoc.locH + pRadius) - (the stage).rect.width{1}
if amountPastSide >= 0 then
if pWallSound <> "" then puppetSound pWallSound
pLoc.locH = pLoc.locH - amountPastSide{2}
pMoveX = -pMoveX
end if
end
The next handler checks to see if the ball has hit the paddle. It uses the Lingo inside function to determine if contact has been made. If there is a hit, the vertical momentum of the ball is reversed. The horizontal momentum, however, is thrown away and totally replaced by a new value depending on where on the paddle the ball hit{3}.
-- see if the ball hit the paddle
on checkHitPaddle me
if inside(pLoc,sprite(pPaddleSprite).rect) then
amountPastPaddle = (pLoc.locV + pRadius) - sprite(pPaddleSprite).rect.top
if pPaddleSound <> "" then puppetSound pWallSound
pLoc.locV = pLoc.locV - amountPastPaddle
pMoveY = -pMoveY
paddleWidth = sprite(pPaddleSprite).rect.width
ballHitSpot = pLoc.locH - sprite(pPaddleSprite).locH
-- send off in a slope relative to how close to the center
-- of the paddle the ball hit
pMoveX = 3.0*ballHitSpot/paddleWidth{3}
end if
end
If the ball falls below the bottom of the screen, it means that the player missed it with the paddle. The ball is lost and the "on endLife" handler in the frame behavior is called.
-- see if ball is below the bottom of the screen
on checkMissedBall me
if pLoc.locV > (the stage).rect.height then
sendSprite(0,#endLife)
end if
end
The goal of the game is to hit bricks. This next handler checks for that. It sends a "checkHit" message to each brick sprite. If any of these messages return a TRUE, then it means that the brick was hit. The brick takes care of removing itself, but the ball behavior must handle reversing the momentum of the ball. This handler also keeps track of the case where no sprites at all return a value. If a brick is hit by the ball, it returns a TRUE. If it has not been hit, but is still on the screen, it returns a FALSE. However, if the sprite is no longer on the screen, or the sprite is not even a brick, it returns VOID. If all inquiries return VOID, then the "someBricksRemain" variable is still FALSE when the loop is done, and the level is over.
-- see if the ball hit a brick
on checkHitBricks me
someBricksRemain = FALSE -- assume all bricks are gone
repeat with s = 1 to the lastChannel
check = sendSprite(s,#checkHit,me.spriteNum)
if check then
pMoveY = -pMoveY -- bounce
else if not voidP(check) then
someBricksRemain = TRUE -- note that at least one brick remains
end if
end repeat
-- if no bricks remain, then level is over
if not someBricksRemain then
sendSprite(0,#endLevel)
end if
end
Brick BehaviorBricks have their own behavior even though they do not move during the game. Using the parameters of this behavior we can make each brick play a different sound, and have each brick worth a different amount of points. The behavior is also in charge of checking to see whether the brick is currently in contact with the ball. If so, the brick should disappear. The on getPropertyDescriptionList handler just needs to find out how many points the brick is worth, and what sound to play when it is hit. Figure 12.4 shows the Parameters dialog box.
Figure 12.4
The Parameters dialog box for the brick behavior.
property pHit -- has brick been hit?
property pSound -- sound to play when hit
property pPoints
on getPropertyDescriptionList me
list = [:]
-- how many points is brick worth
addProp list, #pPoints,[cc]
[#comment: "Points",[cc]
#format: #integer,[cc]
#default: 10]
-- how many points is brick worth
addProp list, #pSound,[cc]
[#comment: "Sound",[cc]
#format: #string,[cc]
#default: ""]
return list
end
-- start off not hit
on beginSprite me
pHit = FALSE
end
The "on checkHit" handler is called by the ball sprite behavior. If this brick determines that it has been hit, then it returns a TRUE after making itself disappear. If the brick is already gone, it returns a VOID instead of a TRUE or FALSE to notify the ball sprite behavior that it is no longer in play{4}.
-- see if the ball is hitting this brick right now
on checkHit me, ballSprite
if pHit then return VOID -- already hit, don't check{4}
if sprite me.spriteNum intersects ballSprite then -- a hit
sprite(me.spriteNum).loc = point(-100,-100) -- remove brick
pHit = TRUE -- remember I'm hit
if pSound <> "" then puppetSound pSound
sendSprite(0,#addPoints,pPoints) -- increase score
return TRUE -- I was hit
else
return FALSE -- I was not hit
end if
end
The brick behavior is also in charge of sending the "addPoints" message to the frame behavior so that the score can be increased. Paddle BehaviorThe frame, ball, and brick behaviors take care of everything in the game except for one aspect: the movement of the paddle. This is simply accomplished by setting the horizontal location of the paddle to the horizontal location of the cursor. -- paddle follows cursor on exitFrame me sprite(me.spriteNum).locH = the mouseH end | |