Reflections on these first 100 days of learning
What a mighty journey it has been, and this is just the first stop! From now onward I will just tag my learning as simply “Learning Swift” while the day-count will get going on. My next goal is to see how many days I can put together in the first year, that is, by January 31st 2020.
The goal is obviously the most-famous #301DaysOfCode even if I find that challenge a bit unrealistic. They say that you need to study for 301 days and then look for a job for the other 64 days. But this is no human thing. You need to rest a bit at least every month or couple of months.
I will not be here for the next two days as I am travelling but I will resume my learning on Wednesday. This is the plan:
- Finish the 100 Days Of Swift program by Paul Hudson (and in the meantime I guess WWDC will drop so I may be delayed by watching videos or what else).
- Hopefully Paul will update the next remaining projects (even if I doubt it will be anytime soon), in which case I will go on and finish them with all the challenges and the consolidation days
- If this doesn’t happen by when I am over with point 1 or while I’m waiting for it, I want to go back to project 1 and review everything done until now and create my coding snippets library so that I just need to know what I want to do and I can grab the code and modify it accordingly. Reviewing also will allow me to go back and see some challenges with another eye (I’m looking at you, project 19!)
- Once this is done (and if point 2 has not come yet) I want to finish the Ray Wenderlich video series of the Swift Apprentice course, which I left to enrol into point 1 at the beginning of March.
- Then I want to finish the Angela Yu Udemy course I had purchased back in October (and if she has released the updated iOS13 version, go with that one).
- This is optional but I have the Devslopes iOS12 course which I may or may not look at. There are so many other things, like the AppCoda course which looks nice.
- Next I would like to make the most out of my Ray Wenderlich video subscription by studying as many subjects as possible and, possibly, start putting some simple apps on the App Store. For this I need to get my hands dirty with some Designer app like Illustrator (but with my subscription expiring in a few days with no intention from my side to renew it at that blood-sucking price) so that I can create my own Assets and my own Icons. Maybe the course by Michael Flarup would be a good one to start. Someone told me that Ray Wenderlich videos are very vague and they do not go in-depth with topics enough to really learn something. I will see how this turns out and, in any case, sooner or later, I will go with …
- Pro Swift book and the ObjC.io advanced books. They are absolutely due! Also I would like to finish the Ray Wenderlich books Swift Apprentice and iOS Apprentice because they look much better than the videos and, yes, in the videos they tell you to buy the books if you want to know more.
- My true goal in programming is still macOS so I will go through the Hacking with macOS book, plus another book I have seen but which has not been updated since Swift 3.
- … any further idea?
The good thing of making plans is that you realise how much you have already bought and how much you do not need to buy any further! I feel that with all this material I will have enough to keep me busy for this first year in Swift.
Of course, if you feel that I should do anything differently, feel free to let me know down in the comments.
Now I need to learn Project 29!
Hacking with Swift – Learning Project 29
I really like what Paul wrote at the beginning of this project and I would like to quote it:
One of the goals in this series is to give you so much practice with the core fundamentals of app building that when it comes to you setting out to starting your own apps you’ll know exactly how to begin. That doesn’t mean you’ll know everything like the back of your hand, because no one can (or should!) memorize all this stuff. But at least you have a firm mental understanding of what’s possible, and you have and ever-increasing stash of your own code you can look back on as a reference for the future.
If only this had been said at the beginning of the course!
Setting up
Create a new iOS app based on the Game template, call it “Exploding Monkeys” and save it. Perform the usual file cleaning for all our SpriteKit projects, make this app iPad-only and working exclusively in landscape orientation and download the assets for this project from here. Don’t copy them in the project just yet, though.
Building the environment: SKTexture
and filling a path
Create a new Cocoa Touch Class file called BuildingNode.swift as a subclass of SKSpriteNode
. Fix the error by importing SpriteKit.
Back into GameScene.swift create, at the top of the file, a new enumeration to take into account the collision types. Put 1
for the banana, 2
for the building and 4
for the player.
Return to BuildingNode.swift. Add an UIImage
implicitly unwrapped property called currentImage
, then write the setup()
method. This sets the name of the node to “building”, the current image property to the return value of the yet unwritten drawBuilding(size: size)
method, the node’s texture to an SKTexture
created from the current image, before finally calling the configurePhysics()
method.
Now write this last one method: it sets the physics body property of the node to an SKPhysicsBody
with an implicitly unwrapped texture and its size, sets isDynamic
to false, assigns the building raw value as category bit mask and the banana raw value for the contact test bit mask. Once more—because I am still not solid on this topic—the contact test bit mask is a mask that defines which categories of physics bodies cause intersection notifications with this physics body.
Then, proceed on to write the long-awaited drawBuilding
method. Inside it, create a new Core Graphics renderer context with the size of the node, then create an image which is the returning value of the renderer.image
method call. In the closure, add the ctx in
parameter and start filling in the rest. Create a rectangle at 0, 0
with size.width
and size.height
as dimensions, declare a UIColor
and initialise it through a switch statement that cycles through three cases (Int.random(in: 0...2)
). Here we meet something new, which is initialising a color with the UIColor(hue:saturation:brightness:alpha)
initialiser. The Documentation doesn’t say anything useful and, again, it is assumed that one knows what the HSB color space is. Do you? I don’t.
Performing a quick search on the Internet it seems that this color space is defined in terms of three components:
- Hue, the color type (such as red, green). It ranges from 0 to 360 degrees, with red at 0°, green at 120°, blue at 240° and soon.
- Saturation of the color which ranges from 0 to 100%. Sometimes it is called “purity”. The lower the saturation, the greater the greyness is present and the more faded the color will appear.
- Brightness, again from 0 to 100%. It is a non-linear transformation of the RGB color space.
If you have really nothing else to do today you can read this article if you think you can understand the math involved in it. I did not read it … Sometimes ignorance is just bliss…
Anyway, however Paul got to those values seems not to be our right to know so we will just copy them. Then, just setFill()
on the generated colour, add the rectangle in the context and draw the path using the fill drawing mode. I still have a bit of confusion in my head about when to use .drawPath
and when not but I think it will just come with time.
Then, create two colours to define when the light should be on and when not. Then, through a nested loop, create the windows using the stride(from:to:by:)
method and a Bool.random()
control flow statement to assign the proper colour. Finally, return the image.
Back into GameScene.swift add a new array of BuildingNode
s property and, in didMove(to:)
create a background color and call createBuildings()
which we have not written yet.
Now write it: declare and initialise a CGFloat
with a value of -15
so that we are making the building creation start slightly off-screen (it gives the feeling that the buildings are continuing left and right). Then, as long as that variable is less than 1024
(i.e., using a while
loop), initialise a size
property with width equal to a random value between 2 and 4 multiplied by 40 and height equal to a random value between 300 and 600. Increase the currentX
property by that amount + 2
so that it seems that buildings are slightly separated between each other. Create a building node with the red color and the random size, position it so that it respects SpriteKit positioning rules (the center of the sprite), call the setup()
method on the created building and add it to the buildings
array after having added it as a child to the scene.
Mixing UIKit
and SpriteKit
: UISlider
and SKView
To pass data between the game scene and the view controller we need to create some sort of reference between the two, making sure that we are not creating a strong reference cycle.
Inside GameScene.swift create a new property weak var viewController: GameViewController?
(watch out, the text in the book makes it implicitly unwrapped). Back in GameViewController.swift create a var currentGame: GameScene?
(also here optional, not implicitly unwrapped). In viewDidLoad
write, after the code that presents the scene:
currentGame = scene as? GameScene
currentGame?.viewController = self
This is all already happening but we need it to allow communication between the two files.
Now some storyboard work. For sake of brevity, please grab the file in the repository and copy the values, or read Paul’s article, there is no point for me just to repeat numbers! Create outlets for the sliders, the labels and the button plus actions for the sliders and the button.
Fill in the two methods for the sliders so that they update the text of the respective labels with their integer values. Then, at the end of viewDidLoad
, call them with self
as parameter. Big warning again: if you follow the book, you will get a warning so… be warned, the code, is slightly different here.
Now fill in the launch method which hides all the of the interface apart from the player’s label and calls the launch method from the game scene (which we have not written yet).
Finally write an activatePlayer
method that accepts a number
integer parameter. If that parameter is 1
the player’s label’s text will say “<<< PLAYER ONE” otherwise is will say “PLAYER TWO >>>”. Then it will show the interface back.
Unleash the bananas: SpriteKit texture atlases
Again, a lot of code to write in these games!
In GameScene.swift add three SKSpriteNode
implicitly unwrapped properties, one for each player and one for the banana. Also add an integer property called currentPlayer
and give it a value of 1
.
Now write the createPlayers()
method: set the player1
property to be a sprite note with an image named “player” and set its name to “player1”. Create a round physics body for it and give it sensible category, collision and contact test bit masks. Turn off its dynamics. Extract the second building from the buildings array and position the player atop it (watch out for the formula needed for the Y position) before adding it to the game scene.
Repeat all the procedure for the second player, changing the code where necessary. Now call this method at the end of didMove(to:)
Now create a New Sprite Atlas in the Assets catalog and drag all the pictures inside its Sprites folder.
Create a degrees to radians angle conversion formula (which returns the degree x PI ÷ 180).
Now fill in the launch
method. Inside set a speed constant equal to the Double value of the velocity parameter divided by 10.0
, set a constant to get the converted radians value of the angle parameter. Then, if for any case there is still a banana somewhere, remove it and set it back to nil
. Now initialise the banana sprite node with the proper image and name, give it a circular physics body and a sensible category bit mask, while for the collision and contact test you want to use the building |
the player so that both of them provoke a reactions. Finally for the banana, turn its .usesPreciseCollisionDetection
to true as it is possible that the launched banana would collide with a player between screen refreshes, creating undesired results, and add it to the scene. Next, if the current player is equal to 1
, position the banana 30 points to the left and 40 points above the player’s position and give it an angular velocity of -20
(which means 20 radians per second to the right, if I got it correctly). Create three actions, two with a .setTexture
call so that we can change the outlook of the monkey when it launches and one for waiting 0.15
seconds, wrap them in a sequence and ask the player sprite to run it. Last thing, set an impulse with a CGVector(dx: cos(radians) * speed, dy: sin(radians) * speed)
and assign this as the only parameter of the physics body .applyImpulse()
method.
Now repeat all this code in the else
part of the control flow statement with sensible changes for the second player.
Destructible terrain: presentScene()
In GameScene.swift make the view controller be the contact delegate of the physics world inside of the didMove(to:)
method.
Now, to avoid philosophical question about who hit who first, implement the didBeging
method. Declare two SKPhysicsBody
s, make it so that whichever has the lower category bit mask is the first body. Make sure that there is a node for each of the two bodies, then call the yet unwritten bananaHit(building:atPoint:)
method if the banana hit the building and destroy(player:)
if banana hit either of the two players.
Now write the destroy(player:)
method: create a new SKEmitterNode
from the “hitPlayer” file and set it at the player’s position. Remove the player and the banana from the parent. Then, with an asynchronous call, create a new game scene from the current scene’s size, set its view controller to be the current scene’s one and set the current scene’s view controller’s current game to be this new scene; change the player then set the current player of the new scene to be the current player (which we just changed); finally, call the doorway transition as parameter of the presentScene
method of the current scene’s view.
Then write the bananaHit(building:atPoint:)
: check that the building parameter is a BuildingNode
(this shouldn’t be necessary, but this course is about practical and nice to read code, and most of all, about SAFE code so, why not continue to be coherent?), convert the point in this node’s coordinate system to the coordinate system of the building node then call the hit(at:)
method (not yet written) on the building for the building location. Create a new emitter node with the “hitBuilding” file at the contact point and add it to the scene. Now set the banana’s name to be an empty string in case the banana would happen to hit two buildings at once, remove it from parent and set it back to nil
before calling the changePlayer
method.
Now write this last method so that if the current player is 1
it has to become 2
and viceversa. Then call the activatePlayer
method on the view controller property of the game scene.
Now go to BuildingNode.swift and create the hit(at:)
method. First convert the point from the SpriteKit coordinate system to the CoreGraphics one, then create a new renderer with the size of the node, then start writing the image
method. First, draw the current image at .zero
so that we have it in the current context, then create an ellipse for the collision point, set its blend mode to clear and draw the path using the fill drawing mode. Now change the texture of the building node to be a new texture from this image and set the current image to this image as well. In practice we are just swapping out one image with another one but this is all happening very, very fast, so fast that we cannot realise it. At the end of the method call the configurePhysics
method.
Last bu not least we need to decide what to do if the banana goes off screen (for example, if we launched it too hard). To solve this, implement the update
method in the GameScene file. Check that the banana is not already nil
, then if the absolute value of the banana’s Y position is greater than 1000
, remove it from the parent node, set it back to nil
and change the player!
Done!
Enjoy your game!
This concludes Project 29 and prepares us for tomorrow’s challenges, which I will face on Wednesday!
You can find the finished code for this project 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!