Game Development with Swift
上QQ阅读APP看书,第一时间看更新

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)
}