Hacking with Swift – Learning Project 11

Welcome to Day 49&50 of my 100 Days of Swift challenge path report!

Today I will be learning Project 11 from the Hacking with Swift initiative by Paul Hudson (Day 45 to 46 + challenges on Day 47).

After reading the introduction to this project one cannot help to smile because when Paul warns us that we will be having a hard time… well… we’d better be prepared!

One thing I love are the daily quotes that Paul feeds us with and today’s one is probably my favorite one:

I’ve missed more than 9000 shots in my career. I’ve lost almost 300 games. 26 times, I’ve been trusted to take the game winning shot and missed. I’ve failed over and over and over again in my life. And that is why I succeed.

Michael Jordan

As a trained classical musician who used to spend 6 to 8 net hours per day practicing his instrument to be able to perform pieces that ranged between 1 and 40 minutes in length I feel at home with the feeling of failure, the disappointment you feel when things just don’t get right.

Ultimately, though, it is not how many times you fall that matters, but how many times you get up after that!

Today’s menu is mainly getting our hands dirty with SpriteKit!

Let’s get started!

Setting up

To get started we need to create a new Xcode project, this time choosing Game as its template, name it Pachinko (or Project 11 if you so prefer, in the end I have prefixed my working folders with project numbers), set the game technology to SpriteKit (which should be already selected if you have not programmed other games with different frameworks before) and deselect any of the three boxes down below that result selected for any reason.

Next, we need to be sure that our game will run on iPad only and that the orientation will be landscape only.

While creating this app I got this annoying error:

Your maximum App ID limit has been reached. You may create up to 10 App IDs every 7 days.

Configuring the iPad for development

Paul recommends using a real device for testing and so this is a great chance for me to configure my iPad Pro 3rd generation (12.9in) for development!

First of all I will connect my iPad via the USB-C charging cable to my 2016 MacBook Pro (still running High Sierra, I hope to upgrade as soon as I get to clone the SSD).

From within Xcode I will then go to Window > Devices and Simulators (shortcut Cmd-Shift-2) and authorise it when prompted.

Remember that the device must be unlocked to use the apps you are building and that if you do not have a strong internet connection (I have a 24Mbps one, which definitely it is not for these days) you should keep your iPad connected for faster transfers and build times!

Falling boxes: SKSpriteNode, UITouch, SKPhysicsBody

After playing a few seconds with the basic running template for SpriteKit we proceed to cleaning our GameScene.swift file, leaving it only with the didMove method and, possibly, adding the touchesBegan one in its empty form. All the rest of the code is not needed.

SpriteKit’s equivalent of Interface Builder is the Scene Editor. We should open it, select the big “Hello, World” label and delete it.

We should now notice that the Attributes Inspector for SpriteKit projects has a different icon. Inside it, we should look for Anchor Point (which determines the coordinates used by SpriteKit to position its “children”—funny, right?—). The default is 0.5; 0.5, which means in the exact middle of the screen. We should change these values to be 0; 0 (finally a system that works like ordinary geometry, thank you! It is really confusing for me to count positive Y going down…).

Last thing, we should change the size of the scene to be 1024 x 768, which is the size in points of a 9.7in iPad (SpriteKit will make the automatic conversion for us in case we use another device).

Oh, well, another last thing… let’s click on the Actions.sks file and delete it (selecting Move to Trash afterwards).

Next we need to import the assets for this project by dragging the files in the Hacking with Swift GitHub repository folder called project11-files into our assets catalog folder (always check the Copy items if needed box).

Place the background image

To place the background image we need to load its file and then position it to the center of the screen which in this case will have coordinates (512, 384). To load the image we need to use the SKSpriteNode class which has an incredibly long article in the Documentation that I will certainly go and study when I will need to master this subject more than what we are doing here.

The positioning is done via a Core Graphic point initialiser.

For the background we still need to perform two additions: adopt a blend mode (that is how a node is drawn) of .replace which is the 6th case in the SKBlendMode enumeration and which means to “replace whatever colour is found with the node’s one” and position it behind everything else setting its .zPosition (that is, its depth position in space geometry) to -1.

To add any node to a scene we need to call the addChild() method with the name of the node. Doing so effectively creates a node tree which has the potential of becoming incredibly complex!

React to touches on the screen

To make it possible for the user to interact with the screen we add the touchesBegan method which, as first parameter, gets a Set of UITouches (we recall that a Set is just like an array where each object can appear only once).

First we need to check if a touch happened and we can do so with our faithful if let syntax! Once we have captured the touch we can get its location (which returns a CGPoint) and store it in a property. Then, using another Sprite Kit node, we create a red square box of 64 x 64 points at the touch’s location. Finally, we add the child to the tree.

The physical magic of SpriteKit!

Right now the boxes are fixed in place like suspended in time where they are. To repair this really embarrassing situation we are going to add some physics spice to the mix calling the .physicsBody property on our box. The Documentation’s description for this property is very clear:

The physics body associated with the node.

Declaration

var physicsBody: SKPhysicsBody? { get set }

Discussion

The default value is nil, which indicates that the node does not participate in the physics simulation at all. If a physics body is provided, when the scene’s physics are simulated, the physics body updates the node’s position and rotates the node.

Amazing, right?

With this clever (and most evil!) act we added a physics power to our boxes but we also want our scene to wield the same awesome power! Inside the didMove(to:) method we then modify the default physicsBody property to perform an edge-loop (which I have no idea what it is, unfortunately) from the frame of the scene.

Whatever this means, the result is just simply amazing! For whatever reason I cannot upload videos here on WordPress… too bad…


Bouncing balls: circleOfRadius

At this point we have the sentence of the year:

You are not going to get rich out of red rectangles, so let’s use balls instead!

Paul Hudson

So, let’s remove all the box creation code and replace it with a, you name it, ball creation code! The new thing here is the kind of physics body we are using, that is a body which is a circleOfRadius half the width of the size of the ball! Such a complicated way of rendering the radius of circle, right?

Then, the restitution element is what makes the body bouncy or not. It is a CGFloat between 0.0 and 1.0 and therefore our ball is going to be quite bouncy!

Finally, we need something for our balls to bounce off, that is a bouncer!

In the didMove(to:) method we are going to create a new node from the image called “bouncer”, position it at the center of the bottom axis of the screen, give it the same physics body of our balls (just with proportionally bigger size, as you will shortly see) and turning off its dynamics—that is, when our balls will fall down crashing on these bouncers we do not want them to move, we want them fixed in place!—.

Next, we will write a method to create a bouncer by cut-pasting the creation code inside a makeBouncer(at:) method. Then we will call it back in the didMove(to:) method with five different (and evenly spaced) x values.


The menu for Day 46 is:

  • SKAction
  • SKPhysicsContactDelegate
  • SKLabelNode

It looks like a good lot so lets get moving!

Spinning slots: SKAction

Now we start talking about what our game will actually do and that is to drop the balls so that they land in good positions and not in bad ones! To do so we have quite a lot to do but it is not too hard to be sincere (and thankfully!).

First we need to fill the gaps with two types of target slots: good ones (green) and bad ones (red) —interesting here, if one would ever localise this app for the Chinese App Store one would have to invert those two colours because in China red is good and green is bad!—.

We create a new method called makeSlot(at:isGood:)and load the correct image accordingly. We then call it four times at precise x-points in our didMove(to:) method.

Now we can add a glow behind the image using the same technique as before and, finally, an action to make this glow rotate forever very gently. This is achieved using the SKAction class, a shamefully powerful class!

A few remainders:

  • angles are in radians, not degrees!
  • when we create an action it executes only once! But we can ask it to repeat forever!

So, we create the spin action calling the SKAction.rotate initialiser on a new property, make it spin forever and then running it on the slowGlow.


Collision detection: SKPhysicsContactDelegate

The collision detection we have put in place so far is great, it works but it has a big handicap: we are not collecting any information about it in code and therefore we cannot do anything about it. It is just happening!

Our game will determine a win or a loss depending on how many green or red slot they manage to hid. To achieve this we need to add a rectangle physics body to the slots, name them (and balls as well so that we know who made happen what!), make our scene the contact delegate of the physics world—that is, generate a message when any contact between two bodies happens so that we can interact with it—and create some methods to handle all this. It seems like a lot to do and, frankly, it really is!

Add rectangle physics to slots

This is simple enough for us SpriteKit experts: just before we add the slotBase to our scene we set its .physicsBody property to be a rectangle of its size—though I wonder where the height of this rectangle will be calculated, if within the scene or below, that is out of the screen—and we set it to be non-dynamic as we want it to be fixed in place.

Name the slots

Apple recommends to assign names to our nodes and then check those names with control-flow to determine what node it is. Fine, so in the ifGood-else block we add a line to each brace where we set the slotBase.name property to either “good” or “bad”. The same we will do for when we create the ball, calling it, you say it, “ball”. It seems trivial but it will become important very, very soon!

Delegation

Our next task is to make our scene become the contact delegate of the physics world, that is, the responsible for telling us what happened in regards to touches on the screen.

So, first things first: let’s conform to SKPhysicsContactDelegate and see what does this mean. This protocol implements methods that our app can implement to respond when physics bodies come into contact. According to the Documentations’s Discussion

An object that implements the SKPhysicsContactDelegateprotocol can respond when two physics bodies with overlapping contactTestBitMask value are in contact with each other in a physics world. To receive contact messages, you set the contactDelegate property of a SKPhysicsWorld object. The delegate is called when a contact starts or ends.

In the Important section we get notified that:

[We] can use the contact delegate to play a sound or execute game logic, such as increasing a player’s score, when a contact event occurs.

This is exactly what we will be doing and even if Paul said we are ignoring by now what the contactTestBitMask is I am way too curious to skip clicking that attractive blue link in the Documentation. I will not pretend to understand everything but, I just need to know what that is.

A contactTestBitMask is a mask that defines which categories of bodies cause intersection notifications with this physics body. Yes? … ok… a glass of water, a deep breath and we move on…

This variable is described as a UInt32(that is an unsigned 32-bit integer) with get and set capabilities. The Discussion says that

When two bodies share the same place, each body’s category mask is tested against the other body’s contact mask by performing a logical AND operation. If either comparison results in a nonzero value, an SKPhysicsContact object is created and passed to the physics world’s delegate.

This doesn’t help that much but tells us that if something happens when two bodies collide the delegate will be notified and, actually, that’s all we need.

Now, just under the line where we set the scene’s physics body, we want to set the scene (self) to be the physicsWorld.contactDelegate.

Now for the hard part (at least to grasp conceptually): in the touchesBegan method, just under the line where we set the ball’s bounciness (restitution), we add a line that sets the contact test bitmask of the physics body of the ball equal to its collisionBitMask. But what is this? The name is dangerously similar to the contactTestBitMask but the explanation is slightly different:

A mask that defines which categories of physics bodies can collide with this physics body.

So, in short, the contactTestBitMask sets which bodies should notify the delegate and the collistionBitMask sets “who can punch who”! We have already set the second one to everything so that they can collide and jump freely (unless they hit the slots of course) so setting the second equal to the first we will be notified of every single collision! Brilliant!

The philosophy of bouncing

But who did hit first?

Did the ball hit the slot, did the slot hit the ball or did both of them hit each other at the same time? Without a bit more of careful work we cannot know what happened and we do need to know which object is the ball!

Before writing the contact method we will write a method that will be called when a ball collides with something else and another one that will simply destroy the ball when we do not need it anymore (this last action is ball.removeFromParent()). These are two building blocks for the method we need to write now.

I cannot say I understood what this method does because, in any case, we then call the destroy method so… everything gets destroyed eventually. Theoretically we are checking if the first colliding object is a ball in which case we check in the other object is “good” or “bad” and destroy the ball, else if something else crashed into the ball we check if that object is “good” or “bad” and… destroy the ball. Uh?

I agree with Paul that we didn’t create a special case for when two balls collide together but to me this is the least of the issues because I have really not understood what all this is about and I sincerely hope it will be explained sooner or later.

Anyway, the contemporary collision of slot and ball can happen and if we do not manage it it will just crash our app. So the final version of the didBegin(_:) method is this one:

func didBegin(_ contact: SKPhysicsContact) {
    guard let nodeA = contact.bodyA.node else { return }
    guard let nodeB = contact.bodyB.node else { return }

    if nodeA.name == "ball" {
        collisionBetween(ball: nodeA, object: nodeB)
    } else if nodeB.name == "ball" {
        collisionBetween(ball: nodeB, object: nodeA)
    }
} 

We check that there were two separate contacts otherwise we get out of the method. I still do not understand what all this is about but… yes, this is definitely safer… Here is the game in its current state.


Scores on the board: SKLabelNode

To track scores and show this to the user we are going to use the same technique we used in UIKit just with slightly different syntax. We create a score label which is of type SKLabelNode, a property of type Int with a property observer that will update the label’s text and we place it on the screen in the top right corner. It is interesting that in SpriteKit the initialisation and the font choice are in the same initialiser. Maybe this optimisation will come to UIKit as well? We then update the score in our collisionBetween method so that a ball actually falling in one of the two slots will mean something!

Placing obstacles

The game as it is now is working, but it is way too easy! We need to add some obstacles for the balls to bounce off and, possibly unpredictably, change the direction of the balls. We create a new label for entering edit-mode and an editing mode boolean that will change the text every time that property is toggled. We create the label in the top left corner of the screen.

Now we need to distinguish between when the user taps on the screen to create a ball or to touch the edit label.

Between the location and the ball code we ask SpriteKit to give us a list of all the nodes (an [SKNode]) at the point that was tapped and to check whether the edit label is inside. If this question receives true as answer we will toggle the editingMode property. Now, if the editing-mode is activated (that is, the property is set to true), we create a box, otherwise we create a ball.

Of course we do not want to have all horizontal (or vertical, or any angle but always the same) obstacles so we need to use the zRotation property, that is how the object will be rotated according to the z-axys (which is, looking from our perspective, the axis that exits perpendicular from the screen of the device).

We will create each of our obstacle using “random” values for the size, the colour and the z-Rotation. The location will be our touch, its physical body will be a rectangle of its size and its dynamic will be set to false so that it sticks in place.

We have a game but we can cheat fairly easily: if we tap just above the slots we will just always win… Paul says we should fix this ourselves, but I wonder if this will be part of the challenges or not. Only time will tell!


Special effects: SKEmitterNode

This is the final touch for this project before we sign off and head toward the challenges. We need to import the “FireParticles” file Paul provided in the GitHub repository for Hacking with Swift into Xcode and change the destroy method to create an SKEmitterNode with that name at the ball’s position when it’s about to be destroyed!

We can also play with the particle editor with some incredibly results. I have to say that the way that inspector works is confusing to say the least. There is no popover explaining what any parameter does (Paul explained it but we do not know how values influence the results and, sincerely, trial and error is just too time consuming…).

In any case, that’s it for this project.

Thank you for sticking with me and see you in the challenges!

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.

The GitHub repository for this project is here.

Until the next one!


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!

Published by Michele Galvagno

Professional Musical Scores Designer and Engraver Graduated Classical Musician (cello) and Teacher Tech Enthusiast and Apprentice iOS / macOS Developer Grafico di Partiture Musicali Professionista Musicista classico diplomato (violoncello) ed insegnante Appassionato di tecnologia ed apprendista Sviluppatore iOS / macOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create your website with WordPress.com
Get started
%d bloggers like this: