但是……
这真是出人意料…
嗯……怎么样?出乎你的意料了吧!当球碰上砖块之后,砖块是被弹开,而不是被击碎?
为了监听球和砖之间的碰撞,你需要修改球的contactTestBitMask(注意,在 initWithSize 方法中已经有对应的赋值语句了,你只需要在等号右边在加上一个 category 常量):
ball.physicsBody.contactTestBitMask = bottomCategory | blockCategory; |
在 bottomCategory 和 blockCategory 之间是位运算符OR。这会将这两个类别所对应的二进制位上都设为1,其他位则为0。
现在,不管是球和地发生碰撞,还是球和砖发生碰撞,都将调用委托方法。
接下来需要处理委托消息。在 didBeginContact: 方法最后加入:
if (firstBody.categoryBitMask == ballCategory && secondBody.categoryBitMask == blockCategory) { [secondBody.node removeFromParent]; //TODO: check if the game has been won } |
上述代码判断碰撞是否来自于球与砖。如果是,将发生碰撞的砖块移除。
想必你已经明白位掩码,尤其是 contactTestBitMask 是怎么回事了。前面提及,物体还有一个collisionBitMask 位掩码。它决定了当两个物体发生碰撞时,它们能否相互作用(即作用力与反作用力)。例如,它决定了球是被砖块弹开,还是直接从砖块上穿过。
如果你想使用 collisionBitMask,可以给球的collisionBitMask 赋值,这样当砖块被消灭时,球将直接穿过。
隐含内容: 使用 collisionBitMask | ||
设置 collisionBitMask 为 paddleCategory 表示球会只会和球拍发生相互作用,而不会和砖块发生相互作用。对于屏幕四边的笼子,其 collisionBitMask 保持默认值,不赋予任何类别掩码。
这一行语句则将砖块的 collisionBitMask 设置为0。这表示砖块不会对其他碰撞物体发生作用力。 |
你是否完成了这个挑战(指 collisionBitMask 的使用)并尝到了胜利的滋味?现在也该到让你在游戏赢得胜利的时候了:]
在游戏中获胜
为了让玩家能在游戏中获胜,在 MyScene.m 中加入这个方法:
-(BOOL)isGameWon { int numberOfBricks = 0; for (SKNode* node in self.children) { if ([node.name isEqual: blockCategoryName]) { numberOfBricks++; } } return numberOfBricks <= 0; } |
在方法中遍历游戏场景中的所有子节点,判断屏幕中还剩下多少砖块。对于每个子节点,我们都要检查它的节点名是否等于 blockCategoryName。如果所有砖块都被干掉了,判定玩家获胜,返回 YES。
回到 didBeginContact:方法,将最终的 TODO 行修改为下面的 if 语句:
if ([self isGameWon]) { GameOverScene* gameWonScene = [[GameOverScene alloc] initWithSize:self.frame.size playerWon:YES]; [self.view presentScene:gameWonScene]; } |
这段代码调用新方法判断玩家是否获胜,如是,则在即将显示的 GameOverScene窗口中告诉玩家已经获胜。
结束触摸
在你玩游戏的时候,你可能注意到,小球在被球拍击中时,有时会非常快有时会非常慢。
通过 MyScene.m 的 update: 方法,你可以防止这种速度的变化:
-(void)update:(CFTimeInterval)currentTime { /* 刷新每一帧都会调用 */ SKNode* ball = [self childNodeWithName: ballCategoryName]; static int maxSpeed = 1000; float speed = sqrt(ball.physicsBody.velocity.dx*ball.physicsBody.velocity.dx + ball.physicsBody.velocity.dy *ball.physicsBody.velocity.dy); if (speed > maxSpeed) { ball.physicsBody.linearDamping = 0.4f; } else { ball.physicsBody.linearDamping = 0.0f; } } |
update:方法在刷新每一帧之前调用。之前没有在 MyScene.m 中实现 update: 方法,因此默认将调用默认实现。这里,我们用自己的实现覆盖了默认实现。首先我们获取小球对象,检查它的velocity 属性,即其运动速度。如果 velocity 很大,则加大球的线性阻尼系数,这样球就会慢下来。
编译运行程序,开始游戏。你会发现一旦球的速度变得太快,就会恢复到正常水平。
注意: 你可能想直接通过改变球的 velocity 来防止球体速度过快,这样也许你会更容易控制球的运动。这种做法在 Box2D 中是禁止的,在 SpriteKit 中也不提倡。
源代码
本教程源代码可以在这里下载。
下一步
很显然,这个游戏十分简单。但你可以从这里开始,加入许多有趣的东西。比如给砖块加上生命值,加入不同的砖块,小球必须击打多次才能摧毁它们。有的砖块可以加分,有的则会减分,甚至让球拍向砖块发射激光,一切只取决于你的想象力!
请留下您的建议,希望本教程对你有用。