Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpriteKit: suggestions for rounding corners of unconventional grid?

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.

like image 819
Crashalot Avatar asked Jan 31 '26 11:01

Crashalot


1 Answers

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:

enter image description here

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:

  • left - bottom background tile
  • left - top background tile
  • right - bottom background tile
  • right - top background tile

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)

enter image description here

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:

enter image description here

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

enter image description here

enter image description here

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:

enter image description here

like image 93
Alessandro Ornano Avatar answered Feb 02 '26 03:02

Alessandro Ornano



Donate For Us

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