Skip to content

Latest commit

 

History

History

day-046

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Day 46: Project 11: Pachinko, Part Two

Follow along at https://www.hackingwithswift.com/100/46.

📒 Field Notes

This day covers the second part of Project 11: Pachinko in Hacking with Swift.

I have a separate repository where I've been creating projects alongside the material in the book. And you can find Project 11 here. However, I also copied it over to Day 45's folder so I could extend from where I left off.

With that in mind, Day 46 focuses on several specific topics:

  • Spinning slots: SKAction
  • Collision detection: SKPhysicsContactDelegate
  • Scores on the board: SKLabelNode

Spinning slots: SKAction

SKActions have a neat paradigm where we essentially create SKAction instances, and then apply them to various nodes. Ultimately, this object-oriented structure leads into being able to compose various sequences and groups in immensely powerful ways. For now, though, we're just looking to create some spinning slots that will detect our ball drops:

func makeSlot(at position: CGPoint, isGood: Bool) -> SKSpriteNode {
    ...
    ...

    let slotGlow = SKSpriteNode(imageNamed: slotGlowFileName)

    let spinAction = SKAction.rotate(byAngle: .pi, duration: 10)
    let glowSpin = SKAction.repeatForever(spinAction)

    slotGlow.run(glowSpin)

    ...
    ...
}

Also, yes... those are radians being used. SpriteKit knows what's up 😎.

Collision detection: SKPhysicsContactDelegate

Collision detection in SpriteKit is orchestrated through a system of node contactTestBitMasks, collisionBitMasks, and categoryBitMasks. Apple has some good documentation explaining these intricacies.

For our purposes, we're mainly concerned about the ball's contactTestBitMask. The way we set this essentially determines which contact events we "sign up" to have handled by our GameScene's physicsWorld.contactDelegate (which, in this case, we're setting to be the GameScene itself).

func makeBall(at position: CGPoint) -> SKNode {
    ...
    ...

    // shortcut to sign up for all ball contact notifications
    ball.ballPhysicsBody!.contactTestBitMask = ballPhysicsBody.collisionBitMask

    ...
    ...
}

This just sets us up to handle a "contact" event every time our ball collides with another node — allowing our SKPhysicsContactDelegate's didBegin(_:) to detect such an event, and handle it further:

func didBegin(_ contact: SKPhysicsContact) {
    guard
        let nodeA = contact.bodyA.node,
        let nodeB = contact.bodyB.node
    else { return }

    if nodeA.name == NodeName.ball {
        handleCollisionBetween(ball: nodeA, object: nodeB)
    } else if nodeB.name == NodeName.ball {
        handleCollisionBetween(ball: nodeB, object: nodeA)
    }
}

Scores on the board: SKLabelNode

Stylistically, SKLabelNodes are pretty similar to UIKit's UILabels. The only real difference, as mentioned in Day 45, is the fact that — because it's a node in a scene — position's are based on the node's center:

func makeScoreLabel() -> SKLabelNode {
    let label = SKLabelNode(fontNamed: "Chalkduster")

    label.text = "Score: \(currentScore)"
    label.horizontalAlignmentMode = .right
    label.position = CGPoint(x: frame.maxX * 0.95, y: frame.maxY * 0.91)

    return label
}

Sweet. Furthermore, now that the label is in our scene, we can update it similar to the way we'd update a standard UIKit element in a view.

var currentScore = 0 {
    didSet {
        scoreLabel.text = "Score: \(currentScore)"
    }
}