What’s New In SpriteKit on iOS 10: A Look At Tile Maps

@ccolombel - I’m sorry - I don’t have an iPhone 5 to try it on. I see what you’re saying; that it’s a 32-bit device. But I don’t know how to get around it. If you do find a way, I’d be grateful if you came back here and posted :smiley:

Hello,

Just to inform you that the issue I had with 32-bit devices is now solved with last Xcode release (I think it has been fixed with Xcode 8.2).

Cheers,

  • ccolombel
1 Like

@ccolombel thanks for reporting back :+1:

Hi Caroline,

First of all, great tutorial! There’s one part which I don’t really get.

  let groundTile = landBackground.tileDefinition(atColumn: column, row: row)
  
  // 9
  let tile = groundTile == nil ? duckTile : gascanTile

How do we know that if landBackground.tileDefinition(atColumn:row:) return nil then it’s a water tile?(So we’ll place a duckTile on it)

1 Like

@sinbad2k - in the scene editor you should have one layer that’s water and one layer that’s grass (landBackground).

The grass layer (landBackground) has transparent areas so that the water layer shows through.

When you test a tile in landBackground, you are testing to see if there is a painted tile. If there is a painted tile, then it will be grass. If there isn’t a painted tile, it’s transparent to show the water through the layer.

So if the tile is nil, then there is no grass tile there, so it must be the water area.

I hope that explains it?

This tutorial is incomplete!
You said you used tile set from kenney.nl…

And? How to import this resource into xcode? It is a single image and xcode understand it as a single image. And you didn’t mentioned how have you split it - with source code, with and external editor, with inner xcode capabilities? The last case is the most preferable but is it even possible?

Hi @gerchicov_bp - the images are included in the starter project.

I downloaded the tile images from: Kenney • Map Pack and dragged them into Assets.xcassets in the starter Xcode project.

The single image in the tutorial is just a display of what some of the tiles look like - you don’t need to do any image splitting.

Ok, thanks but anyways - most of tilesets (not from kenney.nl) are drawn inside a single image. Could you advice how to deal with them?

@gerchicov_bp - if they are intended for use as a tileset, then they should be provided as separate images.

But if you want to cut up an image, you can do it with any image editing program. Even Preview. Just open the original, and then crop it to the tile size and save it out to a new file.

Hello caroline,

thanks for the wonderful tutorial. I have the following problem and can’t seems to find a fix!

I am developing a game that uses tileMapNode.

I create a tilemapnode in code as follows-

guard let playerChips = SKTileSet(named: “PlayerChips”) else {
fatalError(“Object Tiles Tile Set not found”)
}
gameBoardMap = SKTileMapNode(tileSet: playerChips,
columns: columns,
rows: rows,
tileSize: size)
gameBoardMap.anchorPoint = CGPoint(x:0.5,y:0.5)
gameBoardMap.xScale = 2.32
gameBoardMap.yScale = 1.789
addChild(gameBoardMap)

Then I create a SKTileGroup as follows-

guard let playerChip = coloredChips.first(where : {$0.name == “redWhite_border” }) else {
fatalError(“Chip not found!!”)
}

I add/place the “tile” at the appropriate row and column on the tile map as-

gameBoardMap.setTileGroup(playerChip, forColumn: column, row: row)
gameBoardMap.zPosition = 1 // If i do not set the zPosition the tile does not display

So far everything works fine. The app loads and when the SKTileMapNode is created the correct “tile” is placed at the correct column and row.

Now later in the game, I have a requirement to remove the tile from the “column” and “row” of the tilemapnode. I tried different things -

  1. gameBoardMap.setTileGroup(nil, forColumn: column, row: row)

  2. let emptyTile = SKTileGroup.empty()
    gameBoardMap.setTileGroup(emptyTile, forColumn: column, row: row)

Nothing works !! The code is trying to remove the tile from the correct column and row. But ilte continues to stay put! My question is -

How to remove the tile from the view on the SK tilemapnode by code?

@natarajan - In the tutorial I use:

  objectsTileMap.setTileGroup(nil, forColumn: column, row: row)

which removes the duck/gas can tiles. So it could be you’re trying to set nil on the wrong tile map, or perhaps the column / row is wrong?

Could it be something to do with the scaling code? Try it without scaling, if you can.

P.S. It’s easier to read code if it’s formatted. To do this, you can either start a new line and indent by four spaces, or surround the code with a backtick ` character.

thanks for contributing!

1 Like

Very informative. Thank you for sharing.

The only part that wasn’t very clear to me how to achieve multiple layers of background. It took me a while to get that I needed to create two TileMapNodes, one for water and another for the grass. Hope this helps someone else.

What would you suggest for a way to detect the edges of a tile map node so that you can programmatically add more tiles? I’m thinking maybe setTileGroup(_:forColumn:row:) might possibly work…

The documentation suggests that with auto-mapping enabled a tile might modify neighboring tiles. So perhaps you could change an edge tile’s definition when the player enters the tile, and the new definition could cause it to spawn more tiles nearby? But I feel like I’m expecting more from this API than is intended.

Thanks for the great introduction to tile maps!

@surfridersb - you can check to see whether a tile is nil. So when you land on a tile, check the surrounds and if they are nil, then fill them in?

@caroline - Checking for nil sounds like a good idea. Since you are at the edge of the map you will probably need to add some new rows/columns before you can fill them in. Maybe you could do this by adjusting the mapSize or numberOfColumns and numberOfRows properties of your SKTileMapNode. Thanks for the tip!

@surfridersb - you could also have tiled SKTileMapNodes and remove the ones that are a long way away and add new ones on the edge as you reach there

@caroline - thanks for the idea! I’ll try it out and post my solution if I can get it working.

Hello Very nice tutorial .

i have a little issue about Tile recognition when player move on.
I saw you use integer in the user data. I use this technique too and I got the same result if I use Boolean.
i wan’t the player node to detect the style of the tile he’s on.

i got a TileMapnode , with a set of 4 groupe tiles. The map is an Hexagone pointy top type.

this is the code i have.

  func move(theXAmount:CGFloat , theYAmount:CGFloat, theAnimation:String )  {
    
    
   let wait:SKAction = SKAction.wait(forDuration: 0.05)
    
    let walkAnimation:SKAction = SKAction(named: theAnimation, duration: moveSpeed )!
    
    let moveAction:SKAction = SKAction.moveBy(x: theXAmount, y: theYAmount, duration: moveSpeed )
    
    let group:SKAction = SKAction.group( [ walkAnimation, moveAction ] )
    
    let finish:SKAction = SKAction.run {
        
        
    }
    
    
    
    let completionHandler = SKAction.run ({
        let position = self.thePlayer.position
        
        let column = self.galaxieMapnode.tileColumnIndex(fromPosition: position)
        let row = self.galaxieMapnode.tileRowIndex(fromPosition: position)
        
        
        
        if  let tile:SKTileDefinition = self.galaxieMapnode.tileDefinition(atColumn: column, row: row) {
            
            
            let tileBoolData = tile.userData?["wormhole"] as? Bool
            
            
            
            if tileBoolData == true {
                
                
                print("Wormhole touched")
                
                
            } else {
                
                print("different tile")
                
            }
            
            
            
            
            
        }
        //scope tiles overs
    })

    
    
    
    var seq = [SKAction]()
    
    seq += [wait, group, finish, completionHandler]
    
    self.thePlayer.run(SKAction.sequence(seq))
    
    
}

but this code only print the output “different tile” , although i setup the user data correctly in the tile.
Any idea how i can implement this ? i try to pus this with a gesture , in the update scope , in any case only the base tile are printed.

I understand this can be off topic but I really need help on this.

Any help are welcome :slight_smile:the thing i wanna do

@4math - I wonder if you are doing everything correctly.

Try setting up a very simple SpriteKit game using the template.

Add a tile set and set up hexagons with the sample tile image data. For the grass tile, add user data “grass” with Boolean set to 1.

Set up a tile map node in the the Scene Editor named “tilemap” and paint it with various tiles including grass.

In GameScene.swift, add the variable for the tile map node:

private var tilemap: SKTileMapNode!

In didMove(to:) add this:

  tilemap = childNode(withName: "tilemap") as! SKTileMapNode

Remove all the code to do with spinny and label.

In touchDown(atPoint:), add this code:

  let column = tilemap.tileColumnIndex(fromPosition: pos)
  let row = tilemap.tileRowIndex(fromPosition: pos)

  if let tile: SKTileDefinition = tilemap.tileDefinition(atColumn: column, row: row) {
    let isGrass = tile.userData?["grass"] as? Bool
    if isGrass == true {
      print("grass")
    } else if isGrass == nil {
      print("nil")
    } else {
      print("not grass")
    }
  }

This works for me. If I touch down on a grass tile I get grass printed, otherwise I get nil printed.