【问题标题】:SpriteKit - didBegin contact is called 30 times instead of 1 timeSpriteKit - didBegin 联系人被调用 30 次而不是 1 次
【发布时间】:2019-02-12 13:55:31
【问题描述】:

我正在制作一个小 FlappyBird 克隆,并让一切正常工作,直到我将鸟的物理身体更改为与纹理完全相同。现在它所做的是,当它飞过管道中的间隙时,它会计算 30 点而不是 1 点。

这只发生在我使用我需要的纹理精确物理体时,因为鸟不是圆形也不是矩形。

我将如何进行碰撞,使其仅与每个间隙节点碰撞一次。我尝试在联系后将 categoryBitBask 设置为 0,但是之后的所有间隙都不再添加到点数中。

这是完整的游戏代码:

var score = 0

class GameScene: SKScene, SKPhysicsContactDelegate {

var bird = SKSpriteNode()
var bg = SKSpriteNode()
var ground = SKSpriteNode()

var scoreLabel = SKLabelNode(fontNamed: "Candice")
var gameOverLabel = SKLabelNode(fontNamed: "Candice")

var countbg = SKSpriteNode()

var timer = Timer()

enum ColliderType: UInt32 {
    case Bird = 1
    case Object = 2
    case Gap = 4
}

var gameOver = false

let swooshSound = SKAction.playSoundFileNamed("sfx_swooshing.wav", waitForCompletion: false)
let pointSound = SKAction.playSoundFileNamed("sfx_point.wav", waitForCompletion: false)
let hitSound = SKAction.playSoundFileNamed("sfx_hit.wav", waitForCompletion: false)

@objc func makePipes() {

    let movePipes = SKAction.move(by: CGVector(dx: -2 * self.frame.width, dy: 0), duration: TimeInterval(self.frame.width / 150))
    let removePipes = SKAction.removeFromParent()
    let moveAndRemovePipes = SKAction.sequence([movePipes, removePipes])

    let gapHeight = bird.size.height * 2.8

    let movementAmount = arc4random() % UInt32(self.frame.height) / 2

    let pipeOffset = CGFloat(movementAmount) - self.frame.height / 4

    let pipeTexture = SKTexture(imageNamed: "pipe1.png")
    let pipe1 = SKSpriteNode(texture: pipeTexture)
    pipe1.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeTexture.size().height / 2 + gapHeight / 2 + pipeOffset)
    pipe1.zPosition = 2
    pipe1.run(moveAndRemovePipes)

    pipe1.physicsBody = SKPhysicsBody(rectangleOf: pipeTexture.size())
    pipe1.physicsBody!.isDynamic = false

    pipe1.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
    pipe1.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
    pipe1.physicsBody!.collisionBitMask = ColliderType.Object.rawValue

    self.addChild(pipe1)

    let pipe2Texture = SKTexture(imageNamed: "pipe2.png")
    let pipe2 = SKSpriteNode(texture: pipe2Texture)
    pipe2.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY - pipeTexture.size().height / 2 - gapHeight / 2 + pipeOffset)
    pipe2.zPosition = 2
    pipe2.run(moveAndRemovePipes)

    pipe2.physicsBody = SKPhysicsBody(rectangleOf: pipe2Texture.size())
    pipe2.physicsBody!.isDynamic = false

    pipe2.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
    pipe2.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
    pipe2.physicsBody!.collisionBitMask = ColliderType.Object.rawValue

    self.addChild(pipe2)

    let gap = SKNode()

    gap.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeOffset)
    gap.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 1, height: gapHeight))
    gap.physicsBody!.isDynamic = false
    gap.run(moveAndRemovePipes)

    gap.physicsBody!.contactTestBitMask = ColliderType.Bird.rawValue
    gap.physicsBody!.categoryBitMask = ColliderType.Gap.rawValue
    gap.physicsBody!.collisionBitMask = ColliderType.Gap.rawValue

    self.addChild(gap)
}

func didBegin(_ contact: SKPhysicsContact) {

    if gameOver == false {

    if contact.bodyA.categoryBitMask == ColliderType.Gap.rawValue || contact.bodyB.categoryBitMask == ColliderType.Gap.rawValue {

        score += 1
        scoreLabel.text = String(format: "%05d", score)
        run(pointSound)

    } else {

        self.speed = 0
        run(hitSound)
        gameOver = true
        timer.invalidate()
        bird.removeFromParent()

        let changeSceneAction = SKAction.run(changeScene)
        self.run(changeSceneAction)
        }
    }
}

//MARK: Change to Game Over Scene
func changeScene(){

    let sceneToMoveTo = GameOverScene(size: self.size)
    sceneToMoveTo.scaleMode = self.scaleMode
    let myTransition = SKTransition.fade(withDuration: 0.5)
    self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}

override func didMove(to view: SKView) {

    self.physicsWorld.contactDelegate = self

    setupGame()
}

func setupGame() {

    timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.makePipes), userInfo: nil, repeats: true)

    let groundTexture = SKTexture(imageNamed: "ground.png")
    let moveGroundAnimation = SKAction.move(by: CGVector(dx: -groundTexture.size().width, dy: 0), duration: 7)
    let shiftGroundAnimation = SKAction.move(by: CGVector(dx: groundTexture.size().width, dy: 0), duration: 0)
    let moveGroundForever = SKAction.repeatForever(SKAction.sequence([moveGroundAnimation, shiftGroundAnimation]))

    var i: CGFloat = 0
    while i < 3 {

        ground = SKSpriteNode(texture: groundTexture)
        ground.position = CGPoint(x: self.size.width * i, y: self.size.height / 7.65)
        ground.zPosition = 3
        ground.run(moveGroundForever)
        self.addChild(ground)

        i += 1
    }

    let bottom = SKNode()
    bottom.position = CGPoint(x: self.frame.midX, y: self.size.height / 7)
    bottom.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.width, height: 1))
    bottom.physicsBody!.isDynamic = false

    bottom.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
    bottom.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
    bottom.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
    self.addChild(bottom)

    let bgTexture = SKTexture(imageNamed: "bg.png")
    bg = SKSpriteNode(texture: bgTexture)
    bg.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
    bg.size = self.frame.size
    bg.zPosition = 1
    self.addChild(bg)

    let birdTexture = SKTexture(imageNamed: "flappy1.png")
    let bird2Texture = SKTexture(imageNamed: "flappy2.png")
    let bird3Texture = SKTexture(imageNamed: "flappy3.png")
    let bird4Texture = SKTexture(imageNamed: "flappy4.png")
    let bird5Texture = SKTexture(imageNamed: "flappy5.png")
    let bird6Texture = SKTexture(imageNamed: "flappy6.png")

    let animation = SKAction.animate(with: [birdTexture, bird2Texture, bird3Texture, bird4Texture, bird5Texture, bird6Texture], timePerFrame: 0.1)
    let makeBirdFlap = SKAction.repeatForever(animation)


    bird = SKSpriteNode(texture: birdTexture)
    bird.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
    bird.setScale(1)
    bird.zPosition = 6

    bird.run(makeBirdFlap)

    self.addChild(bird)

    bird.physicsBody = SKPhysicsBody.init(circleOfRadius: birdTexture.size().height / 2)
    //bird.physicsBody = SKPhysicsBody(texture: birdTexture, size: birdTexture.size())
    bird.physicsBody!.isDynamic = false
    bird.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
    bird.physicsBody!.categoryBitMask = ColliderType.Bird.rawValue
    bird.physicsBody!.collisionBitMask = ColliderType.Bird.rawValue

    let countbg = SKSpriteNode(imageNamed: "count_bg.png")
    countbg.position = CGPoint(x: self.size.width / 4.8, y: self.size.height * 0.94)
    countbg.setScale(0.8)
    countbg.zPosition = 4
    addChild(countbg)

    scoreLabel.fontSize = 80
    scoreLabel.text = String(format: "%05d", score)
    scoreLabel.fontColor = SKColor(red: 218/255, green: 115/255, blue: 76/255, alpha: 1)
    scoreLabel.position = CGPoint(x: self.size.width / 4, y: self.size.height * 0.94)
    scoreLabel.zPosition = 5
    addChild(scoreLabel)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if gameOver == false {
        bird.physicsBody!.isDynamic = true
        bird.physicsBody!.velocity = CGVector(dx: 0, dy: 0)
        bird.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 280))
        //run(swooshSound)
    } else {
        gameOver = false
        score = 0
        self.speed = 1
        self.removeAllChildren()
        setupGame()
    }
}

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

【问题讨论】:

  • 你不会错的;这对于物理引擎来说是正常的。忽略无关的联系人。
  • 忽略是什么意思?
  • 一旦你“注意到”了碰撞,在一个/两个对象中设置一些标志。在调用 didEnd() 方法时清除该标志。如果您在didBegin() 并且设置了该标志,那么您可以放心地忽略碰撞。我假设 SpriteKit 正在使用 Box2D,可能启用了 ccd,并且会发生这种情况。
  • OK 对 pogromming 来说还是有点新,所以不确定在哪里实现。我知道这段代码中没有 didEnd 函数。

标签: swift sprite-kit collision-detection skphysicsbody


【解决方案1】:

如果您要使用 RxSwift,您可以使用 debounce()throttle()distinctUntilChanged() 轻松摆脱这些额外事件。如果您愿意尝试这种方法,请尝试使用RxSpriteKit 框架。否则,存储最后一次联系的时间戳并忽略后续联系,直到经过一段时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-10
    • 1970-01-01
    • 2022-11-29
    • 1970-01-01
    • 2016-01-09
    相关资源
    最近更新 更多