Physics (collision) on SKTileMap

In the What's New in SpriteKit WWDC video, a feature was shown off that you can apply custom data to certain tiles (the edgeTile boolean, shown at 21:00). What else (other code) do I need to make a sprite collide with these tiles?

Replies

Also wondering about this.

I'm also really interested to know how he did it.


After reading around on the internet about how people are doing it with Cocos2d and other frameworks, I'm planning to create a node for each tile just so that I can add a physics body to it. I'll probably add user data to the TileDefinition objects like he did. Then, I'll walk the map and generate the physics bodys and nodes that way. Some people create one shape (or a few shapes if they're not connected) from the whole map. In my game, I don't want to do that because my tiles will occassionally change and I don't want to have to regenerate the physics body.


If others have better ideas, I'm very interested.


This is what I'm doing right now:

self.tileMap = self.childNode(withName: "Tile Map") as? SKTileMapNode
guard let tileMap = self.tileMap else { fatalError("Missing tile map for the level") }

let tileSize = tileMap.tileSize
let halfWidth = CGFloat(tileMap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(tileMap.numberOfRows) / 2.0 * tileSize.height

for col in 0..<tileMap.numberOfColumns {
    for row in 0..<tileMap.numberOfRows {
        let tileDefinition = tileMap.tileDefinition(atColumn: col, row: row)
        let isEdgeTile = tileDefinition?.userData?["edgeTile"] as? Bool
        if (isEdgeTile ?? false) {
            let x = CGFloat(col) * tileSize.width - halfWidth
            let y = CGFloat(row) * tileSize.height - halfHeight
            let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
            let tileNode = SKShapeNode(rect: rect)
            tileNode.position = CGPoint(x: x, y: y)
            tileNode.physicsBody = SKPhysicsBody.init(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))
            tileNode.physicsBody?.isDynamic = false
            tileNode.physicsBody?.collisionBitMask = playerCollisionMask | wallCollisionMask
            tileNode.physicsBody?.categoryBitMask = wallCollisionMask
            tileMap.addChild(tileNode)
        }
    }
}

Hey,


I was wondering if your solution worked for you ? I tried it on my game and still my player keeps falling through the tiles .. if yours worked can i know more details that i could be missing ? like some definitions or where did you include that code ? in the update function ? thats what i did .. let me know i would appreciate some help on this.

Agreed, I think your best bet is basically a "dummy" node for each tile that just defines a physics body. The body doesn't need to actually have any visual artwork with it. You'd end up with many physics bodies in a row of course, but just as simple boundary, thats fine.

Welldone, Its worked for me too. thanks alot. 🙂🙂🙂

Hi All,


I had a look at this and found you can make the tilemap objects have collision detection of alpha mask. here is the code.


func tileMapPhysics(name: SKTileMapNode)

{

let tileMap = name

/

let tileSize = tileMap.tileSize

let halfWidth = CGFloat(tileMap.numberOfColumns) / 2.0 * tileSize.width

let halfHeight = CGFloat(tileMap.numberOfRows) / 2.0 * tileSize.height

for col in 0..<tileMap.numberOfColumns {

for row in 0..<tileMap.numberOfRows {

if let tileDefinition = tileMap.tileDefinition(atColumn: col, row: row)

{

let isEdgeTile = tileDefinition.userData?["hit"] as? Int

if (isEdgeTile != 0)

{

let tileArray = tileDefinition.textures

let tileTexture = tileArray[0]

let x = CGFloat(col) * tileSize.width - halfWidth + (tileSize.width/2)

let y = CGFloat(row) * tileSize.height - halfHeight + (tileSize.height/2)

let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)

let tileNode = SKNode()

/

tileNode.position = CGPoint(x: x, y: y)

tileNode.physicsBody = SKPhysicsBody(texture: tileTexture,

size: CGSize(width: (tileTexture.size().width * 2),

height: (tileTexture.size().height * 2)))

tileNode.physicsBody?.linearDamping = 60.0

tileNode.physicsBody?.affectedByGravity = false

tileNode.physicsBody?.allowsRotation = false

tileNode.physicsBody?.restitution = 0.0

tileNode.physicsBody?.isDynamic = false

tileNode.physicsBody?.collisionBitMask = CollisionCategoryBitmask.frogmanCC

tileNode.physicsBody?.categoryBitMask = CollisionCategoryBitmask.chestCC

tileNode.name = "jelly"

tileNode.yScale = tileMap.yScale

tileNode.xScale = tileMap.xScale

/

tileMap.addChild(tileNode)

}

}

}

}

}


You can not have 2 objects that are both Alpha Masks i.e main sprite and object in tilemap, as it is overload on the detection procressing.

dontangg,


Correct, adding an invisible child node for each tile in the SKTileMapNode that needs a physics body is the right approach. SKTileMapNode doesn't use nodes for its tiles and therefore its tiles are incapable of having physics bodies. A simple example would be to add the node (that has the physics body for a given tile) as a child node to the SKTileMapNode at the same position and anchor point for the tile that needs the physics body.

Thank you for contributing some insight regarding the best approach for this. It helps to know what the recommended practice is for something that is a common workflow in SpriteKit, but does not have a clear solution.


What I'd really like to see is for Apple to enhance the tile map API so that we can add physics bodies directly to individual tiles. Maybe in the process they could optimize the engine so that physics bodies on individual tiles would be automatically merged to form larger, more optimal shapes in order to improve system performance. (Apple Bug Reporter request filed under report number 38488605).

>What I'd really like to see is



Suggestions such as this are perhaps best put to the bug reporter, link bottom right, adding your report# to your thread for reference, thanks and good luck.

Cocos2D... wow I didnt think I would hear that again.

Cocos2D and objective C? Or the Android version.

Thats how I initially learned how to code.


Until Swift just swept right on in.


Brings back memories. LOL.