|
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
In the last chapter, we had one behavior that was used on each sprite in the game. In addition, we had a simple frame behavior that simply looped on the frame. In this chapter, we'll do the opposite. The frame behavior will actually be the main piece of code, controlling the game play. The sprites, in turn, will have a simple behavior on them that just passes mouse clicks to the frame behavior. This all-powerful frame behavior starts off as any other behavior, with a list of properties. The first six are properties that are set when the behavior is assigned to a frame in the Score. The rest are listed here with descriptions. -- Settable Properties property pSpriteOffset, pDisplayDelay, pGameOverFrame property pClickSound, pMatchSound, pNoMatchSound property pNumberOfCards -- number of cards in the game property pBoard -- holds a list of what cards are where property pCard1 -- the first card in a pair clicked on property pCard2 -- the second card in a pair clicked on property pCardTimer -- the time that the second card was clicked The on getPropertyDescription handler returns the information you want displayed in the behavior inspector. on getPropertyDescription me text = "Used on a frame script to make a matching game."&RETURN put "Will shuffle a list of cards from the 'Cards' cast library"&RETURN after text put "and will assign them to certain sprites based on the sprite offset."&RETURN after text put "Will allow the user to click on a pair of sprites and"&RETURN after text put "then remove them if they refer to the same card."&RETURN after text return text end The on getPropertyDescriptionList behavior is much like the one from the previous chapter. One difference is the way we set a range for the "pDisplayDelay" property. Instead of just allowing the author to type in a number for this property, we are going to have a small slider in the behavior Settings dialog box. You do this by including a #range property, with a list that has a #min and a #max value. The result is the slider seen in Figure 4.3.
Figure 4.3
The Parameters dialog box for the Matching Game Frame Behavior, which includes a slider for one of the value.
on getPropertyDescriptionList me
list = [:]
-- pSpriteOffset is used to determine how far from
-- the top of the Score the first card is
-- so, if the first card starts in channel 11,
-- the offset would be 10 (10 away from channel 1).
addProp list, #pSpriteOffset,[cc]
[#comment: "Card Sprite Offset",[cc]
#format: #integer,[cc]
#default: 0]
-- pDisplayDelay is how many ticks the pair of
-- cards will be kept on the screen for the player
-- to see before they are removed or turned back over
addProp list, #pDisplayDelay, [cc]
[#comment: "Display Delay",[cc]
#format: #integer,[cc]
#default: 60,[cc]
#range: [#min: 0, #max: 240]]
-- when a sprite is clicked, what sound is played?
addProp list, #pClickSound,[cc]
[#comment: "Click Sound",[cc]
#format: #string,[cc]
#default: ""]
-- when a sprite is matched, what sound is played?
addProp list, #pMatchSound,[cc]
[#comment: "Match Sound",[cc]
#format: #string,[cc]
#default: ""]
-- when a sprite is not matched, what sound is played?
addProp list, #pNoMatchSound,[cc]
[#comment: "No Match Sound",[cc]
#format: #string,[cc]
#default: ""]
-- when all sprites are matched, which frame should the movie go to?
addProp list, #pGameOverFrame,[cc]
[#comment: "Game Over Frame",[cc]
#format: #marker,[cc]
#default: #next]
return list
end
Instead of using the #string format for the sound parameters, you could use the #sound format. This presents a pop-up menu of all the sound members in the movie. However, this would also force you to use assign a sound always. Using the #string format is not as convenient, but it does allow you to leave the entry blank, indicating that you do not want to use a sound. The on beginSprite handler is the one that needs to shuffle the cards to provide a random layout each time the game is played. It starts by getting the number of members in the "Cards" cast library{1}. It then adds each card twice to a list{2}. Then, a random item from this first list is taken and put into a new list{3}, the "pBoard" property. When all the items from the first list have been randomly shuffled into the second, the game is ready to begin.
-- shuffles the cards to build the pBoard list
-- initializes the game properties
on beginSprite me
-- refers to the "Cards" cast library to see how many cards there are
pNumberOfCards = the number of members of castLib "Cards"{1}
-- build a list with each card in the list twice
list = []
repeat with i = 1 to pNumberOfCards{2}
add list, i
add list, i
end repeat
-- fill the pBoard list up randomly with items from
-- the previously created list
pBoard = []
repeat while list.count > 0
r = random(list.count)
add pBoard, list[r]{3}
deleteAt list, r
end repeat
-- initialize the game properties
pCard1 = 0
pCard2 = 0
end
Each time a sprite is clicked, it sends a "turnCard" message to the frame behavior. This is then handled by the "on turnCard" handler below. The sprite number is also passed to this handler. With the sprite number, and the "pSpriteOffset" property, the code determines the card number. It records this in either "pCard1" or "pCard2". In addition, the card is turned over{4}. This simply means that the sprite on the Stage is replaced with the appropriate bitmap from the "Cards" cast. No decision is made in this handler as to whether a match has been made. Instead, the "pCardTimer" property is set when the second card is turned over{5}. This is monitored by the on exitFrame handler, and the comparison occurs after a specified amount of time passes. In the meantime, if the player tries to turn another card over, then the time delay ends and the comparison is made immediately. You can see this bit of code in the final else portion of the handler:{6}
-- called by the sprites when the user clicks
-- the spriteNumber parameter is the sprite number clicked
on turnCard me, spriteNumber
-- play sound, if there is one
if pClickSound <> "" then puppetSound 1, pClickSound
-- determine the card number
cardNumber = spriteNumber - pSpriteOffset
if pCard1 = 0 then -- first card clicked
-- record this card
pCard1 = cardNumber
-- turn it over
sprite(spriteNumber).member = member(pBoard[cardNumber],"Cards"){4}
else if pCard2 = 0 then -- second card clicked
-- ignore if it is the same card
if cardNumber = pCard1 then exit
-- record this card
pCard2 = cardNumber
-- turn it over
sprite(spriteNumber).member = member(pBoard[cardNumber],"Cards")
-- set the timer
pCardTimer = the ticks{5}
else -- two cards are already turned over{6}
-- this happens if the user clicks very quickly
-- force a look at the two cards
returnCards(me)
-- make sure the card was not clicked on twice
if sprite(spriteNumber).memberNum = 0 then exit
-- record new card as the first card
pCard1 = cardNumber
-- turn it over
sprite(spriteNumber).member = member(pBoard[cardNumber],"Cards")
end if
end
The "on returnCards" handler does the comparison. This handler is also responsible for dealing with the results--either turning the cards back over{7}, or removing them{8}. If a successful match is made, it calls the "on checkAllMatched" handler to see if the game is over.
-- looks at the two cards turned over and compares them
on returnCards me
if pBoard[pCard1] = pBoard[pCard2] then -- they are a match
-- play sound, if there is one
if pMatchSound <> "" then puppetSound 2, pMatchSound
-- remove both sprites{8}
sprite(pCard1+pSpriteOffset).memberNum = 0
sprite(pCard2+pSpriteOffset).memberNum = 0
-- check for game over
if checkAllMatched(me) then
go to frame pGameOverFrame
end if
else -- no match
-- play sound, if there is one
if pNoMatchSound <> "" then puppetSound 2, pNoMatchSound
-- turn both cards back{7}
sprite(pCard1+pSpriteOffset).member = member("Card Back")
sprite(pCard2+pSpriteOffset).member = member("Card Back")
end if
-- reset the game properties
pCard1 = 0
pCard2 = 0
end
The on exitFrame handler plays an important part in this game. It checks for the situation in which two cards are turned over. If that is true, then it checks to see if it's a certain amount of time past the time set in "pCardTimer". If this is also true, then it calls "on returnCards" to compare them. This behavior is also responsible for the critical go to the frame command. This keeps the game looping on the current frame.
on exitFrame me
if pCard1 <> 0 and pCard2 <> 0 then -- two cards are turned
if the ticks > pCardTimer + pDisplayDelay then -- time has expired
-- check the cards to see if there is a match
returnCards(me)
end if
end if
-- loop on the frame
go to the frame
end
The last handler checks to see if the game is over. It simply looks at all the sprites where a card should be and sees if there is one there. If it finds any cards{9}, then the game is not over, and a FALSE is returned. Otherwise, a TRUE is returned.
-- check to see if the game is over
on checkAllMatched me
-- loop through all cards
repeat with i = 1 to pNumberOfCards
-- determine the card's sprite
spriteNumber = i + pSpriteOffset
-- if it is still a card, then the game is not over
if sprite(i).memberNum <> 0 then return FALSE{9}
end repeat
-- all cards missing, so game over
return TRUE
end
In addition to this one long behavior for the frame script channel, there needs to be a behavior on each of the individual card sprites. This behavior is just one line of code inside an on mouseUp handler. on mouseUp me -- simply tell the frame script that this sprite was clicked sendSprite(0,#turnCard,me.spriteNum) end When you want to send a message to the frame script, use sendSprite with a sprite number of 0. In Director 7, you can actually use any sprite number to send a message to the frame script, so long as the sprite does not already have a behavior that handles the same message. Strangely enough, if you were to ask the frame behavior for the value of me.spriteNum, however, it would return -5. The sendSprite command here sends its message to the frame script channel. To do this, a sprite number of 0 is used as the first parameter. The command also sends an extra parameter, the sprite number. | |