Class

SKTexture

A representation of an image for use in SpriteKit.

Declaration

class SKTexture : NSObject

Overview

An SKTexture object is an image that can be applied to SKSpriteNode and SKShapeNode objects, particles created by an SKEmitterNode object or tiles used in an SKTileMapNode. A texture object manages the texture data and graphics resources that are needed to render the image. Most texture objects are created from source images stored in your app bundle—your game’s artwork. Once created, a texture object’s contents are immutable. Multiple sprites can share the same texture object, sharing a single resource.

Use the init(imageNamed:) method to create the SKTexture object. When the texture object is initialized using an image file, SpriteKit must perform two steps before it can use the texture to render sprites. First, it must load the texture data from the file. Second, it must prepare the texture data to be used by the graphics hardware. Both of these steps can be expensive compared to other SpriteKit operations. In particular, loading a texture from a file is very expensive. SpriteKit defers loading and preparing the texture as long as possible and also provides methods you can use to control these steps.

The texture data is loaded when:

  • The size() method on the texture object is called.

  • Another method is called that requires the texture’s size, such as creating a new SKSpriteNode object that uses the texture object.

  • One of the preload methods is called (See Preloading the Texture Data.)

The texture data is prepared for rendering when:

  • A sprite or particle that uses the texture is part of a node tree that is being rendered.

Once the SKTexture object is ready for rendering, it stays ready until all strong references to the texture object are removed.

Texture objects can be created from data generated at runtime. For example, you can create the texture from a Quartz 2D image, from raw pixel data, or by applying a Core Image filter to an existing texture. You can also call the texture(from:) method to render a node tree into a texture. None of these textures must be loaded because their data is already in memory when the texture is created. However, the texture must still be prepared by SpriteKit before it can be used for rendering.

To create textures that can be updated at runtime, see the SKMutableTexture class.

Managing Texture Memory

Texture objects can consume a lot of memory, depending on the size of your source data. And often, the memory available to prepared textures is limited by the hardware, because the textures need to be loaded into VRAM. So, most games must carefully manage the memory used by textures. Consider the following tips when creating your game:

  • Access a texture object only when needed and dispose of the object when you are confident that it is no longer needed by your game. This frees the memory and makes it available to other texture objects.

  • Avoid loading too many textures in a single pass through the rendering loop. If you do, SpriteKit may skip one or more frames to finish loading the texture data. Instead, preload your content before starting your game.

  • If you frequently use the same textures at the same time to render your sprites, do not load each texture from a separate image file. Instead, group the texture images logically into one or more texture atlases. See SKTextureAtlas for more information.

For a longer discussion of texture management, see Working with Sprites.

Normal Map Textures

A normal map texture is similar to an image texture, but instead of holding image data to be displayed onscreen, every texel represents a normal vector. Normal map textures are used to simulate 3D lighting (see the normalTexture property) or used to generate velocity values (see velocityField(with:)).

You can create normal maps in two different ways. First, you can take an existing image map and use it to generate a normal map. SpriteKit filters the color data in the texture and then uses it to generate a map based on pixel contrast. Alternatively, you can load a regular image file but treat it as a normal map. To do this, provide a texture with 32-bit RGBx pixel data. Each component’s 8-bit integer value is mapped to a floating point number between the values of -1.0 and 1.0. Use a 0 to represent -1.0f, a value of 127 to represent 0.0, and a value of 255 to represent +1.0.

The image below shows two sprite nodes both with the same texture. The node on the right has a normal map from the same noise texture generated using the generatingNormalMap() method.

Figure 1

Comparison of sprites with and without normal mapping

Comparison of sprites with and without normal mapping

Extracting the Contents of a Texture

The cgImage() property returns the contents of a texture as a Quartz Image. For example, to take a screenshot of your scene, use the texture(from:) method to render the scene’s contents to a texture and then read the cgImage() property of that texture.

Preloading Textures Into Memory

A major advantage to SpriteKit is that it performs a lot of memory management for you automatically. When rendering a new frame of animation, SpriteKit determines whether a texture is needed to render the current frame. If a texture is needed but is not prepared for rendering, SpriteKit loads the texture data from the file, transforms the data into a format that the graphics hardware can use, and uploads it to the graphics hardware. This process happens automatically in the background, but it isn’t free. If too many unloaded textures are needed at once, it may be impossible to load all the textures in a single frame of animation, causing the frame rate to stutter. To avoid this problem, you need to preload textures into memory, particularly in larger or complex games.

Listing 1 shows how to preload an array of SKTexture objects. The preload(_:withCompletionHandler:) method calls the completion handler after all of the textures are loaded into memory. In this example, all of the textures for a particular level of the game are preloaded in a single operation. When the textures are all in memory, the completion handler is called. It creates the scene and presents it. (You need to add code to provide these texture objects to the scene; that code isn’t shown here).

SKTexture.preload(textureArrayForLevel1) {
    // The textures are loaded into memory. Start the level.
    let gameScene = GamePlayScene(size: CGSize(width: 768, height: 1024))
    
    if let spriteView = view as? SKView {
        spriteView.presentScene(gameScene)
    }
}

Because you are intimately familiar with the design of your game or app, you are the best person to know when new textures are needed. The exact design of your preloading code is going to depend on your game engine. Here are a few possible designs to consider:

  • For a small game or app, you may be able to preload all of its textures when the app is launched, and then keep them in memory forever.

  • For a larger game or app, you may need to split the textures into levels or themes. Each level or theme’s textures are designed to fit in a specific amount of memory. When the player starts a new level, you preload all of that level’s texture objects. When the player finishes playing the level, the textures not needed for the next level are discarded. By preloading the levels, the load time is all up front, before gameplay starts.

  • If a game needs more textures than can fit into memory, you need to preload textures dynamically as the game is being played. Typically, you preload some textures at the start of a level, and then load other textures when you think they will be needed soon. For example, in a racing game, the player is always moving in the same direction, so for each frame you might fetch a new texture for content the player is about to see. The textures are loaded in the background, displacing the oldest textures for the track. In an adventure game that allows for player-controlled movement, you might have to provisionally load textures when a player is moving in a particular direction.

Removing a Texture From Memory

After a texture is loaded into the graphics hardware’s memory, it stays in memory until the referencing SKTexture object is deleted. This means that between levels (or in a dynamic game), you may need to make sure a texture object is deleted. Delete a SKTexture object object by removing any strong references to it, including:

  • All texture references from SKSpriteNode and SKEffectNode objects in your game

  • Any strong references to the texture in your own code

  • An SKTextureAtlas object that was used to create the texture object

Subclassing Notes

This class cannot be subclassed.

Topics

Creating New Textures from Images

init(imageNamed: String)

Create a new texture object from an image file stored in the app bundle.

init(image: UIImage)

Create a new texture object from an image object.

init(cgImage: CGImage)

Create a new texture object from a Quartz 2D image.

init(rect: CGRect, in: SKTexture)

Creates a new texture from a subset of an existing texture.

func applying(CIFilter) -> Self

Creates a new texture by applying a Core Image filter to an existing texture.

func cgImage() -> CGImage

Returns the texture’s image data as a Quartz 2D image.

Creating Textures from Raw Pixel Data

init(data: Data, size: CGSize)

Creates a new texture from raw pixel data.

init(data: Data, size: CGSize, rowLength: UInt32, alignment: UInt32)

Creates a new texture from custom formatted raw pixel data.

init(data: Data, size: CGSize, flipped: Bool)

Creates a new texture from raw pixel data.

Creating Normal Map Textures

func generatingNormalMap() -> Self

Creates a normal map texture by analyzing the contents of an existing texture.

func generatingNormalMap(withSmoothness: CGFloat, contrast: CGFloat) -> Self

Creates a normal map texture by analyzing the contents of an existing texture.

Creating Noise Textures

init(vectorNoiseWithSmoothness: CGFloat, size: CGSize)

Creates a new texture whose contents are procedurally generated directional noise data.

init(noiseWithSmoothness: CGFloat, size: CGSize, grayscale: Bool)

Creates a new texture whose contents are procedurally generated colored noise data.

Creating Textures from Noise Maps

init(noiseMap: GKNoiseMap)

Creates a texture from the specified noise map.

Inspecting a Texture’s Properties

var filteringMode: SKTextureFilteringMode

The filtering mode used when the size of a sprite drawn with the texture is not drawn at the texture’s native size.

func size() -> CGSize

The size of the texture.

func textureRect() -> CGRect

A rectangle that defines the portion of the texture used to render its image.

var usesMipmaps: Bool

A Boolean value that indicates whether the texture attempts to generate mipmaps.

Preloading the Texture Data

func preload(completionHandler: () -> Void)

Load the texture data into memory, calling a completion handler after the task completes.

class func preload([SKTexture], withCompletionHandler: () -> Void)

Load the texture data of multiple textures into memory.

Constants

enum SKTextureFilteringMode

Texture filtering modes to use when the texture is drawn in a size other than its native size.

See Also

Working with Textures

class SKTextureAtlas

A collection of textures.

class SKMutableTexture

A texture whose contents can be dynamically updated.