0

Im trying to make a cowboy/gunslinger game. I need a collision between the projectile (coming from the first cowboy) to hit the second cowboy. However, the didBegin function isn't being run/called.

Ive tried messing with the physics bodies, by using code from previous games with no prevail. showPhysics = true.

Physics Category:

struct PhysicsCategory {
    static let none              :UInt32 = 0
    static let cowboy1Physics    :UInt32 = 0x1 << 1
    static let cowboy2Physics    :UInt32 = 0x1 << 1
    static let projectile1Physics:UInt32 = 0x1 << 0
    static let projectile2Physics:UInt32 = 0x1 << 0
}

Fire Bullet1 :

func fireProjectile1() {
    let projectile1 = SKSpriteNode(imageNamed: "bullet1")
    projectile1.position = cowboyNo1.position
    projectile1.position.x += 5
    projectile1.size = CGSize(width: projectile1.size.width / 4,           height: projectile1.size.height / 4)
    self.addChild(projectile1)
    projectile1.physicsBody = SKPhysicsBody(rectangleOf: projectile1.size )
    projectile1.physicsBody?.isDynamic = true
    projectile1.physicsBody?.categoryBitMask = PhysicsCategory.projectile1Physics
    projectile1.physicsBody?.contactTestBitMask = PhysicsCategory.cowboy2Physics
    projectile1.physicsBody?.collisionBitMask =  PhysicsCategory.none
    projectile1.physicsBody?.usesPreciseCollisionDetection = true



    projectile1.zPosition = 1
    var actionArray = [SKAction]()

    actionArray.append(SKAction.move(to: CGPoint(x: size.height + 1500, y: cowboyNo1.position.y), duration: TimeInterval(1.5)))

   actionArray.append(SKAction.wait(forDuration: 1))
    actionArray.append(SKAction.removeFromParent())
    print("fire")
    projectile1.run(SKAction.sequence(actionArray))


}

Cowboy2 Physics:

func createCowboyNo2() {
    cowboyNo2.position = CGPoint(x: 1800, y: 200)
    cowboyNo2.zPosition = 1
    cowboyNo2.size = CGSize(width: cowboyNo2.size.width / 2, height: cowboyNo2.size.height / 2)

    // Cowboy2 physics
    cowboyNo2.physicsBody = SKPhysicsBody(rectangleOf: cowboyNo2.size) // 1
    cowboyNo2.physicsBody?.isDynamic = true // 2
    cowboyNo2.physicsBody?.categoryBitMask = PhysicsCategory.cowboy2Physics // 3
    cowboyNo2.physicsBody?.contactTestBitMask = 2 // 4
    cowboyNo2.physicsBody?.collisionBitMask = PhysicsCategory.none
    cowboyNo2.physicsBody?.usesPreciseCollisionDetection = true
    physicsWorld.gravity = .zero
    addChild(cowboyNo2)

}

DidBegin:

func didBegin(_ contact: SKPhysicsContact) {
    // 1
    var firstBody:SKPhysicsBody
    var secondBody:SKPhysicsBody
    print("test")
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask{
        firstBody = contact.bodyA
        secondBody = contact.bodyB
        print("contact body's")
    }
    else{
        firstBody = contact.bodyB
        secondBody = contact.bodyA
        print("more contact bodys")
    }
    if (firstBody.categoryBitMask & PhysicsCategory.projectile1Physics) != 0 && (secondBody.categoryBitMask & PhysicsCategory.cowboy2Physics) != 0{
        projectileDidCollideWithCowboy2(nodeA: firstBody.node as! SKSpriteNode, nodeB: secondBody.node as! SKSpriteNode)
        print("remove child")
    }

DidCollideWithCowboy function:

func projectileDidCollideWithCowboy2(nodeA: SKSpriteNode, nodeB:     SKSpriteNode) {
    print("hit")
    nodeA.removeFromParent()
    nodeB.removeFromParent()
    score1 += 1
}

The expected output is for the bullet to be removed and for the console to print "test", but nothing is being printed. The projectile is firing and going straight past the cowboy. It seems as if the didBegin function isn't being called at all in the code when it should be running.

Swarm03
  • 3
  • 2
  • Hi - is your class is a SKPhysicsContactDelegate and have you made yourself the physics contact delegate? A step-by-step guide to setting up collisions and contacts : https://stackoverflow.com/a/51041474/1430420 – Steve Ives May 22 '19 at 11:51
  • Where is all of your initialization being done at? – Knight0fDragon May 22 '19 at 12:56
  • @SteveIves yes it is a contact delegate, as I put it next to the game scene. Thanks for the response! I tried the way you suggested and it still had the same affect. – Swarm03 May 22 '19 at 17:11
  • Your physics category definitions are a bit odd - you don't usually have more than one with the same value. If projectile1 and projectile2 are both defined as `0x1 << 0`, then just have a single `projectile` category of `0x1 << 0` – Steve Ives May 23 '19 at 09:07
  • @SteveIves it was the physics categories issue. I changed it up and it works. Thank you – Swarm03 May 23 '19 at 16:52

2 Answers2

0

It's not immediately obvious why it's not working.

Can you add this function to your code and when you've set up all the physics, make a call to checkPhysics() and it will list what collisions and contacts you have defined:

//MARK: - Analyse the collision/contact set up.
func checkPhysics() {

    // Create an array of all the nodes with physicsBodies
    var physicsNodes = [SKNode]()

    //Get all physics bodies
    enumerateChildNodes(withName: "//.") { node, _ in
        if let _ = node.physicsBody {
            physicsNodes.append(node)
        } else {
            print("\(node.name) does not have a physics body so cannot collide or be involved in contacts.")
        }
    }

    //For each node, check it's category against every other node's collion and contctTest bit mask
    for node in physicsNodes {
        let category = node.physicsBody!.categoryBitMask
        // Identify the node by its category if the name is blank
        let name = node.name != nil ? node.name! : "Category \(category)"

        let collisionMask = node.physicsBody!.collisionBitMask
        let contactMask = node.physicsBody!.contactTestBitMask

        // If all bits of the collisonmask set, just say it collides with everything.
        if collisionMask == UInt32.max {
            print("\(name) collides with everything")
        }

        for otherNode in physicsNodes {
        if (node.physicsBody?.dynamic == false) {
            print("This node \(name) is not dynamic")
        }
            if (node != otherNode) && (node.physicsBody?.isDynamic == true) {
                let otherCategory = otherNode.physicsBody!.categoryBitMask
                // Identify the node by its category if the name is blank
                let otherName = otherNode.name != nil ? otherNode.name! : "Category \(otherCategory)"

                // If the collisonmask and category match, they will collide
                if ((collisionMask & otherCategory) != 0) && (collisionMask != UInt32.max) {
                    print("\(name) collides with \(otherName)")
                }
                // If the contactMAsk and category match, they will contact
                if (contactMask & otherCategory) != 0 {print("\(name) notifies when contacting \(otherName)")}
            }
        }
    }
}

}

Steve Ives
  • 7,894
  • 3
  • 24
  • 55
0

It could be because you have not selected the physicsWorld delegate:

  • Be sure your class implements SKPhysicsContactDelegate:

    class GameScene: SKScene, SKPhysicsContactDelegate {
    
    ......
    
    }
    //Or whatever your class looks like
    
  • In your didMove:

    override func didMove(to view: SKView) {
    
        self.scene?.physicsWorld.contactDelegate = self
      ....
    
    }
    
Andrew21111
  • 868
  • 8
  • 17