Giving Physics to Tiles of Sktilemapnode in Xcode 8

Connect Physicsbodies on TileMap in SpriteKit

I recommend applying a line sweep algorithm to merge the tiles together.

You can do this in four steps;

  1. Iterate through the position of the tiles in your SKTileMap.

  2. Find the tiles that are adjacent to one another.

  3. For each group of adjacent tiles, collect:

    • a down-left corner coordinate and
    • an up-right corner coordinate.
  4. Draw a square, and move on to the next group of tiles until you run out of tile coordinates.


The first step: creating an array containing all of your position nodes.

func tilephysics() {

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 (tileMap.tileDefinition(atColumn: col, row: row)?.userData?.value(forKey: "ground") != nil) {

let tileDef = tileMap.tileDefinition(atColumn: col, row: row)!
let tile = SKSpriteNode()

let x = round(CGFloat(col) * tilesize.width - halfwidth + (tilesize.width / 2))
let y = round(CGFloat(row) * tilesize.height - halfheight + (tilesize.height / 2))

tile.position = CGPoint(x: x, y: y)
tile.size = CGSize(width: tileDef.size.width, height: tileDef.size.height)

tileArray.append(tile)
tilePositionArray.append(tile.position)
}
}
}
algorithm()
}

The second and third step: finding adjacent tiles, collecting the two corner coordinates, and adding them to an array:

var dir = [String]()
var pLoc = [CGPoint]()
var adT = [CGPoint]()

func algorithm(){

let width = tileMap.tileSize.width
let height = tileMap.tileSize.height
let rWidth = 0.5 * width
let rHeight = 0.5 * height

var ti:Int = 0
var ti2:Int = 0
var id:Int = 0
var dl:CGPoint = CGPoint(x: 0, y: 0)

var tLE = [CGPoint]()
var tRE = [CGPoint]()

for t in tilePositionArray {

if (ti-1 < 0) || (tilePositionArray[ti-1].y != tilePositionArray[ti].y - height) {

dl = CGPoint(x: t.x - rWidth, y: t.y - rHeight)

}

if (ti+1 > tilePositionArray.count-1) {
tLE.append(dl)

tRE.append(CGPoint(x: t.x + rWidth, y: t.y + rHeight))

} else if (tilePositionArray[ti+1].y != tilePositionArray[ti].y + height) {

if let _ = tRE.first(where: {

if $0 == CGPoint(x: t.x + rWidth - width, y: t.y + rHeight) {id = tRE.index(of: $0)!}

return $0 == CGPoint(x: t.x + rWidth - width, y: t.y + rHeight)}) {

if tLE[id].y == dl.y {

tRE[id] = CGPoint(x: t.x + rWidth, y: t.y + rHeight)

} else {

tLE.append(dl)

tRE.append(CGPoint(x: t.x + rWidth, y: t.y + rHeight))

}

} else {

tLE.append(dl)

tRE.append(CGPoint(x: t.x + rWidth, y: t.y + rHeight))

}

}

ti+=1

}

The fourth step: drawing a rectangle and moving on to the next shape:

for t in tLE {

let size = CGSize(width: abs(t.x - tRE[ti2].x), height: abs(t.y - tRE[ti2].y))
let loadnode = SKNode()

loadnode.physicsBody = SKPhysicsBody(rectangleOf: size)
loadnode.physicsBody?.isDynamic = false
loadnode.physicsBody?.affectedByGravity = false
loadnode.physicsBody?.restitution = 0

loadnode.physicsBody?.categoryBitMask = 2

loadnode.position.x = t.x + size.width / 2
loadnode.position.y = t.y + size.height / 2

scene.addChild(loadnode)

ti2 += 1

}
}

Apply these steps correctly, and you should see that your tiles are merged together in large squares; like so:

Screenshot without visuals for comparison

Screenshot without visuals showing the physicsbodies

I had a lot of fun solving this problem. If I have helped you, let me know.
I only recently started coding and am looking for new challenges. Please reach out to me if you have challenges or projects I could possibly contribute to.

Adding Collision Detection to SKTileMapNode

I'm not entirely sure this will work, but it seems you could add a node to each of a SKTileMapNode's tiles and, based on whatever texture component is on it, give it a physics body appropriately shaped and setup for matching that texture.

Here is someone attempting something like this:

https://forums.developer.apple.com/thread/50043

You don't ask for it, but for greater granularity (eg a tile that's partially covered and needs a sloping physics floor element), it seems this would be the place to start:

https://developer.apple.com/reference/spritekit/sktiledefinition

But I can't find exactly how to know what part of a texture is on any given tile of a tile-map. There must be a way to do this, but I'm just not quite seeing it.



Related Topics



Leave a reply



Submit