Connect Physicsbodies on Tilemap in Spritekit

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.

SpriteKit physics: How to make a player sprite follow the (sloped) ground

This has more to do with your game logic instead of your map properties. You need to have several "states" for your player. For example, if your player is idle you can set the CGVector to 0,0. This will stop the player from moving in any direction.

To give you some examples on movement. Let's say you want to make your object move right:

// move node's physics body to the right while leaving vertical movement as is
// 160 is just an example
myNode.physicsBody.velocity = CGVectorMake(160, self.physicsBody.velocity.dy);

// do not allow right movement to exceed 160
if(myNode.physicsBody.velocity.dx > 160)
myNode.physicsBody.velocity = CGVectorMake(160, self.physicsBody.velocity.dy);

To move left you inverse the dx value:

myNode.physicsBody.velocity = CGVectorMake(-160, self.physicsBody.velocity.dy);

if(myNode.physicsBody.velocity.dx < -160)
myNode.physicsBody.velocity = CGVectorMake(-160, self.physicsBody.velocity.dy);

Allow myNode to be affected by gravity and it should remain in contact with the ground as it moves down a slope.



Related Topics



Leave a reply



Submit