The goal is to round the corners of an unconventional grid similar to the following:
https://s-media-cache-ak0.pinimg.com/564x/50/bc/e0/50bce0cb908913ebc2cf630d635331ef.jpg
https://s-media-cache-ak0.pinimg.com/564x/7e/29/ee/7e29ee80e957ec22bbba630ccefbfaa2.jpg
Instead of a grid with four corners like a conventional grid, these grids have multiple corners in need of rounding.
The brute force approach would be to identify tiles with corners exposed then round those corners either with a different background image or by clipping the corners in code.
Is there a cleaner approach?
The grid is rendered for an iOS app in a SpriteKit SKScene.
This is a really interesting question.You can build your matrix with different approaches but surely you must resolve everytime the changes about the 4 corners in background for each tiles.
Suppose you start with a GameViewController like this (without load SKS files and with anchorPoint equal to zero):
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .resizeFill
scene.anchorPoint = CGPoint.zero
view.presentScene(scene)
}
}
My idea is to build a matrix like this:
import SpriteKit
class GameScene: SKScene {
private var sideTile:CGFloat = 40
private var gridWidthTiles:Int = 5
private var gridHeightTiles:Int = 6
override func didMove(to view: SKView) {
self.drawMatrix()
}
func drawMatrix(){
var index = 1
let matrixPos = CGPoint(x:50,y:150)
for i in 0..<gridHeightTiles {
for j in 0..<gridWidthTiles {
let tile = getTile()
tile.name = "tile\(index)"
addChild(tile)
tile.position = CGPoint(x:matrixPos.x+(sideTile*CGFloat(j)),y:matrixPos.y+(sideTile*CGFloat(i)))
let label = SKLabelNode.init(text: "\(index)")
label.fontSize = 12
label.fontColor = .white
tile.addChild(label)
label.position = CGPoint(x:tile.frame.size.width/2,y:tile.frame.size.height/2)
index += 1
}
}
}
func getTile()->SKShapeNode {
let tile = SKShapeNode(rect: CGRect(x: 0, y: 0, width: sideTile, height: sideTile), cornerRadius: 10)
tile.fillColor = .gray
tile.strokeColor = .gray
return tile
}
}
Output:

Now we can construct a background for each tile of our matrix. We can made the same tile node but with a different color (maybe more clear than the tile color) and without corner radius. If we split this background in 4 parts we have:
Code for a typical background tile:
func getBgTileCorner()->SKShapeNode {
let bgTileCorner = SKShapeNode(rect: CGRect(x: 0, y: 0, width: sideTile/2, height: sideTile/2))
bgTileCorner.fillColor = .lightGray
bgTileCorner.strokeColor = .lightGray
bgTileCorner.lineJoin = .round
bgTileCorner.isAntialiased = false
return bgTileCorner
}
Now with the SKSCropNode we can obtain only the corner using the background tile and the tile:
func getCorner(at angle:String)->SKCropNode {
let cropNode = SKCropNode()
let tile = getTile()
let bgTile = getBgTileCorner()
cropNode.addChild(bgTile)
tile.position = CGPoint.zero
let tileFrame = CGRect(x: 0, y: 0, width: sideTile, height: sideTile)
switch angle {
case "leftBottom": bgTile.position = CGPoint(x:tile.position.x,y:tile.position.y)
case "rightBottom": bgTile.position = CGPoint(x:tile.position.x+tileFrame.size.width/2,y:tile.position.y)
case "leftTop": bgTile.position = CGPoint(x:tile.position.x,y:tile.position.y+tileFrame.size.height/2)
case "rightTop": bgTile.position = CGPoint(x:tile.position.x+tileFrame.size.width/2,y:tile.position.y+tileFrame.size.height/2)
default:break
}
tile.fillColor = self.backgroundColor
tile.strokeColor = self.backgroundColor
tile.lineWidth = 0.0
bgTile.lineWidth = 0.0
tile.blendMode = .replace
cropNode.position = CGPoint.zero
cropNode.addChild(tile)
cropNode.maskNode = bgTile
return cropNode
}
Output for a typical corner:
let corner = getCorner(at: "leftBottom")
addChild(corner)
corner.position = CGPoint(x:50,y:50)

Now we can rebuild the drawMatrix function with the corners for each tile:
func drawMatrix(){
var index = 1
let matrixPos = CGPoint(x:50,y:150)
for i in 0..<gridHeightTiles {
for j in 0..<gridWidthTiles {
let tile = getTile()
tile.name = "tile\(index)"
let bgTileLB = getCorner(at:"leftBottom")
let bgTileRB = getCorner(at:"rightBottom")
let bgTileLT = getCorner(at:"leftTop")
let bgTileRT = getCorner(at:"rightTop")
bgTileLB.name = "bgTileLB\(index)"
bgTileRB.name = "bgTileRB\(index)"
bgTileLT.name = "bgTileLT\(index)"
bgTileRT.name = "bgTileRT\(index)"
addChild(bgTileLB)
addChild(bgTileRB)
addChild(bgTileLT)
addChild(bgTileRT)
addChild(tile)
tile.position = CGPoint(x:matrixPos.x+(sideTile*CGFloat(j)),y:matrixPos.y+(sideTile*CGFloat(i)))
let label = SKLabelNode.init(text: "\(index)")
label.fontSize = 12
label.fontColor = .white
tile.addChild(label)
label.position = CGPoint(x:tile.frame.size.width/2,y:tile.frame.size.height/2)
bgTileLB.position = CGPoint(x:tile.position.x,y:tile.position.y)
bgTileRB.position = CGPoint(x:tile.position.x,y:tile.position.y)
bgTileLT.position = CGPoint(x:tile.position.x,y:tile.position.y)
bgTileRT.position = CGPoint(x:tile.position.x,y:tile.position.y)
index += 1
}
}
}
Output:

Very similar to your screenshots (these are two tile example:)


Now when you want to remove a tile, you can decide what corner you want to remove or leave because for each tile you have also the relative 4 corners :
Output:

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With