5
votes

This is the first question I have posted on stack. I am using Xcode 7.3.1, iOS 9.3 and Swift 2. Also tested on simulator and my iPad directly. I have done numerous searches, read 2 books of Spritekit Physics and watched numerous video tutorial as well as written ones. I have written body physics with no problems before, and numerous times with smaller projects but this one is just giving me no feedback. This question has been posed here before in specific instances, i.e. 2 collisions with same spritenode or what is the difference between collisionBitMask and contactTestBitMask. I am aware of the differences and also aware that each object you wish to be in contact needs a categoryBitMask. First, I have assigned a global Physicsbody struct (tried straight binary bitMask and the corresponding Int32 corresponding to each with no success with either), a physics delegate and each SKSpritenode corresponds with its paired contact/s. I have tried numerous ways to implement didBeginContact (regarding the way it is structured), none have made a difference over the other.
Earlier I mentioned feedback. I have eliminated all but my main player, bullets, newEnemy and enemyFire. When I try to even print a result of contact or collision to the console, it is like it does not even get read. I get no activity. These spritenodes are all within 1 class (GameScene), I have tried separate classes for each functional spritenode with no luck. Also, with separate classes, it seems to make things worse, even with protocols in place for communicating between classes and sub-classes. Which using separate classes is still new to me, eventhough I know it is the preferred way. I will show only the code I believe may need to be seen to minimize but let me know if there is anything else that needs to be seen. I simply want the bullets to hit the planes and I can go from there to my other objects.

import AVFoundation
import SpriteKit
import GameKit

// Binary connections for collision and colliding
struct PhysicsCategory {
    static let GroundMask : UInt32 = 1    //0x1 << 0
    static let BulletMask : UInt32 = 2    //0x1 << 1
    static let PlayerMask : UInt32 = 4    //0x1 << 2
    static let EnemyMask  : UInt32 = 8    //0x1 << 3
    static let EnemyFire  : UInt32 = 16   //0x1 << 4
    static let All        : UInt32 = UInt32.max // all nodes
}

class GameScene: SKScene, SKPhysicsContactDelegate {

    // Starting scene, passed to didMoveToView
    func startScene() {

        // Sets the physics delegate and physics body
        view?.showsPhysics = true
        self.physicsWorld.gravity = CGVectorMake(0, 0)
        self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
        self.physicsWorld.contactDelegate = self // Physics delegate set
    }

    func setupPlayer() {

        player = SKScene(fileNamed: "Player")!.childNodeWithName("player")! as! SKSpriteNode

        // Body physics for player's planes
        player.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "MyFokker2.png"), size: player.size)
        player.physicsBody?.dynamic = false
        player.physicsBody?.usesPreciseCollisionDetection = true
        player.physicsBody?.categoryBitMask = PhysicsCategory.PlayerMask
        player.physicsBody?.contactTestBitMask = PhysicsCategory.EnemyFire
        player.physicsBody?.collisionBitMask = 0

        player.removeFromParent()
        self.addChild(player) // Add our player to the scene
    }

    // Create the ammo for our plane to fire
    func fireBullets() {

        bullet = SKSpriteNode(imageNamed: "fireBullet")

        // Body physics for plane's bulets
        bullet.physicsBody = SKPhysicsBody(rectangleOfSize: bullet.size)
        bullet.physicsBody?.dynamic = false
        bullet.physicsBody?.usesPreciseCollisionDetection = true
        bullet.physicsBody?.categoryBitMask = PhysicsCategory.BulletMask
        bullet.physicsBody?.contactTestBitMask = PhysicsCategory.EnemyMask
        bullet.physicsBody?.collisionBitMask = 0

        self.addChild(bullet) // Add bullet to the scene
    }

    func spawnEnemyPlanes() {
        let enemy1 = SKScene(fileNamed: "Enemy1")!.childNodeWithName("enemy1")! as! SKSpriteNode
        let enemy2 = SKScene(fileNamed: "Enemy2")!.childNodeWithName("enemy2")! as! SKSpriteNode
        let enemy3 = SKScene(fileNamed: "Enemy3")!.childNodeWithName("enemy3")! as! SKSpriteNode
        let enemy4 = SKScene(fileNamed: "Enemy4")!.childNodeWithName("enemy4")! as! SKSpriteNode

        enemyPlanes = [enemy1, enemy2, enemy3, enemy4]

        // Generate a random index
        let randomIndex = Int(arc4random_uniform(UInt32(enemyPlanes.count)))

        // Get a random enemy
        newEnemy = (enemyPlanes[randomIndex])

        // Added randomEnemy's physics
        newEnemy.physicsBody = SKPhysicsBody(rectangleOfSize: newEnemy.size)
        newEnemy.physicsBody?.dynamic = false
        newEnemy.physicsBody?.usesPreciseCollisionDetection = true
        newEnemy.physicsBody?.categoryBitMask = PhysicsCategory.EnemyMask
        newEnemy.physicsBody?.contactTestBitMask = PhysicsCategory.BulletMask
        newEnemy.physicsBody?.collisionBitMask = 0

        newEnemy.removeFromParent()
        self.addChild(newEnemy)
    }

    func spawnEnemyFire() {

        enemyFire = SKScene(fileNamed: "EnemyFire")!.childNodeWithName("bullet")! as! SKSpriteNode

        enemyFire.removeFromParent()
        self.addChild(enemyFire) // Generate enemy fire

        // Added enemy's fire physics
        enemyFire.physicsBody = SKPhysicsBody(rectangleOfSize: enemyFire.size)
        enemyFire.physicsBody?.dynamic = false
        enemyFire.physicsBody?.usesPreciseCollisionDetection = true
        enemyFire.physicsBody?.categoryBitMask = PhysicsCategory.EnemyFire
        enemyFire.physicsBody?.contactTestBitMask = PhysicsCategory.PlayerMask
        enemyFire.physicsBody?.collisionBitMask = 0
    }

    func didBeginContact(contact: SKPhysicsContact) {

        if !self.gamePaused && !self.gameOver {

            // beginContact constants
            firstBody = contact.bodyA
            secondBody = contact.bodyB

            if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {    
                firstBody = contact.bodyA
                secondBody = contact.bodyB
            } else {   
                firstBody = contact.bodyB
                secondBody = contact.bodyA
            }

            // Contact statements
            if ((firstBody.categoryBitMask & PhysicsCategory.BulletMask != 0) && (secondBody.categoryBitMask & PhysicsCategory.EnemyMask != 0)) {   
                print("Bullet hit Enemy")
            }
            else if ((firstBody.categoryBitMask & PhysicsCategory.PlayerMask != 0) && (secondBody.categoryBitMask & PhysicsCategory.EnemyFire != 0)) {
                print("EnemyFire hit our Player")
            }
        }
    }
}

I tried to leave out the obvious code yet leave the code that would need to be know...sorry if it is lengthy. I have also tried checkPhysics functions as well and they cannot even recognize my objects. All SpriteNodes are passed to the didMoveToView function. Can anyone see what is wrong or have a reason why I am getting no contact at all?

1
Where do you call your startScene() method? I guess in didMoveToView. Does didBeginContact get triggered at all? Put a break point or a print statement at the very beginning of that method to see if it is executed.Whirlwind
My startScene function is called in the didMoveToView function. I used it for game restarting use. It seems as if it is not triggered at all. I will try a print statement as the first line of my didBeginContact function.Allen Boynton
It is as I believed that didBeginContact is not even triggered. Physics are in place, delegate is in place. Even if I had no contacts set, it should still get triggered. Does the placement of the function within the code matter? It has never before for me. Even using view?.showsPhysics = true, I can see in my simulator what objects have physics.Allen Boynton
didBeginContact method has to be placed inside of a class which conforms to SKPhysicsContactDelegate protocol. Rare mistake, but I've seen it happen is that people put this method inside of another method. If there is no contacts, didBeginContact won't be triggered. So try to put two bodies and set their physics settings correctly and see if didBeginContact is called.Whirlwind
The didBeginContact method is only under the class, not within another function. However, all of my SKSpriteNodes are each a function. The only one called within didMoveToView is my setupPlayer. Most all my other object functions are using a runAction.SKAction sequence to time the spawning and those are within the didMoveToView. May that be my error? The game works fine otherwise, just no contacts or collisions. I will put the spritenode methods directly into didMoveToView and do what you suggested.Allen Boynton

1 Answers

8
votes

The problem you are having is because all of your physics bodies are non-dynamic. Means their dynamic property is set to false. didBeginContact method will be called only if at least one of bodies is dynamic.

From the docs:

The dynamic property determines whether the body is simulated by the physics subsystem.

So non-dynamic bodies are certainly excluded from simulation. I can't find if there is somewhere documented that this applies to contacts, but from my experience, I am pretty positive that contacts won't work between two static bodies.

Hope this helps.