SpriteKit - контакт didBegin вызывается 30 раз вместо 1 раза - PullRequest
0 голосов
/ 12 февраля 2019

Я делаю маленький клон 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
}
}

1 Ответ

0 голосов
/ 13 февраля 2019

Если вы будете использовать RxSwift, вы сможете легко избавиться от этих дополнительных событий, используя debounce() или throttle() или distinctUntilChanged().Если вы хотите попробовать этот подход, попробуйте RxSpriteKit framework.В противном случае сохраните отметку времени последнего контакта и игнорируйте следующие контакты, пока не истечет некоторый период времени.

...