使用SpriteKit绘制网格

22
使用SpriteKit 2D游戏引擎绘制类似于网格的最佳方法是什么? 要求:
  • 以编程方式输入列数和行数(例如5x5,10x3,3x4等)。
  • 使用类似于SKSpriteNodeSKShapeNode的东西进行编程绘制,因为仅使用像此方形的图像似乎并不是很有效率。
  • 正方形应具有固定大小(假设每个正方形为40x40)。
  • 网格应在视图中垂直和水平居中。
我计划在这个网格中使用SKSpriteNode(从图像中),作为在不同正方形中移动的玩家。 因此,我将在二维数组中保存每个正方形的中心点(x,y),然后从玩家的当前位置移动到该位置。如果您对此也有更好的建议,请让我知道。 我希望提供一种Swift(最好是2.1)的解决方案,但Objective-C也可以。计划仅在iPhone设备上使用此解决方案。 我的问题与此问题相似。感谢任何帮助。

为什么不使用CGContext(Quartz)来绘制线条呢?看看CGPaths。唯一需要注意的是确保坐标重合。毕竟,精灵就是精灵... - user3821934
网格是否需要占据整个屏幕,因为你只需将屏幕的长度除以所需的网格长度即可。 - Cing
回答您的问题,不需要在整个屏幕上显示网格。 - Alex
嘿,Michael L!感谢您的建议。我不知道如何以有效的方式使用那些CGPaths,但0x141E的答案完美地解决了这个问题。 - Alex
1个回答

41

我建议你将网格实现为SKSpriteNode的纹理,因为Sprite Kit可以在单个绘制调用中呈现网格。以下是如何做到这一点的示例:

class Grid:SKSpriteNode {
    var rows:Int!
    var cols:Int!
    var blockSize:CGFloat!

    convenience init?(blockSize:CGFloat,rows:Int,cols:Int) {
        guard let texture = Grid.gridTexture(blockSize: blockSize,rows: rows, cols:cols) else {
            return nil
        }
        self.init(texture: texture, color:SKColor.clear, size: texture.size())
        self.blockSize = blockSize
        self.rows = rows
        self.cols = cols
    }

    class func gridTexture(blockSize:CGFloat,rows:Int,cols:Int) -> SKTexture? {
        // Add 1 to the height and width to ensure the borders are within the sprite
        let size = CGSize(width: CGFloat(cols)*blockSize+1.0, height: CGFloat(rows)*blockSize+1.0)
        UIGraphicsBeginImageContext(size)

        guard let context = UIGraphicsGetCurrentContext() else {
            return nil
        }
        let bezierPath = UIBezierPath()
        let offset:CGFloat = 0.5
        // Draw vertical lines
        for i in 0...cols {
            let x = CGFloat(i)*blockSize + offset
            bezierPath.move(to: CGPoint(x: x, y: 0))
            bezierPath.addLine(to: CGPoint(x: x, y: size.height))
        }
        // Draw horizontal lines
        for i in 0...rows {
            let y = CGFloat(i)*blockSize + offset
            bezierPath.move(to: CGPoint(x: 0, y: y))
            bezierPath.addLine(to: CGPoint(x: size.width, y: y))
        }
        SKColor.white.setStroke()
        bezierPath.lineWidth = 1.0
        bezierPath.stroke()
        context.addPath(bezierPath.cgPath)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return SKTexture(image: image!)
    }

    func gridPosition(row:Int, col:Int) -> CGPoint {
        let offset = blockSize / 2.0 + 0.5
        let x = CGFloat(col) * blockSize - (blockSize * CGFloat(cols)) / 2.0 + offset
        let y = CGFloat(rows - row - 1) * blockSize - (blockSize * CGFloat(rows)) / 2.0 + offset
        return CGPoint(x:x, y:y)
    }
}

以下是如何创建网格并将游戏棋子添加到网格中的方法

class GameScene: SKScene {
    override func didMove(to: SKView) {
        if let grid = Grid(blockSize: 40.0, rows:5, cols:5) {
            grid.position = CGPoint (x:frame.midX, y:frame.midY)
            addChild(grid)

            let gamePiece = SKSpriteNode(imageNamed: "Spaceship")
            gamePiece.setScale(0.0625)
            gamePiece.position = grid.gridPosition(row: 1, col: 0)
            grid.addChild(gamePiece)
        }
    }
}

更新:

为了确定被触摸的网格方块,将以下代码添加到init中。

self.isUserInteractionEnabled = true

并将此添加到Grid类中:

override func touchesBegan(_ touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch in touches {
        let position = touch.location(in:self)
        let node = atPoint(position)
        if node != self {
            let action = SKAction.rotate(by:CGFloat.pi*2, duration: 1)
            node.run(action)
        }
        else {
            let x = size.width / 2 + position.x
            let y = size.height / 2 - position.y
            let row = Int(floor(x / blockSize))
            let col = Int(floor(y / blockSize))
            print("\(row) \(col)")
        }
    }
}

1
非常感激你的解决方案,@0x141E!这正是我在寻找的。你让 SpriteKit 只需要单次绘制就能渲染网格真的很酷。另外还要感谢你提供了 gridPosition 函数,它比我原先想到的要好得多。 - Alex
你如何区分网格中的每个方块?假设我将skspritenodes添加为Grid类的子节点,我该如何为网格中的每个方块使用touches began? - Parth Sane
将touchesBegan添加到“Grid”类中不起作用。您需要将其添加到添加到网格的节点实例中。我怀疑这是因为您将网格中的框添加为整个网格精灵的子项。因此,我们需要在每个框中继承“SKSpriteNode”并在那里覆盖“touchesBegan”。这就是对我有用的方法。我通过艰苦的方式弄清楚了这一点。感谢您的答案。 - Parth Sane
@Boggartfly,请查看touchesBegan方法的更新。 - 0x141E
对于这个问题,使用SKTileMapNode会更好吗? - undefined
我做了一些实验,发现你可以使用SKTileMapNode在一个绘制调用中渲染一个网格。只需要几行代码,所以可能更方便。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接