Learn how to create an interactive children’s book for the iPad!
Update note: Jorge Jordán has updated this children’s book tutorial to iOS 8 and Swift! The original post was by tutorial team member Tammy Coron.
With the iPad, it’s never been a better time to be a kid!
The iPad allows developers to create beautiful interactive children’s books that simply cannot be replicated in any other medium. For some examples, check out The Monster at the End of This Book, Bobo Explores Light, and Wild Fables.
In the old days, developers had to use third party frameworks like Cocos2d to get the job done. But now, Apple has provided their own 2D framework called Sprite Kit, which is perfect for creating these types of books.
In this tutorial, you’ll create an interactive children’s book called The Seasons using the Sprite Kit framework, where you’ll learn how to add objects to scenes, create animation sequences, allow the reader to interact with the book and even how to add sound and music to your book!
Note: The Seasons was written and illustrated by Tutorial Team member
Tammy L Coron. This tutorial uses the first few pages from that book. Please do not reproduce any of its contents in your own projects. The narration is provided by
Amy Tominac, and again cannot be used in your own projects.
This tutorial also uses music from Kevin MacLeod and sound effects from FreeSound. These are both great resources for your project, but you must provide attribution. See attribution.txt in the project bundle for more details.
This tutorial assumes you are familiar with at least the basics of Sprite Kit. If you are new to Sprite Kit, please check out our Sprite Kit Tutorial for Beginners first.
Getting Started
First download the starter project for this tutorial. Extract the project to a convenient location on your drive, then open it up in Xcode.
Like any good book, it’s best to start at the beginning – the title scene. The starter project provides stub versions of the methods used in this scene — it’s your job to add the code. Your first steps will be to initialize the scene and add a background image.
Open Scene00.swift and add the following block of code to didMoveToView(_:)
just after the comment that reads /* add setup here */
:
var background = SKSpriteNode(imageNamed: "bg_title_page")
background.anchorPoint = CGPoint(x: 0, y: 0)
background.position = .zeroPoint
addChild(background) |
The code above creates an SKSpriteNode
with the title page’s background. Both the anchor point and position are set to (0,0), which means the lower-left corner of the background will be at the lower-left corner of the screen. Finally, it calls addChild(_:)
to add the newly created node to the scene.
Still working in the same file, add the following two lines of code to init(size:)
immediately after the call to addChild(node:)
:
setUpBookTitle()
setUpFooter() |
The two methods called here are only shells at the moment; you’ll flesh them out in the next section.
Build and run your project; you should see the title page as illustrated below:
Adding the Book Title
As you saw in the code above, it’s a straightforward operation to add an SKSpriteNode
to your scene. With just a few lines of code, you can add a textured sprite to any scene.
Now that the background image has been added, you’ll need to add the book title. You could use an SKLabelNode
to add your title, or even a UIView
, but instead I’ve opted to use a graphic.
Still working in the Scene00.swift file, add the following block of code to setUpBookTitle()
:
var bookTitle = SKSpriteNode(imageNamed: "title_text")
bookTitle.name = "bookTitle"
bookTitle.position = CGPoint(x: 421, y: 595)
addChild(bookTitle) |
You first create an SKSpriteNode
with the title text graphic. You also give the node a name “bookTitle” so you can access it later. You’re setting the position to an exact co-ordinate so it lines up precisely where it should against the background image.
Build and run your project; you’ll see your background image with the graphic title superimposed over it, as shown below:
That looks pretty neat. But wouldn’t it be great if you could add a little bit of action to your title screen?
Adding Animation to Objects
Sprite Kit makes it easy to add animation to the objects in your scene. Instead of simply having the title appear on the screen, you can make it slide on to the screen and bounce a little bit before it comes to rest.
To do this, you’ll use SKAction
objects with predefined sequences.
Head back to Scene00.swift and add the following code to the end of setUpBookTitle()
:
var actionMoveDown = SKAction.moveToY(600, duration: 3.0)
var actionMoveUp = SKAction.moveToY(603, duration: 0.25)
var actionMoveDownFast = SKAction.moveToY(600, duration: 0.25)
bookTitle.runAction(SKAction.sequence([actionMoveDown, actionMoveUp, actionMoveDownFast]]] |
The above code creates three actions using the SKAction
class method moveToY(_:, duration:)
; this specifies an action that moves a node vertically from its original position to the specified y
position. Next, it creates a new sequence action using the SKAction
class method sequence(_:)
; this instructs bookTitle
to run the specified array of actions in order.
The net result of these actions is that the sprite will move the sprite down, then up, and then down again before it comes to rest.
Next, modify the line in setUpBookTitle()
that sets bookTitle
‘s position
property as shown below:
bookTitle.position = CGPoint(x: 425, y: 900) |
This simply modifies the start position of the title image so that it starts off-screen.
Build and run your project; the title now slides in slowly and bounces a little before it comes to rest at the position specified in actionMoveDownFast
.
Actions are great, but sounds are an integral part of any interactive children’s book. Sprite Kit makes this tremendously easy as well!
Adding Sound to Your Story
There are several methods to add music and other sounds to your book, but in this tutorial you’ll just focus on two methods: one for adding the background music, and the other for adding the narration and sound effects.
Open SeasonsSceneBase.swift and add the following import statement:
AVFoundation is the iOS framework that will provide audio-visual capabilities to the app, and will be used here to play the story’s music and sound effects.
Add the following properties to the top of the SeasonsSceneBase
class declaration:
private var backgroundMusicPlayer: AVAudioPlayer?
private var btnSound = SKSpriteNode(imageNamed: "button_sound_on")
private var soundOff: Bool |
Next, add the following line of code before super.init()
line in init(_:)
:
soundOff = NSUserDefaults.standardUserDefaults().boolForKey("pref_sound") |
This code retrieves the Bool
value that represents the user’s preference for having the sound on or off and assigns it to the soundOff
variable.
Next, add the following code in Scene00.swift at the top of didMoveToView(_:)
:
playBackgroundMusic("title_bgMusic.mp3") |
This will call into playBackgroundMusic(_:)
to play the title song on this scene.
Now add the following block of code to playBackgroundMusic(_:)
:
var error: NSError?
let backgroundMusicURL = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
backgroundMusicPlayer = AVAudioPlayer(contentsOfURL: backgroundMusicURL, error:&error)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.volume = 1.0
backgroundMusicPlayer.prepareToPlay() |
playBackgroundMusic(_:)
plays the background music using an AVAudioPlayer
object.
Note: AVAudioPlayer
isn’t specific to Sprite Kit, so it won’t be covered in detail here. For more detail on
AVAudioPlayer
, check out our
Audio Tutorial for iOS.
Okay — you have some code to handle the user preference for enabling or disabling sound in your book. You should probably add a control to let the user set this preference! :]
Still working in SeasonsSceneBase.swift, add the code below to setupFooter()
:
btnSound.zPosition = 1
if soundOff {
let action = SKAction.setTexture(SKTexture(imageNamed: "button_sound_off"))
btnSound.runAction(action)
backgroundMusicPlayer?.stop()
} else {
let action = SKAction.setTexture(SKTexture(imageNamed: "button_sound_on"))
btnSound.runAction(action)
backgroundMusicPlayer?.play()
}
if homeFooter {
// Positions the sound button all the way to the right.
btnSound.position = CGPoint(x: 980, y: 38)
} else {
btnSound.position = CGPoint(x: self.size.width/2 + 330, y: 38)
}
addChild(btnSound) |
The above code sets the texture of the btnSound
SKSpriteNode
to the appropriate image depending on the user’s stored preference and it also sets the button’s zPosition
to be sure that it’s placed above the background’s zPosition
. It then checks soundOff
and updates the sprite’s texture with an SKAction to the appropriate on or off image. Then it sets the sprite’s position, adds it to the parent view, and finally plays or stops the music depending on the user’s preference.
There are a number of ways to handle changing a sprite’s image. The above method is only one way to accomplish this. The next section shows you another way to accomplish the same thing.
For now, build and run the app, and the background music should play. Tapping the button won’t do anything yet though.
Detecting Touch Events
The reason you can’t currently toggle the control is that the sprite doesn’t have any touch event handlers. Add the following code to touchesBegan(_:withEvent:)
in SeasonsSceneBase.swift:
for touch in touches as! Set<UITouch> {
var location: CGPoint = touch.locationInNode(self)
if btnSound.containsPoint(location) {
showSoundButtonForTogglePosition(soundOff)
}
} |
touchesBegan(_:withEvent:)
is part of the UIResponder
class which all SKNode
objects extend. It tells the receiver when one or more fingers begin touching the view. In this method you’re able to capture the location of the touch and respond to it accordingly.
Note: As every SKNode
extends UIResponder
, you can implement touch handling directly in your sprites. This tutorial performs all touch event handling in the scene nodes simply to keep the number of classes more manageable. However, in a production app it would likely make more sense to create an SKSpriteNode
subclass, such as SoundToggleButton
, and have that subclass handle its own touch events.
Look at the if
statement above; you’ll see that if the touch location is on the sound button, you call showSoundButtonForTogglePosition(_:)
.
Now would be a great time to implement that method! :] Add the following code to showSoundButtonForTogglePosition(_:)
in SeasonsSceneBase.swift:
if togglePosition {
let action = SKAction.setTexture(SKTexture(imageNamed: "button_sound_on"))
btnSound.runAction(action)
soundOff = false
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "pref_sound")
NSUserDefaults.standardUserDefaults().synchronize()
backgroundMusicPlayer?.play()
} else {
let action = SKAction.setTexture(SKTexture(imageNamed: "button_sound_off"))
btnSound.runAction(action)
soundOff = true
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "pref_sound")
NSUserDefaults.standardUserDefaults().synchronize()
backgroundMusicPlayer?.stop()
} |
The method above sets the appropriate texture for the SKSpriteNode
that is held in btnSound
— much as you did in setupFooter()
. Once the texture has been set, you can store the user’s selection and either play or stop the music accordingly.
Note: An
SKTexture
object stores a reference to the graphical data in memory that a sprite renders.
Build and run, and now you should be able to tap the sound button to toggle the background music on and off.
Adding the Next Scene
Going back to Scene00.swift, add the following code to setUpBookTitle()
just after the line that creates actionMoveDownFast
, but before the line that calls runAction:
on bookTitle
:
var wait = SKAction.waitForDuration(0.75)
var showButton = SKAction.runBlock {
var buttonStart = SKSpriteNode(imageNamed: "button_read")
buttonStart.name = "buttonStart"
buttonStart.position = CGPoint(x: 425,y: 460)
buttonStart.zPosition = 1
self.addChild(buttonStart)
buttonStart.runAction(SKAction.playSoundFileNamed("thompsonman_pop.wav", waitForCompletion: false))
} |
The code above creates a new action block which creates a sprite for the start button and plays a sound. By putting the code to create the sprite and play a sound inside an action, you can easily make this code run in a particular point in a sequence of actions.
As you may have figured out by now, there is usually more than one way to skin a cat — or code your children’s book!
Replace the line in setUpBookTitle()
that calls runAction(_:)
on bookTitle
with the following code:
bookTitle.runAction(SKAction.sequence([actionMoveDown, actionMoveUp, actionMoveDownFast, wait, showButton])) |
This simply adds the new actions to the sequence that bookTitle
runs when called.
The next step is to modify the touch handling. Add the following line to the top of touchesBegan(_:withEvent:)
// Make sure the start button has been added.
if let startButton = childNodeWithName("buttonStart") {
/* Called when a touch begins */
for touch in touches {
var location = touch.locationInNode(self)
if startButton.containsPoint(location) {
goToScene(Scene01(size: size))
}
}
} |
This assigns the button – added earlier in the showButton
action – to the startButton
variable, and goes to Scene01 if the touch is located within the button..
Next, add the following code to goToScene(_:)
in SeasonsSceneBase.swift
backgroundMusicPlayer?.stop()
var sceneTransition = SKTransition.fadeWithColor(UIColor.darkGrayColor(), duration: 1)
self.view?.presentScene(scene, transition: sceneTransition) |
The above code adds the new scene and presents it with a new transition.
Build and run your project. After the initial animation, you should see the new “Read Story” button, as shown below:
Tap the “Read Story” button and the next scene presents itself on-screen. You won’t see a whole lot at this point — your next task is add some content to the first page.
Adding Content to the First Page
In this section, you’ll add the first page to the book along with some physics simulation and some touch response logic.
For this purpose you will use all of the goodies in SKTUtils
, which is an external resource developed for iOS Games by Tutorials and won’t be covered in detail in this tutorial.
Add the following properties to the class in SeasonsSceneBase.swift:
var footer = SKSpriteNode(imageNamed: "footer")
var btnLeft = SKSpriteNode(imageNamed: "button_left")
var btnRight = SKSpriteNode(imageNamed: "button_right") |
Note: From this point forward, all code dealing with background sound has been included in the starter files for you. You can review the previous section to see how sound is implemented in this project.
Just as before, you’ll need to initialize the scene before you can do anything with it. There are several ways to accomplish this, but this tutorial takes the approach of creating separate methods for each scene instead of handling everything within a single method.
Add the following block of code to the bottom of didMoveToView(_:)
in Scene01.swift:
setUpText()
setUpFooter()
setUpMainScene() |
Those are your basic setup methods — your job is to fill them out. The good news is that the implementation of setUpText
setUpFooter
is identical for each scene in your project, so you’ll only need to write it once for all scenes.
Add the following code to setUpText
in Scene01.swift:
var text = SKSpriteNode(imageNamed: "pg01_text")
text.position = CGPoint(x: 300 , y: 530)
addChild(text)
readText() |
Here you create an SKSpriteNode
, set its location, and add it to the scene. The sprite above holds an image for the text of the page; you’ll have one sprite for the text of each page in your book.
This method also calls readText()
which plays the narration for the book.
Add the following code to readText()
in Scene01.swift:
if actionForKey("readText") == nil {
var readPause = SKAction.waitForDuration(0.25)
var readText = SKAction.playSoundFileNamed("pg01.wav", waitForCompletion: true)
var readSequence = SKAction.sequence([readPause, readText])
runAction(readSequence, withKey: "readText")
} else {
removeActionForKey("readText")
} |
Here you create a new SKAction
, much like you did earlier for the bouncing title text. The difference this time — and it’s an important difference — is that you also assign a key name of readText to your action. You’ll discover why a bit later.
You’re also using SKAction’s playSoundFileNamed(_:waitForCompletion:)
class method, which is a really simple method for playing sounds. While this method works great for quick sound effects, it’s probably not the best choice for read-along text because you can’t interrupt the sound when it’s playing, among other reasons. You’re using it in this tutorial for ease-of-use only and to become familiar with the framework.
Build and run, and now you will see and hear the narrated text for page 1!
Adding the Navigation Controls
Next, you’ll add the navigation controls for your app, which will all be located along the bottom of the screen.
Add the following code above your btnSound setup code to setUpFooter()
in SeasonsSceneBase.swift:
if !homeFooter {
/* add the footer */
footer.position = CGPoint(x: self.size.width/2, y: 38)
btnRight.zPosition = 1
addChild(footer)
}
physicsBody = SKPhysicsBody(edgeLoopFromRect: footer.frame)
/* add the right button if there is a next scene */
if getNextScene() != nil {
btnRight.position = CGPoint(x: self.size.width/2 + 470, y: 38)
btnRight.zPosition = 1
addChild(btnRight)
}
/* add the left button if there is a previous scene */
if getPreviousScene() != nil {
btnLeft.position = CGPoint(x: self.size.width/2 + 400, y: 38)
btnLeft.zPosition = 1
addChild(btnLeft)
} |
The code above initializes footer
with the footer area’s background image, sets its position, and adds it to the scene. It also adds sprites for both the page back and page forward buttons depending on if there is a “previous” or “next” scene.
Build and run, and you will now see the footer along the bottom of the scene (although tapping it won’t do anything yet):
Now that the basic scene setup is complete, it’s time to add the main character.
Adding The Main Character
Still working in the Scene01.swift file, add the following property to Scene01
:
private var kid = SKSpriteNode(imageNamed: "pg01_kid") |
This variable holds a reference to your main character’s sprite.
Next, add the following two lines to setUpMainScene
of Scene01.swift:
setUpMainCharacter()
setUpHat() |
These methods simply call other methods to keep your code nice and tidy. You’ll populate those next.
Add the following code to setUpMainCharacter
in Scene01.swift:
kid = SKSpriteNode(imageNamed: "pg01_kid")
kid.anchorPoint = .zeroPoint
kid.position = CGPoint(x: self.size.width/2 - 245, y: 45)
kid.zPosition = 1
addChild(kid)
setUpMainCharacterAnimation() |
This should be familiar territory to you by now; you create an SKSpriteNode
, set its anchorPoint
, position
and zPosition
, and add it to the scene. Then you call setUpMainCharacterAnimation
to set up the main character animation.
setUpMainCharacterAnimation
only exists as a shell — time to add some madness to your method! :]
Add the following code to setUpMainCharacterAnimation
in in Scene01.swift:
var textures = [SKTexture]()
for i in 0..<3 {
var textureName = "pg01_kid0\(i)"
var texture = SKTexture(imageNamed: textureName)
textures.append(texture)
}
var duration = CGFloat.random() * (6 - 3) + 3
var blink = SKAction.animateWithTextures(textures, timePerFrame: 0.25)
var wait = SKAction.waitForDuration(4.5, withRange: 1.5)
var mainCharacterAnimation = SKAction.sequence([blink, wait, blink, blink, wait, blink, blink])
kid.runAction(SKAction.repeatActionForever(mainCharacterAnimation)) |
Animations are performed using a series of images. In the code above, the main character blink animation uses two images to achieve this effect.
The first line creates an array to hold your images. Following that, you generate the image name, create an SKTexture
object for each image in the animation, then add that object to your array.
Next you create the animation sequence using the SKAction
method animateWithTextures(_:timePerFrame:)
class method which expects an array of textures.
Finally, you instruct the kid
sprite to perform its action. By using repeatActionForever(_:)
, you tell the node to run this action continuously.
Build and run your project; hit the “Read Story” button and you’ll see the main character appear on-screen and blink his eyes while the narration plays in the background, as so:
An Introduction to Physics
You can really improve the appeal of your book by adding some interactivity. In this section, you’re going to create a hat for the main character which the reader can drag around the screen and place on the main character’s head.
Still working in Scene01.swift, add the following variable to the Scene01
:
private var hat = SKSpriteNode(imageNamed: "pg01_kid_hat") |
Add the following code to setUpHat()
in Scene01.swift:
var label = SKLabelNode(fontNamed: "Thonburi-Bold")
label.text = "Help Mikey put on his hat!"
label.fontSize = 20.0
label.fontColor = UIColor.yellowColor()
label.position = CGPoint(x: 160, y: 180)
addChild(label)
hat = SKSpriteNode(imageNamed: "pg01_kid_hat")
hat.position = CGPoint(x: 150, y: 290)
hat.physicsBody = SKPhysicsBody(rectangleOfSize: hat.size)
hat.physicsBody?.restitution = 0.5
hat.zPosition = 2
addChild(hat) |
The first half of the code above creates an SKLabelNode
object and adds it to the scene. An SKLabelNode
is like the Sprite Kit version of UILabel
, which draws a string.
The second half of the code adds the physics to the scene. You created the SKSpriteNode
and assigned it to the hat
variable as well as an SKPhysicsBody
which lets you apply a number of physical characteristics to your objects such as shape, size, mass, and gravity and friction effects.
Giving the hat a physics body sets the shape of the body to match the node’s frame. You also set the zPosition
and the restitution
to 0.5
which means your physics body will bounce off objects with half of its initial force.
Build and run your project; tap the “Read Story” button and…huh? Where did the hat go?
If you were watching the screen closely, you may have noticed the hat falling off the screen. That’s because there was no opposing body to stop it from falling.
You can fix that by adding a physics body to act as the ground.
Add the following code to setUpFooter()
in SeasonsSceneBase.swift, just after the line that calls addChild(_:)
to add footer
to the scene:
physicsBody = SKPhysicsBody(edgeLoopFromRect: footer.frame) |
Build and run your project again; this time, the hat has something to land on — the physics body you set up in the footer. As well, you can see the yellow text label that you created with SKLabelNode
, as shown below:
Okay, you’ve added some physics properties to the hat — but how do you go about adding interactivity?
Handling Touches and Moving the Hat
This section implements the touch handling for the hat so that you can move it around the screen, as well as touch handling for the Next, Previous and sound preference button.
Add the following block of code immediately after the existing if
statement of touchesBegan(_:withEvent:)
in SeasonsSceneBase.swift:
else if actionForKey("readText") != nil { // do not turn page if reading
return
} else if btnRight.containsPoint(location) {
// Right button goes to the next scene.
if let nextScene = getNextScene() {
goToScene(nextScene)
}
} else if btnLeft.containsPoint(location) {
// Left button goes to the previous scene
if let previousScene = getPreviousScene() {
goToScene(previousScene)
}
} |
Here you check to see if the Next or Previous page buttons were the target of the touch event, much like you did for the sound toggle button. You then handle the touch event by moving to the appropriate scene.
Additionally, there is a check for actionForKey(_:)
. Recall the key that you set for the action that narrates the text?
runAction(readSequence, withKey:"readText") |
The block of code you added above uses that key to check if the readText
action is currently playing. If it is, do nothing. If it’s not, turn the page.
But why check at all?
The reason is that when you start an SKAction
that plays a sound, it’s impossible to interrupt that sound. This is why you can’t turn the page while the text is being read. As was mentioned earlier, you’ll probably want to use something more robust to narrate the text in your production-level app.
It would be nice to give the reader a way to jump back to the first scene, no matter where they are in the book.
Add the following code right below the code that you added previously, which will take you back to the first scene when the user touches the book title in the footer:
else if location.x >= 29 && location.x <= 285 && location.y >= 6 && location.y <= 68 {
// Go back to the home scene.
goToScene(Scene00(size: size))
} |
The above code tests the touch location a little differently than the other checks you’ve made. Because the book’s title is part of the footer image, this simply checks to see whether or not the touch location falls within the area where you know the book title appears. It’s usually not a good idea to have “magic numbers” like this strewn about your app, but it serves to keep things simple in this tutorial.
The rest of the above code is exactly like the code that handles the Previous Page button touch events.
Finally, you’ll need to handle touch events on the hat. To do so, you’ll need to store some data between events.
Add the following two variables to the variables of Scene01.swift:
private var touchingHat = false
private var touchPoint: CGPoint? |
In the above code, touchingHat
stores whether the user is currently touching the hat, while touchPoint
stores the most recent touch location.
To ensure the hat catches any touch events that occur both over it and another target area, check the hat first.
To do this, add the following code to touchesBegan(_:withEvent:)
in Scene01.swift:
for touch in touches as! Set<UITouch> {
var location = touch.locationInNode(self)
if hat.containsPoint(location) {
touchingHat = true
touchPoint = location
/* change the physics or the hat is too 'heavy' */
hat.physicsBody?.velocity = CGVectorMake(0, 0)
hat.physicsBody?.angularVelocity = 0
hat.physicsBody?.affectedByGravity = false
}
} |
When the user first touches the hat, the code above sets the touchingHat
flag to true
and stores the touch location in touchPoint
. It also makes a few changes to the hat’s physics body. These changes are necessary because without them it’s virtually impossible to drag the hat around the screen as you’re constantly fighting with the physics engine.
You’ll need to track the touch as it moves across the screen, so add the following code to touchesMoved(_:withEvent:)
in Scene01.swift:
if let touch = touches.first as? UITouch {
touchPoint = touch.locationInNode(self)
} |
Here you update the most recent touch location stored in touchPoint
.
When the user stops touching the screen, you need to reset any hat-related data.
Add the following code to both touchesEnded(_:withEvent:)
and touchesCancelled(_:withEvent:)
:
touchingHat = false
hat.physicsBody?.affectedByGravity = true |
The above code sets the touchingHat
flag to true
and re-enables gravity for the hat so that it will fall back to the floor when the user releases it.
There’s just one more thing to do to get the hat to track the user’s finger as it moves on the screen.
Add the following code to update(_:)
:
if touchingHat {
touchPoint.x.clamp(hat.size.width / 2, self.size.width - hat.size.width / 2)
touchPoint.y.clamp(footer.size.height + hat.size.height / 2, self.size.height - hat.size.height / 2)
hat.position = touchPoint
} |
update
invokes before each frame of the animation renders. Here you check to see if the user is dragging the hat; if it is, change the hat
current location to the position stored in touchPoint
. You’re using the clamp
function from SKTUtils
to ensure the hat doesn’t move off the screen or below the footer.
Build and run your project; hit the “Read Story” button, and play around with the hat on the screen a little.
Note: Try building the app both with and without the changes to the hat’s physics bodies to see the difference it makes. You should be able to move the hat around with your finger easily when the changes to the physics body are applied, but it’s a little more difficult to move it around when you comment out the changes.
Moving the hat around is cool, but there isn’t any feedback as to whether or not the hat is on top of the main character’s head. It’s time to add that feedback.
Modify touchesEnded(_:withEvent:)
so that it looks like the code below:
if let touch = touches.first as? UITouch where touchingHat {
var currentPoint: CGPoint! = touch.locationInNode(self)
if currentPoint.x >= 300 && currentPoint.x <= 550 && currentPoint.y >= 250 && currentPoint.y <= 400 {
currentPoint.x = 420
currentPoint.y = 330
hat.position = currentPoint
let popSound = SKAction.playSoundFileNamed("thompsonman_pop.wav", waitForCompletion: false)
hat.runAction(popSound)
} else {
hat.physicsBody?.affectedByGravity = true
}
touchingHat = false
} |
With the above bit of code you can determine if the user is touching the hat and where they attempted to release it. This is why you want to use the end event and not the begin event.
If the user releases the hat close enough to the kid’s head, your code re-positions the hat to an exact location as defined by currentPoint.x
and currentPoint.y
. You also play a sound to alert the user that the hat is now firmly placed on the main character’s head – which is important! Did you see all that snow outside the window? Brrrrr!
Build and run your project; grab the hat and plunk it down on your character’s head, like so:
Aside from the story and the narration, these interactive elements with actions and sounds are key to the experience and really take advantage of what iOS and Sprite Kit have to offer.
Where To Go From Here?
I hope you enjoyed working through this tutorial and that it was a little different from the usual use of Sprite Kit in games. You can download the complete sample project and compare notes if you’d like.
At this point, the rest of the story is up to you! If you check out the completed project download, you’ll find additional scenes for pages 2–4 are there as a bonus.
The code there is similar to what you’ve already built, and you should be able to look around and figure out what’s going on in those scenes.
If you’re looking to learn more about Sprite Kit, be sure to check out our book, iOS Games by Tutorials.
If you have any questions or comments, feel free to join in the discussion below!
The post Sprite Kit Tutorial: Create an Interactive Children’s Book with Sprite Kit and Swift appeared first on Ray Wenderlich.