Welcome to my Day 70 of Learning how to code with Swift.
If you are not new to this article series you will know I am following Paul Hudson’s initiative called 100 Days of Swift which, in short, is his book and web content called Hacking with Swift updated for Swift 5 and fed in small bits to the audience. I plan to continue learning Swift after these 100 days come to an end so I will not limit myself to Paul’s material as I want to experiment with other kinds of pedagogy as well.
Today is Challenge-Day so we need to perform the usual 12-question review and then complete the three challenges. Let’s get started.
Here is what we learned during this game-project
Timerclass lets us run code after some time has elapsed, either once or repeatedly. To be honest, we just used the class, nothing was really explained to us. As the challenge involves delving more into the topic we will see what we can learn. I still think that one chooses a teacher for how he explains things to you and not for how he leaves you alone in the darkness without a flashlight, but we will see how things go.
- Particle emitters start with no particles when they are created. Yes, they seem to have an internal timer that fires up when the emitter is created. This is why we used the advance in time method.
- Any time we use
#selectorto call a method, we must mark that method as
@objc. This should feel quite comfortable by now.
SpriteKitpositions objects from their center by default. I don’t recall this being explained but judging by how fast the preaching goes I may have very easily missed that (even though I read the notes after watching the video).
- We can create an
SKSpriteNodefrom a texture, from an image name, or from a color. I guess we have done the first two of them but I will browse the initialisers to see if there is indeed such a thing.
- It’s possible to copy a SpriteKit particle file from one project to another. Yes, just drag it from one to the other and remember to check that the right checkboxes are marked.
SKLabelNodecan draw any fonts available to our game. Here I had chosen the other option out of confusion, which was: “A physics body’s category bitmask lets us set which collisions we wanted to be notified of”. We saw all of the kinds of bit-mask in these projects but only in practice, we never ever walked down the explanation and theoretical part of it, which would have really helped it. Remember what I said during my last article? Repetition is nothing without understanding!
velocityproperty of a physics body controls how fast it’s moving in a particular direction. I have never really understood if there is a real difference between -velocity- and -speed-. Bah…
- SpriteKit can create pixel-perfect collision detection by examining the pixels in a sprite’s texture. I guess this uses integral calculus to define the shape and the area within.
update()method is called once every frame, and lets us make changes to our game. Just for your information, this is the definition of the
Do not call this method directly; it is called by the system exactly once per frame, so long as the scene is presented in a view and is not paused. This is the first method called when animating the scene, before any actions are evaluated and before any physics are simulated.
- Creating a new
SKEmitterNodefrom a filename returns an optional. Yes, because that file may not exist.
- Swift has two property observers:
didSet. The first is activated just before the property value changes and the second one just after it has been changed.
Challenge 1: stop the player from cheating by lifting their finger and tapping elsewhere — try to implement
touchesEnded() to make it work.
The first nice thing is that as soon as one adds the
touchesEnded method and tries to look for a set of instructions in the Documentation it gets nothing returned. Nice!
Anyway, as this is an
override method I guess I do not need to care about whether it is perceiving my previous touches. I will just try and make the player explode if the finger is lifted.
So, I simply copied the code from the
didBegin(contact:) method into the
touchesEnded one. It works as the player waits for a first touch to happen and then if I lift the finger at any time the player just explodes. The only little issue—but this is daughter of this app being just a skeleton with no chance of being a finished one—is that if I keep tapping I get more explosions.
I solved it by wrapping the code inside an
if statement which checks if the
children array contains the player. If not, this method will just do nothing. I could add
else return to avoid leaking memory, maybe?
Challenge 2: make the timer start at one second, but then after 20 enemies have been made subtract 0.1 seconds from it so it’s triggered every 0.9 seconds. After making 20 more, subtract another 0.1, and so on.
Note: you should call
gameTimer before giving it a new value, otherwise you end up with multiple timers.
So, let’s go in order: I created a
Double variable set to
0.0 with a
didSet property observer that would print the current time interval to the console. This is just for debugging purposes and may go away once we are sure things work.
I then created a
numberOfEnemies integer variable set to 0 with a
didSet property observer that would print the number of enemies (just for debug, again) and then, if the number of enemies would be multiple of 20 (using the new Swift 4.2
.isMultiple(of:) it would decrease the time interval variable by
0.1, invalidate the timer and create a new one with the new time interval value. Before Swift 4.2 we would have used the modulo operator (
%) coupled with an
&& numberOfEnemis > 0 to avoid the first trigger.
Down below I added a
timeInterval = 1.0 line to trigger the console printing and to set the timer the first time and simply changed the
0.35 hardcoded value to
timeInterval. Much more elegant, right?
Finally, inside the
createEnemy method, I added a
numberOfEnemies += 1 after the add child line.
Challenge 3: stop creating space debris after the player has died.
I created a new branch from the first challenge’s one because it is not said everyone would like the game to increase in difficulty.
This challenge is quite simple: after the
isGameOver = true line in the
touchesEnded methods, simply add a
gameTimer?.invalidate() call and that’s it.
Seeing the repetition going on in our code I have created a new
stopTheGame method, put the explosion code inside and then called the method inside
didBegin, saving about 8 lines of code.
I think I will now merge these changes from challenges 1 and 3 into
master and then just refactor the other challenge.
That’s it for today’s challenges.
You can find the code here. If you want to see challenge 1 and 3 you can simply check the master branch as they provide very useful additions to the game. If you want to see the challenges one by one check the branches that are named according to them, bearing in mind that I have applied challenge 3’s code to all of them in any case.
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!