2
votes

So I made some adjustments to the ball in my game and to avoid an unwrapping optional value error, I commmented out the following

//didMoveToView
    self.physicsWorld.gravity = CGVectorMake(0, 0)
    self.physicsWorld.contactDelegate = self
    let fieldBody = SKPhysicsBody.init(edgeLoopFromRect: self.frame)
    self.physicsBody = fieldBody
    self.physicsBody!.affectedByGravity = false
    self.physicsBody!.usesPreciseCollisionDetection = true
    self.physicsBody!.dynamic = true
    self.physicsBody!.mass = 0
    self.physicsBody!.friction = 0
    self.physicsBody!.linearDamping = 0
    self.physicsBody!.angularDamping = 0
    self.physicsBody!.restitution = 1
    self.physicsBody!.categoryBitMask = CollisionTypes.Floor.rawValue
    self.physicsBody!.contactTestBitMask = CollisionTypes.Ball.rawValue

    // Prepare the ball - physics engine.
    ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.width/2)
    ball.physicsBody!.affectedByGravity = true
    ball.physicsBody!.restitution = 0.8
    ball.physicsBody!.linearDamping = 0
    ball.physicsBody!.friction = 0.3
    ball.physicsBody!.dynamic = true
    ball.physicsBody!.mass = 0.5
    ball.physicsBody!.allowsRotation = true
    ball.physicsBody!.categoryBitMask = CollisionTypes.Ball.rawValue
    ball.physicsBody!.contactTestBitMask = CollisionTypes.Floor.rawValue
    ball.physicsBody!.collisionBitMask = CollisionTypes.Floor.rawValue
}

I did that, my game launced but as soon as toucheBegan, the same error popped up. I do not know how to get around all of these unrwapping errors. I am new to swift and not sure how i should adjust thse physics. My contactBegin looks this:

func didBeginContact(contact: SKPhysicsContact) {
    let bitMaskAAndB = contact.bodyA.categoryBitMask == CollisionTypes.Floor.rawValue &&
                contact.bodyB.categoryBitMask == CollisionTypes.Ball.rawValue
    let ballAndBoardMask = CollideType.Ball.toMask() | boards.usedCollideMasks

    // ball and board, handle it by board delegate
    if bitMaskAAndB | ballAndBoardMask == ballAndBoardMask {

        let boardNode: SKNode! = contact.bodyA.categoryBitMask == CollideType.Ball.toMask() ? contact.bodyB.node : contact.bodyA.node
        let board = boardNode.bind as! BoardDelegate
        board.didBeginContact(boardNode, ball: ball, contact: contact, game: self)
    }

    // ball and ceil => stop game
    else if bitMaskAAndB == CollideType.toMask([.Ball, .Ceil]) {
        stopGame()
    }

    // ball and floor => stop game add explosion or fall off screen
    else if bitMaskAAndB == CollideType.toMask([.Ball, .Floor]) {
        stopGame()
    }
}
func didEndContact(contact: SKPhysicsContact) {

    let ballAndBoardMask = CollideType.Ball.toMask() | boards.usedCollideMasks

    // ball and board, handle it by board delegate
    if contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask | ballAndBoardMask == ballAndBoardMask {

        let boardNode: SKNode! = contact.bodyA.categoryBitMask == CollideType.Ball.toMask() ? contact.bodyB.node : contact.bodyA.node
        let board = boardNode.bind as! BoardDelegate
        board.didEndContact(boardNode, ball: ball, contact: contact, game: self)
    }

}

EDIT: I set up my physics world to the suggestions you made and adjusted my didBeginContact fun however with the following code, I am getting multiple errors regarding: cannot convert value of type bool to UInt32.

PS. thank you for your help and patience, this is all new terminology and Im doing my best to understand. :)

1
Just use optional binding and you'll be fine. Or you can use optional chaining. Means, when an underlying value might be nil, then you should access it in safe way using one of the methods above. Or you can just check if variable != nil. - Whirlwind

1 Answers

2
votes

I don't know your CollideType class or declaration, so my code below explain to you how to build a correct physicsWorld and physicsBody to don't have issues during the collisions.

But first: we're going to be using the categoryBitMask, contactTestBitMask and collisionBitMask properties in their fullest for this project, because we have very precise rules that make the project work:

  • categoryBitMask is a number defining the type of object this is for considering collisions.

  • collisionBitMask is a number defining what categories of object this node should collide with.

  • contactTestBitMask is a number defining which collisions we want to be notified about.

SpriteKit expects these three bitmasks to be described using a UInt32.Your bitmasks should start at 1 then double each time.

The code:

class GameScene: SKScene, SKPhysicsContactDelegate {
    var ball: Ball!
    let ballSpeedX = CGFloat(500)
    enum CollisionTypes: UInt32 {
        case Field = 1
        case Ball = 2
    }
    override func didMoveToView(view: SKView) {
        // ball
        ball = Ball(imageNamed:"colorBall.png")
        ball.position = CGPointMake(self.ball.frame.size.width,self.ball.frame.size.height)

        // Prepare the world - physics engine.
        self.physicsWorld.gravity = CGVectorMake(0, -6)
        self.physicsWorld.contactDelegate = self
        let fieldBody = SKPhysicsBody.init(edgeLoopFromRect: self.frame)
        self.physicsBody = fieldBody
        self.physicsBody!.affectedByGravity = false
        self.physicsBody!.usesPreciseCollisionDetection = true
        self.physicsBody!.dynamic = true
        self.physicsBody!.mass = 0
        self.physicsBody!.friction = 0
        self.physicsBody!.linearDamping = 0
        self.physicsBody!.angularDamping = 0
        self.physicsBody!.restitution = 1
        self.physicsBody!.categoryBitMask = CollisionTypes.Field.rawValue
        self.physicsBody!.contactTestBitMask = CollisionTypes.Ball.rawValue

        // Prepare the ball - physics engine.
        ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.width/2)
        ball.physicsBody!.affectedByGravity = true
        ball.physicsBody!.restitution = 0.8
        ball.physicsBody!.linearDamping = 0
        ball.physicsBody!.friction = 0.3
        ball.physicsBody!.dynamic = true
        ball.physicsBody!.mass = 0.5
        ball.physicsBody!.allowsRotation = true
        ball.physicsBody!.categoryBitMask = CollisionTypes.Ball.rawValue
        ball.physicsBody!.contactTestBitMask = CollisionTypes.Field.rawValue
        ball.physicsBody!.collisionBitMask = CollisionTypes.Field.rawValue

        //ball.physicsBody!.categoryBitMask = CollideType.Ball.toMask()
        //ball.physicsBody!.collisionBitMask = CollideType.toMask([.Scene, .Ceil, .Floor]) | boards.usedCollideMasks
        //ball.physicsBody!.contactTestBitMask = CollideType.toMask([.Scene, .Ceil, .Floor]) | boards.usedCollideMasks
        ball.hidden = false
        self.addChild(ball)
    }
    func didBeginContact(contact: SKPhysicsContact) {
        if (contact.bodyA.categoryBitMask == CollisionTypes.Field.rawValue &&
            contact.bodyB.categoryBitMask == CollisionTypes.Ball.rawValue) {
            print("contact between field and ball")
        }
    }
}

As you can see, the first important thing is to prepare the physicsWorld (track the boundaries within wich your physics must work), assign it a category and all the other parameters need to interact with the other objects. Then you build the physicsBody of your object , in this case the ball.