RainCat: Lesson 1
How to make a simple SpriteKit game in Swift 3
This 5-part article series will be about making a simple SpriteKit game on iOS that explores the basics of what SpriteKit and GameplayKit have to offer.
We will scratch the surface of the new GameplayKit library, and we will use simple physics with SKPhysicsBody. We will also program this in this in Swift 3, since it is the latest and greatest that Apple has to offer. To get a taste of what you are creating, check out the store link for the completed project. It has some more bells and whistles than what we will accomplish here, but you can check on those additions later on our GitHub.
What you will need:
- A Mac of some sort
- Xcode 8 (at the time of this writing you will need the beta for Swift 3)
- Something to test on! In this case it should be an iPad to remove some of the complexity that developing for many screen sizes can bring to the table. The simulator works, but it will always have lag and run at a lower frame rate than a proper iOS device.
Time to make RainCat!
The game that we will build has a simple premise: we want to feed the hungry cat, but it is outside in the rain. RainCat ironically does not like rain all that much, and will become sad when it gets wet. To correct this issue, we must hold an umbrella above the cat to allow it to eat without getting rained on.
We will do the following in this article:
- Initial project setup
- Add a floor
- Alpha version of the rain drops
- Initial physics
- Rain drop spawning function
So let’s set up our project!
Open up Xcode and click Create a new Xcode project on the initial dialog that comes up, or click File > New > Project. Under the iOS heading, click Game, then update the Project Name to say RainCat. Make sure that the language is Swift, and the Game Technology is SpriteKit.
For ease of programming, we will make this an iPad only game. We may circle back and update the code for iPhone support in the future.
Click Next, find your working directory, then click Save.
Congrats! Technically you have a game! At this point you may as well hit Run and check out what happens. You will see a SKLabelNode that says “Hello World!” as well as some fun interactions when you click or drag your finger around the screen.
Let’s look around the code a little bit. You will see the project file called RainCat with a blue symbol next to it. Click that, then make sure you are on the General tab, then click Targets > RainCat on the left-hand bar. Scroll to Deployment Info and then edit the Device Orientation. Edit it to only have Landscape Left and Landscape Right selected.
Over on the left pane where all of your files are located, you will see the common AppDelegate file. For this game we will not need to touch the AppDelegate, but it should not be completely forgotten. You can use the functions in this file for more advanced options while launching and when the app goes into the background. Feel free to peruse and read the comments that were generated here for more information on each function.
Now move over to the GameScene.sks. This is a visual editor for setting up your scenes. We don’t need to focus on this, but if you are curious there are plenty of other tutorials on how to use it. A great example can be found here. We aren’t using it to set up our scenes; we will be doing everything programatically.
To set up your scene go to GameViewController.swift. All the UIKit methods can be stored here, presenting new controllers which can be handled here and in the Main.storyboard. For this game, we won’t play with this too much, other than a few options with the SKView regarding FPS and node count, visualizing physics, and presenting our GameScene.
Now we will jump into the GameScene.swift. This is where you will be doing most of the work for RainCat.
The code in its current state will just have a label that says “Hello, World!” as well as some colored squares that change if you tap and drag your finger around the screen. The new starting project is a more complete example of how to use SpriteKit along with the sks and Actions.sks file, but it gives us more code that we need.
Let’s clean up the new project!
GameScene.sks and Actions.sks can be deleted or ignored. I recommend learning how to use them, but currently they are unnecessary for making a simple SpriteKit game.
In GameViewController.swift, we will replace the viewDidLoad() function. Currently the code is loading the scene from the GameScene.sks file, but we deleted it earlier. We will bypass the GameScene.sks file and manually create and present our scene:
We will heavily update the GameScene.swift since much of the pre-generated code is unnecessary for what we are trying to accomplish. Since we do not need random squares generated when we touch the screen, nor the “Hello World!” SKLabelNode, we can update the code to:
The code is now in a good starting state for us to add our own functionality to the game. If we run our app now, we will be greeted with a blank screen. This is exactly what we need to begin development on RainCat.
Time to start coding!
Now that we have a blank canvas, we can use some of the methods we left behind to bring our app to life.
First we will add a floor, since we need a surface for the cat to walk and feed on. The floor will be updated in the next few articles, but for now we can keep things simple.
In sceneDidLoad(), we can add a SKShapeNode to act as our floor:
This will add a thin rectangle near the bottom of your screen. We could also accomplish this with a SKSpriteNode and just set the color without adding a texture. Either way will work and since it is a temporary red rectangle, either way is technically correct.
Next let’s add our alpha version of our raindrop. For now it will be a blue square:
If you run the app at this point, you won’t see much happening:
Time to add physics!
Next we will add a physics body to the raindrop. Insert the following code before we add the raindrop to the scene with addChild(raindrop):
If we run the app now, the raindrop will do what we expect it to: fall. Since we did not add a physics body to the floor, it will also fall through the floor.
Since we want the rain to be affected by the floor, we can add an edge barrier that is made out of a line. This isn’t really fancy, but it works as intended. Add this physics body to the floorNode before we add it to the scene:
At this point, when we run the app, we will have the cube fall and stop at the floorNode as expected.
Make it rain!
We want to make a spawner to create falling raindrops at random locations from the top of the screen the physics body we created earlier. There are a few ways to make a spawner. One way is to create an SKAction that will execute the spawnRaindrop method, then execute a wait action. I decided against this, because I want the ability to update the rate of spawning of the raindrops. If we used an SKAction, we would constantly need to create and destroy SKActions. We can tie this to our update(_ currentTime:) function without any of the extra overhead of SKActions.
At the top of the GameScene.swift file, add these lines underneath the lastUpdateTime variable, above the sceneDidLoad() function:
The variable rainDropSpawnRate will be the max time allowed between each raindrop falling. It will not be exact, but it will give us a good timeframe for when a new raindrop will be created. The next variable, currentRainDropSpawnTime will act as a running timer that we will update every time update is called. Once the currentRainDropSpawnTime is greater or equal to rainDropSpawnRate, we will call the spawnRaindrop function. Finally, we will add a reference called random, our random number generator. Note that the rainDropSpawnRate and currentRainDropSpawnTime are vars instead of lets, so we can update them later. To get this to work, we can remove the raindrop creation from sceneDidLoad and into its own method.
After we create the raindrop, we randomize the x position on screen by calling the random number generator, and making sure it is on screen with our truncatingRemainder method. We want to take the absolute value of this random number because ideally we will keep all of the raindrops on the screen instead of wasting power spawning them off screen.
In the update function, we will add the following lines of code:
This will help us generate a raindrop when the time is right. When we spawn the raindrop, we need to reset the timer so we aren’t spawning them earlier than we intend to.
At this point, our simulator should look a little bit like this:
We can play with the spawn rate and we can spawn raindrops faster or slower depending on what value we enter. Update rainDropSpawnRate to be zero, and you will see many pretty raindrops. If you do this you will notice that we have a big problem now. We are currently spawning unlimited objects and never getting rid of them. We will eventually be crawling at four frames per second and soon after that, out of memory. In the next lesson, we will address this, we’ll add the umbrella object, and we’ll add a more complex SKPhysicsBody to it. We will also add in collision detection so that we can get a callback when two nodes come into contact.
You may download the final code for today on GitHub.
How did you do? Did your code look almost exactly like mine? What changed? Did you update the code for the better, or was I not being clear explaining what to do? Let me know in the comments below.
Thank you for making it this far. Stay tuned for lesson 2 of RainCat!