Search code examples
swiftsprite-kitskspritenode

Swift spritekit didbegincontact being called with delay


This is my GameScene code.

class GameScene: SKScene, SKPhysicsContactDelegate {

    let orcWidth = UIScreen.main.bounds.width / 5

    var orcCategory:UInt32 = 0x1 << 0
    var knightCategory:UInt32 = 0x1 << 1

    private var orc = SKSpriteNode()
    private var  knight = SKSpriteNode()
    private var orcWalkingFrames: [SKTexture] = []
    private var knightIdleFrames: [SKTexture] = []
    private var knightAttackFrame: [SKTexture] = []
    var background = SKSpriteNode(imageNamed: "game_background1")

    override func didMove(to view: SKView) {
        physicsWorld.contactDelegate = self
        physicsWorld.gravity = CGVector(dx: 0, dy: 0)
        setupbackground()
        startGame()
    }

    func setupbackground() {
        background.zPosition = 0
        background.size = self.frame.size
        background.position = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
        addChild(background)
    }

    func startGame() {
        buildRandomOrcs()
        buildKnight()
    }

    func stopGame() {
    }

    func buildOrc(yposition: CGFloat) {
        var orcWalkFrames: [SKTexture] = []
        let orcAnimatedAtlas = SKTextureAtlas(named: "OrcWalking")

        let numImages = orcAnimatedAtlas.textureNames.count
        for i in 0...numImages - 1 {
            let orcTextureName = "0_Orc_Walking_\(i)"
            orcWalkFrames.append(orcAnimatedAtlas.textureNamed(orcTextureName))
        }
        self.orcWalkingFrames = orcWalkFrames

        let firstFrameTexture = orcWalkingFrames[0]
        orc = SKSpriteNode(texture: firstFrameTexture)
        orc.name = "orc"
        orc.position = CGPoint(x: frame.minX-orcWidth/2, y: yposition)
        self.orc.zPosition = CGFloat(self.children.count)
        orc.scale(to: CGSize(width: orcWidth, height: orcWidth))
        orc.physicsBody = SKPhysicsBody(rectangleOf: orc.size, center: orc.position)
        orc.physicsBody?.affectedByGravity = false
        orc.physicsBody?.isDynamic = true
        orc.physicsBody?.categoryBitMask = orcCategory
        orc.physicsBody?.contactTestBitMask = knightCategory
        orc.physicsBody?.collisionBitMask = knightCategory
        addChild(orc)
        walkOrc()
        moveOrcForward()
    }

    func buildKnight() {
        var knightIdleFrames: [SKTexture] = []
        let knightIdleAtlas = SKTextureAtlas(named: "KnightIdle")

        let numImages = knightIdleAtlas.textureNames.count
        for i in 0...numImages - 1 {
            let orcTextureName = "_IDLE_00\(i)"
            knightIdleFrames.append(knightIdleAtlas.textureNamed(orcTextureName))
        }
        self.knightIdleFrames = knightIdleFrames

        let firstFrameTexture = knightIdleFrames[0]
        knight = SKSpriteNode(texture: firstFrameTexture)
        knight.name = "knight"
        knight.position = CGPoint(x: frame.maxX-orcWidth/2, y: frame.midY)
        self.knight.zPosition = 1
        knight.scale(to: CGSize(width: -orcWidth, height: orcWidth))
        knight.physicsBody = SKPhysicsBody(rectangleOf: knight.size, center: knight.position)
        knight.physicsBody?.affectedByGravity = false
        knight.physicsBody?.isDynamic = false
        knight.physicsBody?.categoryBitMask     = knightCategory
        knight.physicsBody?.contactTestBitMask = orcCategory
        knight.physicsBody?.collisionBitMask     = orcCategory
        addChild(knight)
        idleKnight()
    }

    func idleKnight() {
        knight.run(SKAction.repeatForever(SKAction.animate(with: knightIdleFrames, timePerFrame: 0.1)))
    }

    func walkOrc() {
        orc.run(SKAction.repeatForever(SKAction.animate(with: orcWalkingFrames,timePerFrame: 0.025)))
    }

    func moveOrcForward() {
        orc.run(SKAction.repeatForever(SKAction.moveBy(x: 55, y: 0, duration: 0.25)))
    }

    func buildRandomOrcs () {
        let wait = SKAction.wait(forDuration: TimeInterval(makeRandomNumberBetween(min: 0, max: 0)))
        let spawn = SKAction.run {
            self.buildOrc(yposition: self.makeRandomCGFloatNumber())
        }

        let spawning = SKAction.sequence([spawn,wait])

        self.run(SKAction.repeat(spawning, count: 10))
    }

    func makeRandomCGFloatNumber() -> CGFloat {
        let randomNumber = arc4random_uniform(UInt32((frame.maxY-orcWidth/2) - (frame.minY+orcWidth/2))) + UInt32(frame.minY+orcWidth/2)
        return CGFloat(randomNumber)

    }

    func makeRandomNumberBetween (min: Int, max: Int) -> Int{
        let randomNumber = arc4random_uniform(UInt32(max - min)) + UInt32(min)
        return Int(randomNumber)
    }


    func didBegin(_ contact: SKPhysicsContact) {
        let collision:UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
        if collision == orcCategory | knightCategory {
            self.scene?.view?.isPaused = true
            print("COLLIDED")
        }
    }
}

The problem is that the scene pauses almost 2-3 seconds after the collision. I changed the position of knight and delay time changed.

For example, if I set position to frame.minX+orcWidth/2 there is no delay.

What is wrong with my code?


Solution

  • Your problem isn't things are being delayed, your problem is your bounding box is not where you think it is

    use view.showPhysics = true to determine where your boxes are

    once you realize they are in the wrong spots, go to this line

    knight.physicsBody = SKPhysicsBody(rectangleOf: knight.size, center: knight.position)
    

    and fix it

    knight.physicsBody = SKPhysicsBody(rectangleOf: knight.size)
    

    Do so for the rest of your bodies