在Swift中检测两个物体之间的碰撞

4

在Swift中,碰撞检测相当简单 - 但是在这个特定的项目中,身体碰撞不像通常那样触发didBeginContact事件。

以下是我用于两个物体碰撞(使用士兵和子弹)的检查清单:

  1. SKPhysicsContactDelegate添加到类中。
  2. 适当设置physicsWorld,通常为:self.physicsWorld.contactDelegate = self
  3. 为每个节点组创建类别。例如:let bulletCategory = 0x1 << 0
  4. 为每个子弹和士兵创建SKNodes。
  5. 为每个子弹和士兵分别创建一个具有匹配形状的物理体。
  6. 将每个子弹的categoryBitMask设置为bulletCategory(士兵设置为soldierCategory)。
  7. 将每个子弹的contactBitMask设置为soldierCategory(士兵设置为bulletCategory)。
  8. 定义didBeginContact()处理程序。

下面是完整的代码。它是一个最小化的 ~20 行“Hello World”程序,用于演示碰撞。

如果您创建了一个新的“Game”,并将其复制粘贴到GameScene.swift中,它将运行,但不会触发碰撞事件。

import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate {
    var soldier = SKShapeNode(circleOfRadius: 40)

    let soldierCategory:UInt32 = 0x1 << 0;
    let bulletCategory:UInt32 = 0x1 << 1;

    override func didMoveToView(view: SKView) {
        self.physicsWorld.contactDelegate = self

        // THE soldier
        soldier.fillColor = SKColor.redColor()
        soldier.position = CGPoint(x: CGRectGetMidX(self.frame), y: 40)
        soldier.physicsBody = SKPhysicsBody(circleOfRadius: 20)
        soldier.physicsBody!.dynamic = false
        soldier.physicsBody!.categoryBitMask = soldierCategory
        soldier.physicsBody!.contactTestBitMask = bulletCategory
        soldier.physicsBody!.collisionBitMask = 0


        // bulletS
        var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("makeBullet"), userInfo: nil, repeats: true)

        self.addChild(soldier)
    }

    func makeBullet() {
        var bullet = SKShapeNode(rect: CGRect(x: CGRectGetMidX(self.frame), y: self.frame.height, width: 10, height: 40), cornerRadius: CGFloat(0))
        bullet.fillColor = SKColor.redColor()

        bullet.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 10, height: 40))
        bullet.physicsBody?.dynamic = false
        bullet.physicsBody?.categoryBitMask = bulletCategory
        bullet.physicsBody?.contactTestBitMask = soldierCategory
        bullet.physicsBody?.collisionBitMask = soldierCategory

        var movebullet = SKAction.moveByX(0, y: CGFloat(-400), duration: 1)
        var movebulletForever = SKAction.repeatActionForever(movebullet)
        bullet.runAction(movebulletForever)

        self.addChild(bullet)
    }

    func didBeginContact(contact: SKPhysicsContact) {
        print("CONTACT")
    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        /* Called when a touch begins */
    }

    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }
}

等一下。如果一个物理体不是“动态”的,它就不能与任何东西发生碰撞吗? - Don P
1个回答

5
以下是需要翻译的内容:

您的清单中缺少以下几项:

  1. 物理体之一必须是动态的
  2. 物理体必须通过力/冲量或设置其速度来移动
  3. 物理体的位置应与相应的精灵/形状节点相匹配

对于(2),您正在通过使用SKAction随时间更改其位置来移动每个子弹。

对于(3),每个子弹的位置从(0,0)开始,而形状则在场景顶部中心绘制。由于物理体位于形状节点的位置中心,因此形状和物理体之间存在位置不匹配的问题;子弹的物理体永远不会与士兵接触。要解决此问题,请尝试

    var bullet = SKShapeNode(rectOfSize: CGSizeMake(10, 40))
    bullet.position = CGPointMake(self.size.width/2.0, self.size.height)

此外,士兵的物理身体半径是其形状的一半。


太棒了,谢谢。有没有一种方法可以调试或查看物理体的位置?我想看看它们如何移动/设置与对象不同,但我无法给它们着色。 - Don P
2
didMoveToView 或者你的视图控制器中设置 view.showsPhysics = true - 0x141E
两个物理体都需要是动态的吗? 在另一个项目中,我制作了 FlappyBirdy 的克隆版本,只有一个对象是动态的(小鸟),而管道不是动态的,只是使用 SKAction.moveByX() 移动,但接触事件仍然会发生。 - Don P
1
我建议您使用 performSelector SKAction 来调用 makeBullet,而不是使用 NSTimer。当您暂停游戏(使用 view.paused = true)时,动作会适当地暂停/恢复,并在您转换场景时自动删除。 - 0x141E
现在当子弹与士兵碰撞时,它会推动士兵。你知道如何使它们触发碰撞,但彼此穿过吗? - Don P
你可以设置 bullet.physicsBody?.collisionBitMask = 0 来防止子弹与任何物体碰撞,或者你可以设置 bullet.physicsBody?.collisionBitMask &= ~soldierCategory 来仅防止子弹与士兵碰撞。默认情况下,所有位都被设置为与所有类别碰撞。 - 0x141E

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接