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

3555人阅读 评论(0) 收藏 举报
分类:

移动球拍

我们需要移动它!移动球拍需要接收触摸事件。你可以在 MyScene 中实现以下方法以接收触摸事件:

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;

-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event;

-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event;

在此之前,还需要新加一个属性。在 MyScene.m 中@implementation  语句前加入:

@interface MyScene()  

@property (nonatomic) BOOL isFingerOnPaddle;  

@end

这个属性用于保存玩家是否触摸到了球拍。这对于实现球拍的移动是有用的。

然后,在 MyScene.m 中实现 touchesBegan:withEvent: 方法:

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {

     /* 触摸开始时调用 */

     UITouch* touch = [touches anyObject];

     CGPoint touchLocation = [touch locationInNode:self];

       SKPhysicsBody* body = [self.physicsWorld bodyAtPoint:touchLocation];

     if (body && [body.node.name isEqualToString: paddleCategoryName]) {

         NSLog(@"Began touch on paddle");

         self.isFingerOnPaddle = YES;

     }

}

上述代码获取触摸并得到触摸在屏幕中的位置。然后用physicasWorld的 bodyAtPoint:方法找出触摸位置所处的物体(如果有的话)。然后判断是否有这样一个物体存在,以及这个物体是不是球拍。早先我们为球拍指定了一个名字——你可以通过物体的名字来判断它是否是指定的对象。如果触摸位置就是球拍所处的位置,将isFingerOnPaddle 设为 YES 并在控制台中输出一个消息。

你已经摸到我了;)

接下来实现  touchesMoved:withEvent:方法:

-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {

     // 1 如果用户触摸到球拍

     if (self.isFingerOnPaddle) {

         // 2 获取触摸位置

         UITouch* touch = [touches anyObject];

         CGPoint touchLocation = [touch locationInNode:self];

         CGPoint previousLocation = [touch previousLocationInNode:self];

         // 3 获得球拍节点

         SKSpriteNode* paddle = (SKSpriteNode*)[self childNodeWithName: paddleCategoryName];

         // 4 计算球拍将移动的x位置

         int paddleX = paddle.position.x + (touchLocation.x - previousLocation.x);

         // 5 限制球拍的移动在屏幕范围之内

         paddleX = MAX(paddleX, paddle.size.width/2);

         paddleX = MIN(paddleX, self.size.width - paddle.size.width/2);

         // 6 更新球拍的坐标

         paddle.position = CGPointMake(paddleX, paddle.position.y);

     }

}

这就是球拍的移动方式。

  1. 检查玩家是否触摸到球拍。
  2. 如果是,需要根据玩家滑动手指的距离来更改球拍的坐标。这就需要算出本次触摸的位置和上次触摸的位置。
  3. 获得球拍对应的 SKSpriteNode 对象。
  4. 将手指移动的x距离加到球拍当前位置上。
  5. 重置球拍位置之前,限定球拍移动分为在屏幕许可的范围之内。
  6. 将球拍位置设置为计算结果。

注意:如你所见,代码直接操作了球拍的坐标。这是由于球拍是静态的。对于动态物体,不应该直接改变其坐标。因为这样会破坏物理引擎模拟的状态,导致不可预料的行为。

你别不信,如果你将球拍改为动态物体,将重力加速度设置为低重力状态,例如:

self.physicsWorld.gravity = CGPointMake(-0.1, -0.1);

试试看,一切都乱了,不是吗?

还有最后一个触摸方法 touchesEnded:withEvent: 需要实现,我们在这里进行变量的重置:

-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {

     self.isFingerOnPaddle = NO;

}

将 isFingerOnPaddle  设置为 NO;确保用户手指离开并再次触摸屏幕之后,变量被重置。

SpriteKit 的第一次亲密接触!

啊,不是你想象的那种接触!(本图片来自 DeviantArt.com 的 hugoo13,受知识共享协议保护)

 

迄今为止,我们的游戏中只有一个会在屏幕中四处乱蹦的小球,和一个通过触摸来进行移动的球拍。

对于真正的游戏,还需要设定某种规则以判定玩家输赢。我们设定,如果球落到了屏幕底部而不是球拍上为输。但如何使用SpriteKit 来判断这种情况?

SpriteKit 可以判断两个物体间发生的接触。但是为实现这个目的,需要遵循几个固定的步骤。我先简单描述一下每个步骤,稍后再细讲:

 

  • 设置物体的 bit 掩码:在游戏中,物体存在几种类型——例如玩家、怪物、子弹、道具等等。为了标志每种不同的物体,我们可以将对象通过以下几个 bit 掩码来加以配置:
  • Set up physics body bit masks: In your game you might have several different types of physics bodies – for example, you can have the player, enemies, bullets, bonus items etc. To uniquely identify these different types of physics bodies, each physics body can be configured using several bit masks. These include:
    • categoryBitMask: 用于表示对象类型。用一个 32 位整数表示,其中,每一位表示一种类型,因此整个游戏可以包含 32 种对象。这对大部分游戏来说就已足够了。对于复杂游戏,还可以用多个类型来组合。因此,通过这种灵活的方式你可以突破 32 这个限制。
    • contactTestBitMask: 当对象和其他对象发生触碰时,我们可以在这个掩码上设置一个类型码,这样 SpriteKit 会向 contact delegate 发送消息。默认每位都是0——表示不通知任何对象。从性能的角度考虑,你应该只设置那些真正需要设置的位。
    • Setting a bit in this bitmask causes SpriteKit to notify the contact delegate when the body touches another body assigned to that particular category. By default, all bits are cleared – you are not notified about any contacts between objects. For best performance you should only set bits in the contacts mask for interactions you are really interested in.
    • collisionBitMask: 用于定义能够和本类物体发生碰撞的物体。一般情况下,一个质量很大的物体与一个质量很小的物体发生碰撞,后者对前者加速度的改变微乎其微,因此通常情况下无需对此类碰撞进行检测。也就是说,允许两个对象能够彼此穿过。
  • 设置并实现 contact delegate:conatact delegate 是 SKPhysicasWorld 的一个属性。当两个物体开始和发生碰撞时(根据指定的 contactTestBitMasks 值),contact delegate 对象将被通知。

注意:位掩码?如果你从未使用过它,不要担心。尽管一开始他们看起来很复杂,但却非常有用!

什么是位掩码?位掩码是一连串的二进制数。比如:1011 1000。其实并不复杂。

为什么说它很有用?首先,你可以从这个二进制数读取对象状态,也可以将对象状态单独设置到这个数的某一个二进制位。只需要进行一个 AND 或 OR 操作:

强大的位掩码 :)

它以一种非常紧凑的方式(比如一个变量)存储大量信息,同时存取、操作存储在其中的信息也很方便。




0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:4101823次
    • 积分:38447
    • 等级:
    • 排名:第109名
    • 原创:287篇
    • 转载:6篇
    • 译文:230篇
    • 评论:1547条
    博客专栏
    最新评论