Welcome to my 69th day of Swift learning!
Yesterday I managed to catch up a little but I care very little about that. My only goal here is pure understanding, knowledge and that power feeling it gives you! I know, I have an inner Sith soul but they were not wrong in everything!
So, today it should be a game day. I am personally not fanatic with SpriteKit but by no mean I want to say anything against it as I am in no position for that.
Let’s get going. Today’s menu is: pixel-perfect collision detection, the
linearDamping among other things.
Create a new Xcode Project in the form of an iOS Game App and, if you want to follow how I do things, place it under source control, create a remote repository and push it to GitHub. Then from the Hacking with Swift repository, import the images for project 17 in the assets folder and the two scene-kit files into the project (I had a fresh install of Xcode so, on import, the “copy items if needed” was deselected and the radio button was on “create folder reference”, so please invert all that if you are in my same situation).
Perform the same cleaning routine described in previous SpriteKit projects.
Space: the final frontier
Thanks to Star Trek™ we can finally experiment with spaceships. This looks very familiar to a recent Swift on Sundays stream, which is nice.
First step: properties
starfield implicitly unwrapped
player implicitly unwrapped
scoreLabel implicitly unwrapped
SKLabelNode and a
score integer property set to
0 with a
didSet property observer that will update our label’s text when changed.
Second step: the
Set the background color to black, then initialise the three properties just declared above.
For the star field’s position set it to be
(1024, 384), that is at the center of the right edge, and advance its simulation time by 10 seconds. Add that child and set its
zPosition to be
-1, behind everything else. Why this is done after adding the child is beyond me, really, we have always done it the other way around before.
For the player put it at
(100, 384), that is towards the left edge but still in the center, set its physics body to have the
texture:size: initialiser so that its physical body tries to be as similar as possible to its real contours, set its physics body’s contact test bit mask to
1 so that we will be notified when other bodies collide with it and add the child.
For the score simply initialise it with any font you like, position it to
(16, 16), set its text horizontal alignment mode to be left-aligned and add the child.
Set the score to
0 (why?) so that it triggers the label…
Set the physics world’s gravity to be
.zero, we are in space after all and make it be its own contact delegate (the one we should send notifications to). To make this not-problematic add conformance to the
Bring on the enemies:
Add an array of strings to the properties section containing the string identifier of the images we added, an optional
Timer variable and a false boolean called
Timer class is defined as “a timer that fires after a certain time interval has elapsed, sending a specified message to a target object”. So, inside
didMove(to:) set that variable to be equal to a
Timer.scheduledTimer with five parameters: a
timeInterval of 0.35 seconds, a target of self, a selector of the
createEnemy method we have not written yet, a
nil and set
true to its repeating parameter. We are reminded that this
scheduledTimer initialiser not only creates the timer, but it also fires it.
@objc method. Be sure that you have a random element of the
possibleEnemies array, create a sprite from an image named after that element and position it to the right edge of the screen at a random vertical position and add it to the parent view.
For its physics body we want to respect these priorities: its texture needs to be respected as well as its size, we want a category bit mask of 1, exactly as our player sprite, we want it to move very fast toward the player (with a
.velocity value of
CGVector(dx: -500, dy: 0)), with a good spinning (
.angularVelocity = 5) but we want it to never slow down nor stop spinning so we set both its
angularDamping properties to
To make the app not become heavy as hell we need to remove the nodes when they are off screen and this is done in the
update delegate method with a looping over every node in the children’s array that will remove the node from its parent if its horizontal position will be less than
-300 (that is way off screen).
Still inside this method, if the game isn’t over, we want to increase the score by 1. If we build and run now we see the score increasing crazily fast which makes me ask: “what is that time interval?”. It seems that the
TimeInterval is just a type alias of the
Double data type, interesting. The Documentation says it is always specified in seconds so, in theory, our score should go up by 1 each second but instead it is spinning crazily upwards… Why is it so?
To handle the player’s movement we need to add the
touchesMoved method. First we check if there is a touch then we capture its location. We need to forbid the player from going off screen as that would be cheating so we are keeping the most external 100 points free. We then set the player’s position to the location we just captured.
When the player touches any debris the player should explode and be removed from the game.
That’s it, our game is finished.
I am a bit disappointed that we never ever polish down an app, they are all “useless” as they are. I mean, very interesting but if we need to learn how to make and then ship apps, we should also mindlessly repeat how to make the finishing touches! Right?
Anyway, here is the finished code.
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 don’t forget to visit the 100 Days Of Swift initiative page.
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!