SpriteKit Basics: Sprites

In this series, we’re learning how to use SpriteKit to build 2D games for iOS. In this post, we’ll continue our exploration of SpriteKit nodes, and learn about a special kind of node called a “sprite”—an SKSpriteNode.

SpriteKit Basics: Sprites

To follow along with this tutorial, just download the accompanying GitHub repo. It has a folder called ExampleProject Starter. Open the project in that folder in Xcode, and you’re ready to go!

Sprite Nodes

An SKSpriteNode is drawn either as a rectangle with a texture mapped onto it, or as a colored untextured rectangle. Creating a SKSpriteNode with a texture mapped onto it is the most common, as this is how you bring your game’s artwork to life.

Add the following to the didMove(to:) method within GameScene.swift.

override func didMove(to view: SKView) {
    let startGameLabel = SKLabelNode(text: "Start Game")
    startGameLabel.name = "startgame"
    startGameLabel.position = CGPoint(x: size.width/2, y: size.height/2)
    addChild(startGameLabel)
        
    let redSprite = SKSpriteNode(color: .red, size: CGSize(width: 200, height: 200))
    addChild(redSprite)
}

Here we are using the convenience intiailizer init(color:size:) which will draw a rectangular sprite with the color and size you pass in as parameters. If you test the project now, you will see half of the red square showing.

SpriteKit Basics: Sprites

You might be wondering why only half of the sprite is showing since we already determined that SKNode‘s origins are at (0,0). This because the SKSpriteNode‘s frame and therefore its texture is centered on its position. To change this behaviour, you can change the sprite’s anchorPoint property, which determines the point at which the frame is positioned. The diagram below shows how the anchorPoint works.

SpriteKit Basics: Sprites

The anchorPoint is specified in the unit coordinate system, which places (0,0) at the bottom left and (1,1) at the top right corner of the frame. The default for SKSpriteNodes is (0.5,0.5).

Go ahead and change the anchorPoint property to (0,0) and notice the difference it makes.

override func didMove(to view: SKView) {
    let startGameLabel = SKLabelNode(text: "Start Game")
    startGameLabel.name = "startgame"
    startGameLabel.position = CGPoint(x: size.width/2, y: size.height/2)
    addChild(startGameLabel)
        
        
    let redSprite = SKSpriteNode(color: .red, size: CGSize(width: 200, height: 200))
    redSprite.anchorPoint = CGPoint(x: 0, y:0)
    addChild(redSprite)
}

Now, if you test, you will see that the sprite is lined up perfectly with the bottom left of the scene.

SpriteKit Basics: Sprites

Now let’s move it to the top center of the scene by changing its position property. Replace your didMove(to:) function with the following:

override func didMove(to view: SKView) {
    let startGameLabel = SKLabelNode(text: "Start Game")
    startGameLabel.name = "startgame"
    startGameLabel.position = CGPoint(x: size.width/2, y: size.height/2)
    addChild(startGameLabel)
        
        
    let redSprite = SKSpriteNode(color: .red, size: CGSize(width: 200, height: 200))
    redSprite.anchorPoint = CGPoint(x: 0, y:0)
    redSprite.position = CGPoint(x: size.width/2 - 100, y: size.height - 210)
    addChild(redSprite)
}

Notice how we had to subtract from both the x and y values to center the sprite. If we had left the anchorPoint at its default then it would already have been centered on the x axis. It is important to remember that when you change the anchor point, you may have to make some adjustments in your positioning.

SpriteKit Basics: Sprites

Textured Sprites

That red box is good for practice with positioning, but you’ll usually want to texture your sprite with artwork for your game.

Let’s create a textured SKSpriteNode. Add the following code at the bottom of the didMove(to:) method.

override func didMove(to view: SKView) {

    ...
        
    let player = SKSpriteNode(imageNamed: "player")
    player.position = CGPoint(x: size.width/2 , y: 300)
    addChild(player)
}

Here we use the convenience initializer init(imageNamed:), which takes as a parameter the name of an image without the extension. This is the easiest way to create a textured sprite as it creates the texture for you from the image you pass in.

The other way to create a textured SKSpriteNode is to create an SKTexture beforehand, and use one of the intializers that take a texture as a parameter.

SpriteKit Basics: Sprites

Let’s create a couple more SKSpriteNodes and change some of their properties. Again, add these to the bottom of your didMove(to:) function.

override func didMove(to view: SKView) {
    
    ...
    
    let enemy1 = SKSpriteNode(imageNamed: "enemy1")
    enemy1.position = CGPoint(x: 100 , y: 300)
    enemy1.xScale = 2
    addChild(enemy1)
        
        
    let enemy2 = SKSpriteNode(imageNamed: "enemy2")
    enemy2.position = CGPoint(x: size.width - 100 , y: 300)
    enemy2.zRotation = 3.14 * 90 / 180
    addChild(enemy2)
}

Here we create two SKSpriteNodes, enemy1 and enemy2. We set the xScale on enemy1 to 2 and change the zRotation on enemy2 to rotate it by 90 degrees. (The zRotation property takes it values in radians, and a positive value indicates a counterclockwise rotation.)

We’ve experimented with changing a few properties on a sprite. Take a look at the documentation for SKNodes and SKSpriteNodes and try changing a few of the other properties to see the effects they have.

SpriteKit Basics: Sprites

Sprite nodes are good for basic rectangles and textures, but sometimes a more complex shape will be needed. The SKShapeNode has you covered in those cases. We’ll take a look at shape nodes next.

Shape Nodes

Another useful node is the SKShapeNode. This node renders a shape defined by a Core Graphics path. SKShapeNodes are useful for content that cannot be easily realized with an SKSpriteNode. This class is more memory intensive and has lower performance than using an SKSpriteNode, so you should try to use it sparingly.

To assign a shape to SKShapeNode, you can set a CGPath to the node’s path property. However, there are a few initializers that offer predefined shapes such as rectangles, circles, and ellipses. Let’s create a circle using the convenience initializer init(circleOfRadius:).

Then, add the following to the bottom of the didMove(to:) method.

override func didMove(to view: SKView) {
        
    ...
        
    let circle = SKShapeNode(circleOfRadius: 50)
    circle.strokeColor = SKColor.green
    circle.fillColor = SKColor.blue
    circle.lineWidth = 8
    circle.position = CGPoint(x: size.width/2, y: 400)
    addChild(circle)
}

We change a few properties on the shape node, position it, and add it to the scene. It is very easy to use the predefined shape initializers. However, creating a complex CGPath manually takes a considerable amount of time and is not for the faint of heart as it usually involves some complex math.

Thankfully, there is a tool that lets you draw shapes visually and export their CGPath as Swift code. Check out PaintCode if you want to learn more.

SpriteKit Basics: Sprites

Sprite Nodes and Shape Nodes will cover most cases, but sometimes you may wish to display video in your apps. The SKVideoNode, which we’ll take a look at next, has you covered.

Video Nodes

The last node we will be taking a look at is the SKVideoNode. As the name implies, this node allows you to play video within your games.

There are a few different ways to create an SKVideoNode. One uses an instance of an AVPlayer, another just uses the name of a video file that is stored in the app bundle, and the third way is to use a URL.

One thing to keep in mind is that the video’s size property will initially be the same as the size of the target video. You can change this size property, though, and the video will be stretched to the new size.

Another thing to be aware of is that the SKVideoNode offers play() and pause() methods only. If you wanted more control over your videos, you would initialize an SKVideoNode with an existing AVPlayer and use that to control your videos.

Let’s use the simplest method to create an SKVideoNode. Add the following to the bottom of the didMove(to:) method.

override func didMove(to view: SKView) {
        
    ...

    let video = SKVideoNode(fileNamed: "video.mov")
    video.position = CGPoint(x: size.width/2,y: 600)
    addChild(video)
    video.play()
}

Here we used the intiailizer init(fileNamed:) to create a video. You pass in the video’s name along with the extension. I have not included a video along with the project’s source code, but if you want to see this work, you can add a video named “video.mov” to your project.

Conclusion

This completes our study on nodes. After reading this post and the previous one, you should have a good understanding of SKNodes and their subclasses. In the next part of this series, we will take a look at SKActions and using physics within our games. Thanks for reading, and I will see you there!

In the meantime, check out some of our other great courses and tutorials on creating iOS apps with Swift and SpriteKit.

  • SpriteKit Basics: Sprites
    SpriteKit
    Create Space Invaders with Swift and Sprite Kit: Introducing Sprite Kit
    James Tyner
  • SpriteKit Basics: Sprites
    iOS SDK
    Create a Blackjack Game in Swift 3 and SpriteKit
    James Tyner
  • SpriteKit Basics: Sprites
    iOS
    SpriteKit From Scratch: Fundamentals
    Davis Allie

Also, check out our SpriteKit courses! These will take you through all the steps of building your first SpriteKit game for iOS, even if you’ve never coded with SpriteKit before.

  • SpriteKit Basics: Sprites
    Swift
    Code a Side-Scrolling Game With Swift 3 and SpriteKit
    Derek Jensen
  • SpriteKit Basics: Sprites
    Game Development
    Game Development With Swift and SpriteKit
    Derek Jensen