用 SpriteKit 做一个逃逸游戏 (5)

但是……

用 SpriteKit 做一个逃逸游戏 (5)

这真是出人意料…

嗯……怎么样?出乎你的意料了吧!当球碰上砖块之后,砖块是被弹开,而不是被击碎?

为了监听球和砖之间的碰撞,你需要修改球的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

ball.physicsBody.collisionBitMask = paddleCategory

设置 collisionBitMask 为 paddleCategory 表示球会只会和球拍发生相互作用,而不会和砖块发生相互作用。对于屏幕四边的笼子,其 collisionBitMask 保持默认值,不赋予任何类别掩码。

    block.physicsBody.collisionBitMask = 0;

这一行语句则将砖块的 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 中也不提倡。

源代码

本教程源代码可以在这里下载。

下一步

很显然,这个游戏十分简单。但你可以从这里开始,加入许多有趣的东西。比如给砖块加上生命值,加入不同的砖块,小球必须击打多次才能摧毁它们。有的砖块可以加分,有的则会减分,甚至让球拍向砖块发射激光,一切只取决于你的想象力!

请留下您的建议,希望本教程对你有用。

 


用 SpriteKit 做一个逃逸游戏 (5)

上一篇:uva - 10057 - A mid-summer night's dream. (数学、中位数)


下一篇:ORA-01034错误