|
所有游戏中最经典的游戏“宇宙入侵者”.已有成百上千种翻版,其中有些就是用Director制作的。下面的例子只是非常简单的一个游戏。图1 是正在玩游戏时的情景。
图1 在这个“入侵者”游戏里,入侵者是一些样子十分可笑的位图
这个入侵者游戏比前面几个游戏都复杂。它分为四个行为:一个用于入侵者的角色,一 个用于战舰,一个用于入侵者所发射的子弹,一个用于战舰所发射的子弹。
1 制作入侵者角色 游戏主要由入侵者的运动所控制,它们集体从舞台的一侧横向移到另一侧。当它们碰到 舞台某个侧边时,就向下掉,离舞台的底边更近一步。 控制这种运动只需要一个行为,再把这个行为赋予舞台上的每一个入侵者角色就可以了。 不过,还要使用g H i t Wa l l 和g H i t B o t t o m 两个全局变量。当至少一个角色碰到舞台的侧边或底边 时,相应的全局变量就被设为T R U E ,然后由帧剧本来处理这个情况。 该行为开始时,方向和速度都被设为2 ,表示入侵者每帧向右移动两个像素。它还记录角 色的演员编号。在演员表里,每个入侵者有两个演员,它们基本相同,只是脚的方向反过来, 给人以“进军”的感觉。入侵者行为在各帧交替使用这两个演员.。 global gHitWall, gHitBottom property pDirection, pMemNum on beginSprite me -- start moving 2 pixels to right pDirection = 2 pMemNum = sprite(me.spriteNum).memberNum end on exitFrame me -- freeze if not on play frame if the frameLabel <> "Play" then exit if pDirection = 0 then -- no direction, must have been hit sprite(me.spriteNum).memberNum = 0 -- remove sprite
else -- move sprite(me.spriteNum).locH = sprite(me.spriteNum).locH + pDirection -- hit a wall? if pDirection > 0 and sprite(me.spriteNum).locH > 460 then gHitWall = TRUE else if pDirection < 0 and sprite(me.spriteNum).locH < 20 then gHitWall = TRUE end if -- toggle to other member to create animation if sprite(me.spriteNum).memberNum = pMemNum then sprite(me.spriteNum).memberNum = pMemNum + 1 else sprite(me.spriteNum).memberNum = pMemNum end if -- fire 1 out of 200 times if random(200) = 1 then sendSprite(sprite 55, #fire, sprite(me.spriteNum).loc) end if end if end on exitFrame 处理程序还以1 / 2 0 0 的机会引发入侵者发射子弹。它向角色5 5 —即入侵者的 子弹—发送一个# f i r e 消息。 如果哪个角色与舞台侧边的距离太近,on exitFrame 处理程序就把全局变量g H i t Wa l l 设为 T R U E 。图3 2 - 7 的例子里使用了2 4 个入侵者。只要有一个入侵者距离墙(舞台的侧边)太近,就 要把这个全局变量设置为T R U E 。如果真的变为T R U E 了,帧剧本就将处理它,即向所有角色 发送一个# c h a n g e D i r e c t i o n 消息。只有入侵者角色才使用这个消息。下面就是那个被调用的处 理程序。它把入侵者向下移动,也改变运动方向。它还检查是否有角色已经太靠下了。 on changeDirection me -- got change direction message -- move down sprite(me.spriteNum).locV = sprite(me.spriteNum).locV + abs(pDirection) -- hit bottom? if sprite(me.spriteNum).locV > sprite(5).rect.top then gHitBottom = TRUE -- reverse direction pDirection = -pDirection end 可以发送给入侵者的另一个消息是# h i t 。这时,演员变为一个爆炸情景的位图。舞台被更 新,于是爆炸的情景立刻显示出来。然后,p D i r e c t i o n 属性被设为0 。这是为了让on exitFrame 处理器知道这个入侵者已经死了,并通过在下一帧把它的演员编号设为0 而删除这个角色。 on hit me -- got the message I was hit -- change to hit graphic
sprite(me.spriteNum).member = member("Invader Hit") -- show me, since I will disappear next frame updateStage -- dead, so no direction pDirection = 0 end 与入侵者行为相连的是入侵者的子弹的行为。这个行为将赋予角色5 5 至角色7 5 。如果想 让角色每次发射更多子弹,可以把它赋予更多的角色;如果想让入侵者少发射些子弹,可以 赋予较少的角色。 入侵者行为里有一个p M o d e 属性,它告诉行为现在是否射击。on fire 处理程序导致发射子 弹,或者当已经发射子弹时,把该消息传给下一个角色。on exitFrame 处理程序负责移动发射 出来的子弹,并检测它是否击中了什么目标,.或是落到了舞台的底部。 property pMode on beginSprite me pMode = #none end on fire me, loc if pMode = #fire then -- busy, send to next sprite sendSprite(sprite(me.spriteNum+1),#fire,loc) else -- set loc, mode sprite(me.spriteNum).loc = loc pMode = #fire end if end on exitFrame me -- freeze unless on play frame if the frameLabel <> "Play" then exit if pMode = #fire then -- move down sprite(me.spriteNum).locV = sprite(me.spriteNum).locV + 8 if sprite(me.spriteNum).locV > 320 then -- hit bottom pMode = #none else -- hit gun? didIHit(me) end if end if end on exitFrame 处理程序调用on didIHit ,以确定子弹是否击中了战舰。战舰(或枪)是角色5 。 on didIHit me -- hit gun? if sprite 5 intersects me.spriteNum then -- gun explodes sprite(5).member = member("Invader Hit") updateStage -- game over go to frame "Done" end if end
2 创建战舰 现在入侵者和入侵者的子弹都可以活动了,下面的任务是让战舰活动。用左、右箭头键 让战舰左右移动是十分容易的。如果按空格键,则把# f i r e 消息发送给一组战舰子弹角色。 property pFiredLastFrame on exitFrame me if the frameLabel <> "Play" then exit if keyPressed(123) then -- left arrow sprite(me.spriteNum).locH = sprite(me.spriteNum).locH - 5 end if if keyPressed(124) then -- right arrow sprite(me.spriteNum).locH = sprite(me.spriteNum).locH + 5 end if -- check spacebar, plus check to make sure did not fire last frame if keyPressed(SPACE) and not pFiredLastFrame then -- space, fire sendSprite(sprite 6, #fire, sprite(me.spriteNum).loc) pFiredLastFrame = TRUE else pFiredLastFrame = FALSE end if end 战舰行为使用了一个名为p F i r e d L a s t F r a m e 的属性,它可以阻止用户按住空格键不放,从 而发射连续的子弹流,导致入侵者的伤亡太重。相反,只能每隔一帧发射一颗子弹。要把发 射子弹的时间间隔拉得更长,可以用该属性计算从上一次发射子弹到现在已经过了多少帧, 并只让子弹每隔3 或4 帧才能发射一次。 战舰的子弹的行为与入侵者的子弹的行为十分相似。其区别在于战舰的子弹向上运动, 并检测是否击中了任何入侵者角色。 由于每当击中入侵者就要加分,需要使用全局变量g S c o r e 。当检测到击中时,就为用户 加分。 global gScore property pMode on beginSprite me pMode = #none end on fire me, loc -- got signaled to fire if pMode = #fire then -- busy, send to next sprite sendSprite(sprite(me.spriteNum+1),#fire,loc) else -- fire sprite(me.spriteNum).loc = loc pMode = #fire end if end on exitFrame me -- freeze if not on play frame if the frameLabel <> "Play" then exit if pMode = #fire then -- move bullet up sprite(me.spriteNum).locV = sprite(me.spriteNum).locV - 16 if sprite(me.spriteNum).locV < 0 then -- reached top of screen pMode = #none else -- check for hit didIHit(me) end if end if end on didIHit me -- loop through invader sprites repeat with i = 30 to 53 -- see if it hit if sprite i intersects me.spriteNum then -- send hit message sendSprite(sprite i, #hit) -- get rid of bullet sprite(me.spriteNum).locV = -100 pMode = #none -- add to score gScore = gScore + 1 showScore end if end repeat end 影片剧本只承担很少的任务,如在必要时重新设置用户的得分,并显示新的分数等。 global gScore on startMovie gScore = 0 showScore go to frame "Play" end on showScore member("Score").text = "Score: "&&gScore end 3 创建帧剧本 最后,游戏还需要一个帧剧本。这个帧剧本比一般的帧剧本的任务要多一些。其任务之 一是当发现g H i t Wa l l 标志时,向所有角色发送# c h a n g e D i r e c t i o n 。当发现g H i t B o t t o m 时,它就 让游戏结束。这些任务都是在on enterFrame 处理程序里完成的,它保证这些任务将在角色的 行为里的on exitFrame 处理程序之前执行。 global gHitWall, gHitBottom on enterFrame me if gHitWall then -- an invader hit the wall sendAllSprites(#changeDirection) else if gHitBottom then -- an invader hit the bottom go to frame "Done" end if -- reset wall hit flag for this frame gHitWall = FALSE end on exitFrame go to the frame end 以上就是游戏所需要的剧本。其中,战舰、子弹和入侵者的速度的数值还都可以改变。 可以根据需要,在屏幕上放置更多或更少的入侵者。也可以使用其他位图,但记住每个入侵 者由两个演员组成,这两个演员频繁替换,以造成活动的效果。 把C D - R O M 上的这个影片打开来,并试着改一改某些设置。可以把入侵者的阵形改变成 其他形状,也可以添加一些“盾牌”,方法是添加一些角色和“盾牌”行为。可以采取多种方 法改进这个游戏。
|