Как вырезать случайные отверстия в SKSpriteNodes
Помимо того, что здесь задан вопрос: нарисовать отверстие в прямоугольнике с помощью SpriteKit? на этот вопрос не было получено удовлетворительного ответа, самое существенное различие между ними заключается в том, что этот вопрос требует отсутствия обходных путей и, в частности, требует отмены функциональности SKCropNodes.
Основные проблемы в этом вопросе не могут быть решены типом хакерских способов, на которые можно было ответить на вышеупомянутый вопрос из-за случайности отверстий, количества отверстий и изменения в объектах, к которым должны быть применены отверстия.
Отсюда и пример швейцарского сыра:
Представьте себе кучу прямоугольных SKSpriteNode узлов произвольного размера, наполненных различными оттенками сыроподобных цветов.
Как сделать швейцарский сыр из каждого, вырезав случайные кружочки из каждого кусочка сыра?
Если узлы SKCropNode являются кружками, они оставляют только круглые кусочки сыра, а не вырезают отверстия из кусочков сыра. Есть ли способ инвертировать поведение SKCropNodes, чтобы они вместо этого вырезали отверстия?
1 ответ
Если честно, я не совсем понимаю, чего вы хотите достичь, но я могу попытаться ответить на этот вопрос:
... вырезать случайные круги из каждого кусочка сыра?
В этом проекте я пытаюсь воссоздать типичный прямоугольник (кусок сыра) со случайными отверстиями, затем я извлекаю эти отверстия и собираю их в массив.
import SpriteKit
class GameScene: SKScene {
struct Cheese
{
static let color1 = SKColor(red: 255/255, green: 241/255, blue: 173/255, alpha: 1)
static let color2 = SKColor(red: 255/255, green: 212/255, blue: 0/255, alpha: 1)
static let color3 = SKColor(red: 204/255, green: 170/255, blue: 0/255, alpha: 1)
static let color4 = SKColor(red: 140/255, green: 116/255, blue: 0/255, alpha: 1)
}
let cheeseColor = [Cheese.color1,Cheese.color2,Cheese.color3,Cheese.color4]
override func didMove(to view: SKView) {
let totHoles = randomNumber(range:4...8)
let color = randomNumber(range:0...3)
let cheeseCropNode = makeCheese(size: CGSize(width:400,height:200),color: cheeseColor[color], totHoles:totHoles)
cheeseCropNode.position = CGPoint(x:0,y:-50)
addChild(cheeseCropNode)
// Start to collect and show holes
var holes = [SKNode]()
var counter = 1
let _ = cheeseCropNode.enumerateChildNodes(withName: "//hole*", using:{ node, stop in
// node is the hole
let pos = self.convert(node.position, from: cheeseCropNode)
let sprite = SKSpriteNode.init(color: .red, size: node.frame.size)
sprite.position = pos
//Remove these shapes, it's just to debug
let shape = SKShapeNode.init(rect: sprite.frame)
shape.strokeColor = .red
self.addChild(shape)
// -- end to remove
let holeTxt = SKView().texture(from: cheeseCropNode, crop: sprite.frame)
let hole = SKSpriteNode.init(texture: holeTxt)
hole.position = CGPoint(x:-(self.frame.maxX)+(100*CGFloat(counter)),y:150)
hole.name = node.name
self.addChild(hole)
holes.append(hole)
counter += 1
})
}
func randomNumber(range: ClosedRange<Int> = 1...6) -> Int {
let min = range.lowerBound
let max = range.upperBound
return Int(arc4random_uniform(UInt32(1 + max - min))) + min
}
func randomCGFloat(min: CGFloat, max: CGFloat) -> CGFloat {
return (CGFloat(arc4random()) / CGFloat(UINT32_MAX)) * (max - min) + min
}
func makeCheese(size:CGSize , color:SKColor, totHoles:Int)->SKCropNode {
let cropNode = SKCropNode()
let cheese = SKSpriteNode.init(color: color, size: size)
for i in 0..<totHoles {
let radius = randomCGFloat(min:20.0, max:50.0)
let circle = SKShapeNode(circleOfRadius: radius)
circle.position = CGPoint(x:randomCGFloat(min:-size.width/2, max:size.width/2),y:randomCGFloat(min:-size.height/2, max:size.height/2))
circle.fillColor = color
circle.blendMode = .subtract
circle.name = "hole\(i)"
cheese.addChild(circle)
}
cropNode.addChild(cheese)
cropNode.maskNode = cheese
return cropNode
}
}
Результат:
PS Не обращайте внимания на красные прямоугольники, это просто чтобы показать вам отверстия:
Если вы хотите точно перевернутое отверстие (негативное изображение), вы можете использовать SKCropNode
с hole.blendMode
, например:
Подставьте эту часть кода:
// -- end to remove
let holeTxt = SKView().texture(from: cheeseCropNode, crop: sprite.frame)
let hole = SKSpriteNode.init(texture: holeTxt)
hole.position = CGPoint(x:-(self.frame.maxX)+(100*CGFloat(counter)),y:150)
hole.name = node.name
self.addChild(hole)
holes.append(hole)
counter += 1
с этой частью:
// -- end to remove
let holeTxt = SKView().texture(from: cheeseCropNode, crop: sprite.frame)
let hole = SKSpriteNode.init(texture: holeTxt)
hole.position = CGPoint(x:-(self.frame.maxX)+(100*CGFloat(counter)),y:150)
hole.name = node.name
let negativeCropHole = SKCropNode()
let shadow = SKShapeNode.init(rect: hole.frame)
shadow.fillColor = (node as! SKShapeNode).fillColor
shadow.strokeColor = SKColor.clear
hole.blendMode = .subtract
negativeCropHole.addChild(shadow)
negativeCropHole.maskNode = shadow
negativeCropHole.addChild(hole)
negativeCropHole.name = hole.name
self.addChild(negativeCropHole)
holes.append(negativeCropHole)
counter += 1
Результат (другой пример):
Надеюсь, что этот пример и этот код помогут вам в достижении ваших целей. Я использовал прямоугольники для создания масок, но вы можете создавать CGPaths, если вам нужно.