My Physics Contacts are not making contact in SpriteKit
Asked Answered
L

1

6

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?

Liriodendron answered 20/9, 2016 at 17:22 Comment(6)
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.Delk
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.Liriodendron
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.Liriodendron
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.Delk
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.Liriodendron
Checkout my answer. The problem is with dynamic property.Delk
D
9

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.

Delk answered 20/9, 2016 at 18:32 Comment(4)
It worked!! I changed all the dynamic properties to TRUE and all of my print to consoles went off like wildfire!! I thought I have tried that before but must have been on a different spritenode. I will implement some code to my contacts and see the reactions. You have no idea how long I have been racking my brain over this...Thank you, thank you, thank you!Liriodendron
@ABoynton No problem, glad to hear it worked :) In the meanwhile, take a peek into help section to find out what you should do when someone answers your question and learn more about how this site works. And welcome to the StackOverflow :)Delk
@Delk you are correct, if I remember correctly, when looking at the Box2D source, collision and contact run through the same functions, with contact happening first, setting some flag, and processing collision code afterwards. This was a while ago I looked at this though, so not sure if it changed, or what version of Box2D apple is using.Netta
@Netta If all goes through the same function, then this make sense. I think I saw somewhere in the docs earlier that this behaviour is mentioned, but I could be wrong, because I haven't found anything about it today. Still, this is how it works, obviously.Delk

© 2022 - 2024 — McMap. All rights reserved.