Working with Tiles

SKTiled provides several ways to work with tiles in your tilemap. Most of the methods for accessing tiles from a tile layer instances have corresponding methods in the parent SKTilemap node, which will aggregate the results from all tile layers.

Tile IDs

The SKTile.globalId property determines how each tile is rendered.

tiles.globalId = 13

Querying Tiles with ID

Global ID

To query tiles with a global ID, pass the value to either the SKTilemap node, or an individual SKTileLayer node:

// query tiles from the tilemap node
let tiles = tilemap.getTiles(globalID: 10)

// query tiles from the parent layer
let tiles = tileLayer.getTiles(globalID: 10)

Local ID

If you have a reference to a tileset object and want to query tile data with a local id, use the SKTileset.getTileData(localID:) method. Here’s a reference to an external tileset with a firstgid value of 1. So if we want to access tile data with a local id of 79, we can query the tileset instance directly:

Tileset Data

if let tiledata = tileset.getTileData(localID: 79) {
    print(tiledata)
}
// Tile ID: 78 @ 16x16, 6 frames

As you can see, the tile data is correctly returned. The local ID is the sum of the tileset’s firstgid value plus the tile’s internal ID: (1 + 78 = 79).

Tile Render Mode

Once your tilemap is loaded, you may tweak the way tiles are rendered individually via the SKTile.renderMode property:

Mode Description
default Tile renders at default settings.
static Tile ignores any animation data.
ignore Tile does not take into account its tile data.
animated Animate with a global id value.
// instruct tile to ignore animation
tile.renderMode = TileRenderMode.static

// tile will animate with a global id of 129
tile.renderMode = TileRenderMode.animated(gid: 129)

The SKTile.renderMode flag allows you to quickly manipulate the appearance of a tile. While the animated case implies that the tile should be animated, setting this value with any global id effectively makes the tile take the appearance of another. Be aware, however, peoperties in the new tile data are not carried over to this tile… the original data still exists.

Querying Tiles at Location

Accessing tiles is simple: simply query a tile layer or the tile map node for tiles at a given coordinate:

let coord = simd_int2(10, 8)

// return a tile from a layer at the given coordinate
let tilesForCoord = tileLayer.tileAt(coord: coord)

// query all tiles from a tilemap at the given coordinate
let allTilesForCoord = tilemap.tilesAt(coord: coord)

Tile Flags

Each tile instance has a tileID property which accepts a UInt32 value and applies tile flip flags accordingly.

// a global id of 2147483659 translates to 11, with horizontal & diagonal flip flags
let tile = tilemap.getTiles(globalID: 11).first!

// get the `TileID` object
let globalId = tile.tileID

tile.isFlippedHorizontally = true

Tile Alignment

{ gif here showing alignment/anchorpoint changing }

Find Tiles of Type

The string property type can be used to label or group tiles in SKTiled. Adding a string property to a tile ID will allow it to be accessed from SpriteKit:

Tile Types

Querying any tile with that property is simple:

let allFireTiles = tilemap.getTiles(ofType: "Fire")
let fireTiles = tileLayer.getTiles(ofType: "Fire")

Find Tiles with Property

You are not restricted to using type as a property name - any property name/value can be queried:

let fireTiles = tilemap.getTilesWithProperty("Attack", "Fire")
let waterTiles = tilemap.getTilesWithProperty("isWater", true)

Adding and Removing Tiles

To add a new tile using a GID, use the SKTileLayer.addTile(at:) method to add it to the current layer:

if let tile = tileLayer.addTile(at: simd_int2(5, 8), globalID: 32) {
    tile.setupPhysics(withSize: 8)
}

You may also specify a tile type with this method, for use with the TilemapDelegate.objectForTileType protocol method:

if let wallTile = tileLayer.addTile(at: simd_int2(2, 17), globalID: 145, tileType: "Wall") {
    // set the custom tile property
    wallTile.hitMaxCount = 3
}

To remove a tile, simply call one of the SKTileLayer.removeTile methods:

if let removedTile = tileLayer.removeTile(10, 8) {
    // do something with tile
}

You are not limited to using tile objects; any SKNode type can be added to a layer and positioned. All SKTiledLayerObject objects have expanded addChild convenience methods for positioning nodes:

// add a child with a coordinate and offset and zPosition values
tileLayer.addChild(tile, 5, 8, offset: CGPoint(x: 4.0, y: 8.0), zpos: 50)

// add a child with a coordinate and offset-x value
tileLayer.addChild(tile, 5, 8, dx: 4)

It’s important to note that adding an object (or another tile) at a coordinate does not add the object to a tile layer’s array of tiles - the node is simply positioned at the location of the coordinate in the given layer.

Refreshing Tile Content

At any time, if you need to update a tile, use the SKTile.draw() method.

Creating New Tiles

Creating a new tile is very simple. You can create one with a tile data instance:

let tiledata = tileset.getTileData(globalID: 11)!
let tile = SKTile(data: tiledata)!

You can also create tiles from with a SKTileset or SKTilemap instance with a global id or local id:

// create tiles from a tileset instance, with an option to specify tile type
let tile = tileset.newTile(localID: 1)
let tile = tileset.newTile(globalID: 50, type: MyTileType.self)


// create tiles from a tilemap instance, with an option to specify tile type
let tile = tilemap.newTile(localID: 1)
let tile = tilemap.newTile(globalID: 50, type: MyTileType.self)

Copying Tiles

Tiles can be copied for use as regular sprites, simply use the SKTile.spriteCopy method to copy a tile to a regular SKSpriteNode instance.

Tile Shapes

It is also possible to create shapes from individual tiles. All SKTiled geometry types have a getVertices() method which will return an array of points that constitute the object’s shape.

let tileVerts = tile.getVertices()

Tile Colors

Tint Color

let tileVerts = tile.getVertices()

Animated Tiles

Animated Tiles

Tile animations exist within the SKTilesetData class, accessible via the SKTilesetData.frames property. To check whether the tileset data is animated, simply query the SKTileset.isAnimated attribute.

Tile animations in Tiled will render if you add your tilemap instance to your SpriteKit scene’s SKScene.update method. Animated tiles are easily accessed with the parent SKTileLayer.animatedTiles method, or globally via the SKTilemap.animatedTiles method:

// return animated tiles in a single layer
let animatedTiles = tileLayer.animatedTiles()

// return all animated tiles
let allAnimatedTiles = tilemap.animatedTiles()

All SKTile instances allow you to pause and change the animation speed:

// pause or unpause tile animation
for animatedTile in animatedTiles {
    animatedTile.isPaused = true
}

// double animation speed
for animatedTile in animatedTiles {
    animatedTile.speed = 2.0
}

// run animation backwards
for animatedTile in animatedTiles {
    animatedTile.speed = -1
}

Be aware that updating animation speed on objects running SpriteKit actions doesn’t always work correctly. See the SpriteKit Actions section for more details.

To toggle animation on an individual tile (or tile object), use the SKTile.enableAnimation flag:

// enable animation on a tile instance
tile.enableAnimation = true

// disable animation on a tile object
object.enableAnimation = false

To stop animation, use the SKTile.removeAnimation(restore:) method to remove the animation, and the SKTile.runAnimation method to restore it:

// remove the animation (optionally restore the original texture)
for animatedTile in animatedTiles {
    animatedTile.removeAnimation(restore: true)
}

// restart tile animations
for animatedTile in animatedTiles {
    animatedTile.runAnimation()
}

Updating Tile Animation

It’s easy to add or change the animation for tile objects, but bear in mind that as SKTilesetData objects are still linked to their parent tileset, changing or removing animation one will change every tile that references that data.

To change the animated frames of a tile, you will first need to access the tile data, either from the tile itself, or the parent tileset:

// access from tile
let tileData = tile.tileData

// access from tile
let tileData = tileset.tileData

To add animation to a tile, add ID values to the SKTilesetData instance. Frame durations are stored in milliseconds to match Tiled’s:

tileData.addFrame(withID: 33, interval: 250)
tileData.addFrame(withID: 34, interval: 350)
tileData.addFrame(withID: 35, interval: 150)

You also have the option of manipulating frames directly:

// set the texture for a specific frame. The old texture is returned (if one exists)
let jumpTexture = SKTexture(imageNamed: "jump-alt-002")
let oldTexture = tileData.setTexture(jumpTexture, forFrame: 2)

// change the frame interval for a frame (in milliseconds)
if tileData.setDuration(interval: 200, forFrame: 2) {
    print("frame 2 duration is now 200!")
}

SpriteKit Actions

You might get better CPU usage if you render tile animations with SpriteKit actions. See the SpriteKit Actions section for more details.

Physics

Physics can be turned on for tile objects with the SKTileObject.setupPhysics methods. Passing the argument isDynamic determines whether the physics body is active or passive.

// create a physics body with a rectangle of size 8
tile.setupPhysics(shapeOf: .rectangle, isDynamic: true)

// create a physics body with a rectangle of size 8
tile.setupPhysics(rectSize: CGSize(width: 8, height: 8), isDynamic: true)

// setup dynamics on an array of tiles with a radius of 4
let dots = dotsLayer.getTilesWithProperty("type", "dot" as AnyObject)
dots.forEach { $0.setupDynamics(radius: 4) }

Tile Overlap

The tile overlap value is used to help alleviate the “cracks” that sometimes appear when the tilemap or worldNode is scaled. The value is clamped with the SKTile.maxOverlap value. Usually a value between 1.0 - 3.0 is effective. While you can set the overlap value on individual tiles & tile layers, for best results set it via the SKTilemap.tileOverlap property:

// this will override values for every tile
tilemap.tileOverlap = 1.0

// set the overlap for an entire layer
tileLayer.setTileOverlap(1.0)

// set the overlap on individual tiles
tile.setTileOverlap(1.0)

Next: Coordinates - Index