Time for another challenge! This is Day 51 for me and I will tackle Day 47 from Paul Hudson’s 100 Days of Swift initiative. I will keep trying to catch up as much as possibly but with every project becoming longer and each challenge getting…well, more challenging, it gets quite hard!
I will also try to be more concise in my article writing; I do not want to repeat what Paul said and will just focus on a step-by-step reminder of what was to be done and a deep dive in the Documentation when needed.
Review
Here is what we learned with this project (among other things):
- We can add physics to a node without letting it be moved, by changing its
isDynamic
property to false. - A sprite node may or may not have a physics body attached.
- The alternative answer was proposing that we can control the density of a sprite by setting its
restitution
property. This is false becauserestitution
controls the “bounciness” of our sprite.
- The alternative answer was proposing that we can control the density of a sprite by setting its
collisionBitMask
determines what objects a node bounces off, andcontactTestBitMask
determines which collisions are reported to us. This was the hard part to understand of the project.- We can adjust whether a node is positioned in front of or behind other nodes by setting its
zPosition
property.- Here the alternative was hard as well: “the
touchesBegan()
method is called the first time any touch happens in our game, and never again”. We know this to be false but the reason may be found already in the method’s name: “touches”, plural! I guess there may be a time interval after which the method stops listening to touches.
- Here the alternative was hard as well: “the
- Xcode has a built-in graphical particle editor that lets us preview changes live.
- When collisions happen, we might get told that thing A hit thing B, thing B hit thing A, or potentially both.
- We can find the location of a touch in our game scene by using the
location(in:)
method. - The
toggle()
method of Booleans flips its value fromtrue
tofalse
or vice versa. - Both
SpriteKit
andUIKit
measure angles in radians.- I was a bit confused here because the alternative was “We can add text to our game scenes using
SKLabel
”, which is also true right? Well… it turns out it isn’t and that it would have beenSKLabelNode
!
- I was a bit confused here because the alternative was “We can add text to our game scenes using
- We can control the behaviour of nodes using
SKAction
. - Blend modes affect how nodes are drawn.
SKSpriteNode
stores one sprite for our game scene.
Challenges
Challenge 1: the pictures we’re using in the app have other ball pictures rather than just “ballRed”. Try writing code to use a random ball color each time they tap the screen.
I created a global variable like this:
let availableBalls = ["ballBlue", "ballCyan", "ballGreen", "ballGrey", "ballPurple", "ballRed", "ballYellow"]
I initially created it inside the touchesBegan
method but then I told myself: “Do we really need to create an destroy this property every time?” Sure, having it globally may require constant resources but, come on, this is a ball game so, shouldn’t I give importance to the balls?
Then in the part of code where a new ball is created I replaced the hardcoded “ballRed” with this:
let ball = SKSpriteNode(imageNamed: availableBalls.randomElement() ?? "ballRed")
I preferred to use nil-coalescing rather than force-unwrapping.
That’s it. On to the next one!
Challenge 2: right now, users can tap anywhere to have a ball created there, which makes the game too easy. Try to force the Y value of new balls so they are near the top of the screen.
I changed the location of the ball when it gets created to this:
ball.position = CGPoint(x: location.x, y: 768)
It just works! Great! On to the next one which seems the hard one!
Challenge 3: give players a limit of five balls, then remove obstacle boxes when they are hit. Can they clear all the pins with just five balls? You could make it so that landing on a green slot gets them an extra ball.
Mmm… I tried it and… I managed it?
It was strangely simple and therefore I am suspicious about it. I wonder if someone may look at my code and tell me if it is fine or not.
So: I created a numberOfBalls
property and set it equal to 0
. Then, in the ball’s creation code, after setting the ball’s name, I increased the numberOfBalls
by 1 and set a condition as follows:
if numberOfBalls > 5 {
if let fireParticles = SKEmitterNode(fileNamed: "FireParticles") {
fireParticles.position = location
addChild(fireParticles)
}
return
}
I tried to add an alert controller with a willSet
property observer but then I could not call the present
method on it… I have no idea why… Possibly because we are not using UIKit
but, is it possible to have an alert controller in SpriteKit?
Anyway, this is a simple shortcut but it will just make the screen “explode” if one has created more than five balls (of course I could tweak the graphics but by now the matter is functionality).
In the collision
method I added a numberOfBalls -= 1
in the “good” case and, after giving a “box” name to our created box node, I added another else if
statement to remove the object instead of the ball…
I mean… that’s it, it just work but… I wonder if I have forgotten something…
Please give it a look here on GitHub if you have some spare time.
I would really appreciate your feedback.
Thank you!
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!