Drawing your first sprite
It is time to write some game code – fantastic! Open your GameScene.swift
file and find the didMoveToView
function. Recall that this function fires every time the game switches to this scene. We will use this function to get familiar with the SKSpriteNode
class. You will use SKSpriteNode
extensively in your game, whenever you want to add a new 2D graphic entity.
Note
The term sprite refers to a 2D graphic or animation that moves around the screen independently from the background. Over time, the term has developed to refer to any game object on the screen in a 2D game. We will create and draw your first sprite in this chapter: a happy little bee.
Building a SKSpriteNode class
Let's begin by drawing a blue square to the screen. The SKSpriteNode
class can draw both texture graphics and solid blocks of color. It is often helpful to prototype your new game ideas with blocks of color before you spend time with artwork. To draw the blue square, add an instance of SKSpriteNode
to the game:
override func didMoveToView(view: SKView) { // Instantiate a constant, mySprite, instance of SKSpriteNode // The SKSpriteNode constructor can set color and size // Note: UIColor is a UIKit class with built-in color presets // Note: CGSize is a type we use to set node sizes let mySprite = SKSpriteNode(color: UIColor.blueColor(), size: CGSize(width: 50, height: 50)) // Assign our sprite a position in points, relative to its // parent node (in this case, the scene) mySprite.position = CGPoint(x: 300, y: 300) // Finally, we need to add our sprite node into the node tree. // Call the SKScene's addChild function to add the node // Note: In Swift, 'self' is an automatic property // on any type instance, exactly equal to the instance itself // So in this instance, it refers to the GameScene instance self.addChild(mySprite) }
Go ahead and run the project. You should see a similar small blue square appear in your simulator:
Tip
Swift allows you to define variables as constants, which can be assigned a value only once. For best performance, use let
to declare constants whenever possible. Declare your variables with var
when you need to alter the value later in your code.
Adding animation to your Toolkit
Before we pe back in to sprite theory, we should have some fun with our blue square. SpriteKit uses action objects to move sprites around the screen. Consider this example: if our goal is to move the square across the screen, we must first create a new action object to describe the animation. Then, we instruct our sprite node to execute the action. I will illustrate this concept with many examples in the chapter. For now, add this code in the didMoveToView
function, below the self.addChild(mySprite)
line:
// Create a new constant for our action instance // Use the moveTo action to provide a goal position for a node // SpriteKit will tween to the new position over the course of the // duration, in this case 5 seconds let demoAction = SKAction.moveTo(CGPoint(x: 100, y: 100), duration: 5) // Tell our square node to execute the action! mySprite.runAction(demoAction)
Run the project. You will see our blue square slide across the screen towards the (100,100) position. This action is re-usable; any node in your scene can execute this action to move to the (100,100) position. As you can see, SpriteKit does a lot of the heavy lifting for us when we need to animate node properties.
Tip
Inbetweening, or tweening, uses the engine to animate smoothly between a start frame and an end frame. Our moveTo
animation is a tween; we provide the start frame (the sprite's original position) and the end frame (the new destination position). SpriteKit generates the smooth transition between our values.
Let's try some other actions. The SKAction.moveTo
function is only one of many options. Try replacing the demoAction
line with this code:
let demoAction = SKAction.scaleTo(4, duration: 5)
Run the project. You will see our blue square grow to four times its original size.
Sequencing multiple animations
We can execute actions together simultaneously or one after the each other with action groups and sequences. For instance, we can easily scale our sprite larger and spin it at the same time. Delete all of our action code so far and replace it with this code:
// Scale up to 4x initial scale let demoAction1 = SKAction.scaleTo(4, duration: 5) // Rotate 5 radians let demoAction2 = SKAction.rotateByAngle(5, duration: 5) // Group the actions let actionGroup = SKAction.group([demoAction1, demoAction2]) // Execute the group! mySprite.runAction(actionGroup)
When you run the project, you will see a spinning, growing square. Terrific! If you want to run these actions in sequence (rather than at the same time) change SKAction.group
to SKAction.sequence
:
// Group the actions into a sequence
let actionSequence = SKAction.sequence([demoAction1, demoAction2])
// Execute the sequence!
mySprite.runAction(actionSequence)
Run the code and watch as your square first grows and then spins. Good. You are not limited to two actions; we can group or sequence as many actions together as we need.
We have only used a few actions so far; feel free to explore the SKAction
class and try out different action combinations before moving on.
Recapping your first sprite
Congratulations, you have learned to draw a non-textured sprite and animate it with SpriteKit actions. Next, we will explore some important positioning concepts, and then add game art to our sprites. Before you move on, make sure your didMoveToView
function matches with mine, and your sequenced animation is firing properly. Here is my code up to this point:
override func didMoveToView(view: SKView) { // Instantiate a constant, mySprite, instance of SKSpriteNode let mySprite = SKSpriteNode(color: UIColor.blueColor(), size: CGSize(width: 50, height: 50)) // Assign our sprite a position mySprite.position = CGPoint(x: 300, y: 300) // Add our sprite node into the node tree self.addChild(mySprite) // Scale up to 4x initial scale let demoAction1 = SKAction.scaleTo(CGFloat(4), duration: 2) // Rotate 5 radians let demoAction2 = SKAction.rotateByAngle(5, duration: 2) // Group the actions into a sequence let actionSequence = SKAction.sequence([demoAction1, demoAction2]) // Execute the sequence! mySprite.runAction(actionSequence) }