Day 47 - 100 Days of Swift

3 minute read

Project 11 (part 3)

Day 47 is the third part of the eleventh project. It is the challenge day. You add a particle emitter to give a little flair when a ball is destroyed. He challenges you to use other images than just the red ball for the ball (he provides a few other options), making it a random color each time. He challenges you to only let the user drop a ball from the top of the screen. And he challenges you to give the player a 5 ball limit and to remove each obstacle that they hit, so that the user tries to see if they can clear all the boxes with 5 balls.

First, you add the particle emitter in destroy(ball:):

1
2
3
4
if let fireParticles = SKEmitterNode(fileNamed: "FireParticles") {
    fireParticles.position = ball.position
    addChild(fireParticles)
}

That runs this little animation whenever a ball is destroyed:

Gif of fire particle animation

To add the other images, I pulled the file names out of the Bundle, saved them in an array, and then grabbed a random name from that array when I create a ball:

1
2
3
4
5
6
7
8
9
10
11
12
13
var ballNames: [String] = []

// In didMove(to:)
let url = Bundle.main.bundleURL
if let files = try? FileManager.default.contentsOfDirectory(atPath: url.path) {
    ballNames = files.filter({ $0.hasPrefix("ball") && !$0.contains("@2x") })
    print(ballNames)
} else {
    fatalError("Couldn't load any ball names")
}

// In makeBall(at:)
let ball = SKSpriteNode(imageNamed: ballNames.randomElement()!)

I actually did the second challenge yesterday, so I’ll just link to that here.

For the third challenge I first added a label to let the user know how many balls they have left:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var remainingLabel: SKLabelNode!

var remainingBalls = 5 {
    didSet {
        remainingLabel.text = "Remaining: \(remainingBalls)"
    }
}

// In didMove(to:)
remainingLabel = SKLabelNode(fontNamed: "Chalkduster")
remainingLabel.text = "Remaining: 5"
remainingLabel.horizontalAlignmentMode = .right
remainingLabel.position = CGPoint(x: 980, y: 660)
addChild(remainingLabel)

Then I verify the count is greater than 0 and subtract from the count each time I make a ball:

1
2
3
// In makeBall(at:)
guard remainingBalls > 0 else { return }
remainingBalls -= 1

Then I added a name to the boxes, so I can detect collisions with them and add the code to remove them when they have a collision with a ball:

1
2
3
4
5
6
7
8
// In makeBox(at:)
box.name = "box"

// In collisionBetween(ball:object:)
} else if object.name == "box" {
    score += 1
    object.removeFromParent()
}

I also added one more ball each time the user hits a green slot, just to make things a little easier:

1
2
3
4
5
if object.name == "good" {
    destroy(ball: ball)
    remainingBalls += 1
    score += 1
}

With that, it looks like this:

Gif of working app.

You can find my version of this project at the end of day 47 on Github here.