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

移动球拍

我们需要移动它!移动球拍需要接收触摸事件。你可以在 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 操作:

强大的位掩码 :)

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




深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值