SpriteKit:关于非常规网格四角的建议?

时间:2017-04-18 05:07:04

标签: ios sprite-kit skspritenode skscene

目标是围绕非传统网格的角落,类似于以下内容:

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

这些网格不是像传统网格那样有四个角的网格,而是需要四舍五入的角落。

蛮力方法是识别带有角落的瓷砖,然后使用不同的背景图像或通过在代码中剪切角来围绕这些角落。

有更清洁的方法吗?

为SpriteKit SKScene中的iOS应用程序渲染网格。

4 个答案:

答案 0 :(得分:4)

这是一个非常有趣的问题。您可以使用不同的方法构建矩阵,但每次瓦片背景中的4个角落的变化时,您必须解决这个问题。

假设您从这样的ticker_list = ['s','f'] df = pd.DataFrame({'ticker':['s','d','f']}) print (df) ticker 0 s 1 d 2 f df['target'] = df['ticker'].isin(ticker_list).astype(int) print (df) ticker target 0 s 1 1 d 0 2 f 1 开始(没有加载GameViewController文件且SKS等于零):

anchorPoint

我的想法是建立一个这样的矩阵:

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)
    }
}

<强>输出

enter image description here

现在我们可以为矩阵的每个tile构建一个背景。 我们可以制作相同的图块节点但具有不同的颜色(可能比图块颜色更清晰)并且没有角半径。如果我们将这个背景分为4个部分,我们有:

  • 左 - 底部背景图块
  • 左 - 顶部背景图块
  • 右 - 底部背景图块
  • 右 - 顶部背景图块

典型背景图块的代码:

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
    }
}

现在使用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 } ,我们只能使用背景图块和图块获取角落:

SKSCropNode
典型角落的

输出

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
    }

enter image description here

现在我们可以使用每个磁贴的角重建let corner = getCorner(at: "leftBottom") addChild(corner) corner.position = CGPoint(x:50,y:50) 函数:

drawMatrix

<强>输出

enter image description here

非常类似于您的屏幕截图(这些是两个图块示例:)

enter image description here

enter image description here

现在,当您想要移除图块时,您可以决定要删除或离开的角落,因为对于每个图块,您还有相对的4个角:

<强>输出

enter image description here

答案 1 :(得分:1)

好的,网格创建过程并不是真的与此相关。您只需要某种方法来区分网格中的空白点和填充点。在我的例子中,我有一个类型为.blank或.regular的Tile对象。您需要拥有所有15个图像(您可以将样式更改为您喜欢的任何图像,尽管它们必须按相同的顺序排列,并且必须以1..15为前缀)。它使用位计算来确定要用作背景的图像,并将背景图像偏移为x和y的1/2图块大小。除此之外,它是非常自我解释。那些背景图像是我在开发时创建的测试图像,所以请随意使用它们。

without background     with background

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

struct GridPosition {

    var col: Int = 0
    var row: Int = 0
}

class GameScene: SKScene {

    private var backgroundLayer = SKNode()
    private var tileLayer = SKNode()
    private var gridSize: CGSize = CGSize.zero
    private var gridRows: Int = 0
    private var gridCols: Int = 0
    private var gridBlanks = [Int]()
    private var tiles = [[Tile]]()
    var tileSize: CGFloat = 150

    override func didMove(to view: SKView) {

        backgroundLayer.zPosition = 1
        addChild(backgroundLayer)

        tileLayer.zPosition = 2
        addChild(tileLayer)

        gridRows = 8
        gridCols = 11
        gridBlanks = [0,1,3,4,5,6,7,9,10,11,12,13,15,16,17,19,20,21,22,23,31,32,33,36,40,43,56,64,67,69,70,71,72,73,75,77,78,79,82,85,86,87]

        createGrid()

        createBackgroundTiles()
    }

    func createGrid() {

        for row in 0 ..< gridRows {

            var rowContent = [Tile]()

            for col in 0 ..< gridCols {

                let currentTileLocation: Int = row * gridCols + col
                var tile: Tile

                if gridBlanks.contains(currentTileLocation) {
                    tile = Tile(row: row, col: col, type: .blank, tileSize: tileSize)
                }
                else {
                    tile = Tile(row: row, col: col, type: .regular, tileSize: tileSize)
                }

                tile.position = positionInGrid(column: col, row: row)
                tile.zPosition = CGFloat(100 + gridRows - row)
                tileLayer.addChild(tile)
                rowContent.append(tile)
            }

            tiles.append(rowContent)
        }
    }

    func tileByGridPosition(_ gridPos: GridPosition) -> Tile {
        return (tiles[Int(gridPos.row)][Int(gridPos.col)])
    }

    func positionInGrid(column: Int, row: Int) -> CGPoint {

        let startX = 0 - CGFloat(gridCols / 2) * tileSize
        let startY = 0 - CGFloat(gridRows / 2) * tileSize + tileSize / 2

        return CGPoint(

            x: startX + CGFloat(column) * tileSize,
            y: startY + CGFloat(row) * tileSize)
    }

    func createBackgroundTiles() {

        for row in 0...gridRows {

            for col in 0...gridCols {

                let topLeft = (col > 0) && (row < gridRows) && tileByGridPosition(GridPosition(col: col - 1, row: row)).type == .regular
                let bottomLeft = (col > 0) && (row > 0) && tileByGridPosition(GridPosition(col: col - 1, row: row - 1)).type == .regular
                let topRight = (col < gridCols) && (row < gridRows) && tileByGridPosition(GridPosition(col: col, row: row)).type == .regular
                let bottomRight = (col < gridCols) && (row > 0) && tileByGridPosition(GridPosition(col: col, row: row - 1)).type == .regular

                // The tiles are named from 0 to 15, according to the bitmask that is made by combining these four values.
                let value = Int(NSNumber(value: topLeft)) | Int(NSNumber(value: topRight)) << 1 | Int(NSNumber(value: bottomLeft)) << 2 | Int(NSNumber(value: bottomRight)) << 3

                // Values 0 (no tiles)
                if value != 0 {

                    var gridPosition = positionInGrid(column: col, row: row)
                    gridPosition.x -= tileSize / 2
                    gridPosition.y -= tileSize / 2

                    let backgroundNode = SKSpriteNode(imageNamed: ("background_tile_\(value)"))
                    backgroundNode.size = CGSize(width: tileSize, height: tileSize)
                    backgroundNode.alpha = 0.8
                    backgroundNode.position = gridPosition
                    backgroundNode.zPosition = 1
                    backgroundLayer.addChild(backgroundNode)
                }
            }
        }
    }
}

class Tile: SKSpriteNode {

    private var row = 0
    private var col = 0
    var type: TileType = .blank

    init(row: Int, col: Int, type: TileType, tileSize: CGFloat) {

        super.init(texture: nil ,color: .clear, size:CGSize(width: tileSize, height: tileSize))

        self.type = type
        size = self.size

        let square = SKSpriteNode(color: type.color, size: size)
        square.zPosition = 1
        addChild(square)
    }
}

答案 2 :(得分:0)

唯一想到的是当一个节点接触另一个节点时,在那个时刻评估所述节点的显示,以及更改受其影响的邻居。

答案 3 :(得分:0)

我们做的是铺设瓷砖,然后调用此函数来围绕暴露瓷砖的节点。

// Rounds corners of exposed tiles. UIKit inverts coordinates so top is bottom and vice-versa.
fileprivate func roundTileCorners() {
    // Get all tiles
    var tiles = [TileClass]()
    tileLayer.enumerateChildNodes(withName: ".//*") { node, stop in
        if node is TileClass {
            tiles.append(node as! TileClass)
        }
    }

    // Round corners for each exposed tile
    for t in tiles {
        // Convert tile's position to root coordinates
        let convertedPos = convert(t.position, from: t.parent!)

        // Set neighbor positions
        var leftNeighborPos = convertedPos
        leftNeighborPos.x -= tileWidth
        var rightNeighborPos = convertedPos
        rightNeighborPos.x += tileWidth
        var topNeighborPos = convertedPos
        topNeighborPos.y += tileHeight
        var bottomNeighborPos = convertedPos
        bottomNeighborPos.y -= tileHeight

        // Set default value for rounding
        var cornersToRound : UIRectCorner?

        // No neighbor below & to left? Round bottom left.
        if !isTileAtPoint(point: bottomNeighborPos) && !isTileAtPoint(point: leftNeighborPos) {
            cornersToRound = cornersToRound?.union(.topLeft) ?? .topLeft
        }

        // No neighbor below & to right? Round bottom right.
        if !isTileAtPoint(point: bottomNeighborPos) && !isTileAtPoint(point: rightNeighborPos) {
            cornersToRound = cornersToRound?.union(.topRight) ?? .topRight
        }

        // No neightbor above & to left? Round top left.
        if !isTileAtPoint(point: topNeighborPos) && !isTileAtPoint(point: leftNeighborPos) {
            cornersToRound = cornersToRound?.union(.bottomLeft) ?? .bottomLeft
        }

        // No neighbor above & to right? Round top right.
        if !isTileAtPoint(point: topNeighborPos) && !isTileAtPoint(point: rightNeighborPos) {
            cornersToRound = cornersToRound?.union(.bottomRight) ?? .bottomRight
        }

        // Any corners to round?
        if cornersToRound != nil {
            t.roundCorners(cornersToRound: cornersToRound!)
        }
    }
}

// Returns true if a tile exists at <point>. Assumes <point> is in root node's coordinates.
fileprivate func isTileAtPoint(point: CGPoint) -> Bool {
    return nodes(at: point).contains(where: {$0 is BoardTileNode })
}