I’ve just come back from my trip and will try today to face the challenges for Project 29. I highly doubt I will manage to complete them as I am dog tired after more than 16 hours of travelling over two days and a huge amount of work to do but I will do my best.
Let’s start with the review.
Review for Project 29 of Hacking with Swift
- First question, first mistake… nice! Anyway, the correct answer was: Setting a Core Graphics blend more affects all drawing from that point on, until another blend mode is set. This is true of other Core Graphics operations too, such as changing the current transformation matrix.
- SpriteKit can tell us the exact location where a collision took place. This will be in the coordinate space of our entire scene, but we can convert it so it’s relative to a specific node.
- The
abs()
function makes all numbers positive. If we feed it a negative number it will return the positive equivalent. This is what’s called the absolute value in mathematics. - The
stride()
function lets us count between two numbers in an interval we specify. For example, we can usestride()
to count in 10s. - SpriteKit scenes have a background colour. You can use a background image if you want, but sometimes a simple colour works great.
- If two objects have a reference to each other, one should be made weak in order to avoid a reference cycle. Although there are some cases where strong reference cycles aren’t a problem, it’s usually best to avoid them.
- SpriteKit texture atlases are more efficient than regular asset catalog images. They allow SpriteKit to load in a single image and share it across many nodes.
- We can move between two SpriteKit game scenes using a built-in animation from SKTransition. Apple provides a variety for us to choose from, and we can control their speed.
Bool.random()
returns eithertrue
orfalse
randomly. Think of it as being more or less the same asInt.random(in: 0...1) == 0
.- We can generate SpriteKit textures dynamically. This might be through Core Graphics, or even through Core Image. I’m not sure I understood the question, but I’m sure it’s just me at this point… everyone else out there is already a master programmer…
- We can make an
SKTexture
directly from aUIImage
. This allows us to generate textures dynamically. - Raw values for physics body bitmasks should be integers that are double the value of the previous case. This allows us to combine them together to make new bitmasks without causing collisions.
Done! Now the challenges! From what I’ve seen there will be a lot to laugh about!
Challenges for Hacking with Swift — Project 29
Challenge 1: add code and UI to track the player scores across levels, then make the game end after one player has won three times.
First of all I performed a general cleaning of the project as methods and properties are just thrown out there without a real sense.
Now, let’s try to make things in order. How do we track score? By creating a property for each player and making sure it doesn’t reset after each new level, two integer properties set to 0
in GameScene.swift should suffice. Inside the didBegin
method I added a line to the second and third if
statement, precisely player2score += 1
and player1score += 1
.
I then added two labels to the storyboard, connected them via outlets to the GameViewController file, set their text in viewDidLoad
and then provided two property observers in the GameScene one.
With this I managed to get the score updated to 1 and to persist into the second screen, but when the second game finished and the first started, nothing changed. Now I need to get some work done and I will come back later.
Here I am… so the change persisted to 1 but did not get updated to 2 (yes, player 1 won twice in a row). So let’s look for a reason behind this. It could be that there being no code triggering the update of the label’s text, the text remained to 1 while the variable went back to 0
and the new winning game put it back to 1. To see if this is the case I should see what happens if I win 3 times in a row or if I win some matches with the other player.
Winning the second game with the second player correctly loads the screen with 1-1 as a score. Winning the third game with player 2 should have brought the score to 1-2 but it remained on 1-1. Let’s move on and see… On what should have been 2-2 score remained on 1-1… I guess my intuition is correct, i.e., the variable gets reset to 0 as soon as we reload the game so the first update goes through but no one else afterwards…
I tried to put a conditional binding in didMove(to:)
but this didn’t help so I removed it. Now I’m going to print the value of the players’ scores and see if I can get something out of it.
Interesting… I won with Player 2 this time and the console printed “Player 2 Score: 0”, while the label correctly displayed 1… I do not really think I need to save scores between scenes? Should I? I now added a setting of both scores to 0 in didMove(to:)
, and it correctly printed out. Now we need to verify if transitioning to a new scene also calls that method again… Yes it does!
The score was correctly increased to 1 and then reset to 0. It seems I have to save and load scores… let me try…
Nice … I tried to put up the usual saving method but I got back failed to save score… very nice… The awful thing is that now not even the score property gets updated.
Very nice, another 30 minutes thrown away in the garbage… and I need to get back to work with this awful feeling… and you know what? I even know that all this is damned easy and I cannot do it… Damn it!
Somehow I solved it. There was something I did badly, sure, but other things I wrote exactly as before and it just worked this time. So, what did I do in the end? Created two properties like this:
var player1score = 0 {
didSet {
viewController?.playerOneScore.text = "PLAYER ONE SCORE: \(player1score)"
print("Player 1 Score: \(player1score)")
}
}
var player2score = 0 {
didSet {
viewController?.playerTwoScore.text = "PLAYER TWO SCORE: \(player2score)"
print("Player 2 Score: \(player2score)")
}
}
I then created a saveScore()
and loadScore()
method. In the didBegin
method, in the second and third if
statement, I added, AT THE TOP!, otherPlayerScore += 1
and saveScore()
. Quite obviously, doing this after the call to destroy
was not working! Finally, in didMove(to:)
I called loadScore()
at the beginning.
Now, how to reset things when we get to 3?!
I refactored the transition scene in its own method:
func startNewGame() {
let newGame = GameScene(size: self.size)
newGame.viewController = self.viewController
self.viewController?.currentGame = newGame
self.changePlayer()
newGame.currentPlayer = self.currentPlayer
let transition = SKTransition.doorway(withDuration: 1.5)
self.view?.presentScene(newGame, transition: transition)
}
Then I modified the destroy(player:)
method adding / condensing this after the removal of the banana:
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
if self.player2score == 3 || self.player1score == 3 {
let gameOver = SKSpriteNode(imageNamed: "gameOver")
gameOver.position = CGPoint(x: 512, y: 384)
gameOver.zPosition = 1
self.addChild(gameOver)
self.scene?.isUserInteractionEnabled = false
self.player1score = 0
self.player2score = 0
self.saveScore()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.startNewGame()
}
} else {
self.startNewGame()
}
}
That’s it, you will find the code for the finished challenge in the “Challenge29-1” branch of the project.
Challenge 2: add Auto Layout rules for the UI components in our storyboard, allowing them to remain positioned neatly regardless of which iPad size is used.
This was not easy because with a black background seeing all the red lines was not very automatic and, also, understanding what Auto Layout wants is a bit too much. I have learned, though, that you can put only ONE inequality in a set of constraints and then, if the engine suggests you to have more than one, then it is fine… I mean, as Paul said, it makes simple things complicated and … that’s it!
For the sake of brevity, here is my set of constraints, feel free to add yours in the comments:

Done, you can find the code, well… the Storyboard for this in the “Challenge29-2” branch of the project.
Challenge 3: use the physics world’s gravity to add random wind to each level, making sure to add a label telling players the direction and strength.
This is Day 103.
Mmm… let’s put it plainly… how the heck should we do this? Random wind???
Going through the code for GameScene.swift I decided to fold all of the method that I think will have nothing to do with the wind and only launch(angle:velocity:)
remained. Most probably we will have other methods but by now this is just preliminary cleaning.
Also in GameViewcontroller.swift there is nothing that makes me think that something needs to be changed there. Sincerely I have no idea where to start from. What is wind? A property? A method? I mean … come on …
I hate this but I looked on Stack Overflow for suggestions and found this old question that explains how to do this in Objective-C.
I now got to something but I have no apparent way to see if something is working or not. From the tests it seems that nothing different is happening so I am probably missing something. The applyForce
method works with Newton (which is a measure of Force —not the Jedi one) so I thought I could have added a mass to the physics body. I thought a banana could weight half a kilo (of course it doesn’t but, just to have a value) and this made the banana launch and explode on the player right above its head…
Reading the description of the mass it seems that this value is arbitrary and not really necessary as long as it remains coherent among the different bodies in the game… so, what am I missing?
By now I noticed that I was adding this method to the wrong place, that is before the applyImpulse
which I think doesn’t make so much sense.
Now it seems to work because using the same velocity the two players do not get as far. I asked for some advice on the community but no one answered so I guess it is fine like this.
You can find the finished project with all the three challenges here.
Please don’t forget to drop a hello and a thank you to Paul for all his great work (you can find him on Twitter) and be sure to visit the 100 Days Of Swift initiative page. We are learning so much thanks to him and he deserves to know of our gratitude.
He has written about 20 great books on Swift, all of which you can check about here.
The 100 Days of Swift initiative is based on the Hacking with Swift book, which you should definitely check out.
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!