在SKNode上剪切一个孔并在Swift中更新其physicsBody

在SKNode上剪切一个孔并在Swift中更新其physicsBody,swift,cut,skphysicsbody,Swift,Cut,Skphysicsbody,我正在创建一个应用程序,其中将包含一些洞的形象。 由于代码非常大,我创建了这个简单的代码,它有着相同的想法。 这段代码已经有了一个物理体设置的图像 我想要的是在touchesbearth函数中,在触摸的位置绘制一些透明的圆,并更新图像physicsBody(在图像上打一个洞) 我在objective C和UIImage中找到了几个代码,有人能帮我处理Swift和SKSpriteNode吗 import SpriteKit class GameScene: SKScene { overr

我正在创建一个应用程序,其中将包含一些洞的形象。 由于代码非常大,我创建了这个简单的代码,它有着相同的想法。 这段代码已经有了一个物理体设置的图像

我想要的是在touchesbearth函数中,在触摸的位置绘制一些透明的圆,并更新图像physicsBody(在图像上打一个洞)

我在objective C和UIImage中找到了几个代码,有人能帮我处理Swift和SKSpriteNode吗

import SpriteKit

class GameScene: SKScene {
    override func didMoveToView(view: SKView) {
        let texture = SKTexture(imageNamed: "Icon.png")
        let node = SKSpriteNode(texture: texture)
        node.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
        addChild(node)

        node.physicsBody = SKPhysicsBody(rectangleOfSize: texture.size())
        node.physicsBody?.dynamic = false

    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {



    }

}
导入SpriteKit
类游戏场景:SKScene{
覆盖func didMoveToView(视图:SKView){
let texture=SKTexture(图像名为:“Icon.png”)
let node=SKSpriteNode(纹理:纹理)
node.position=CGPointMake(CGRectGetMidX(self.frame)、CGRectGetMidY(self.frame))
addChild(节点)
node.physicsBody=SKPhysicsBody(矩形大小:texture.size())
node.physicsBody?.dynamic=false
}
覆盖功能触摸开始(触摸:设置,withEvent事件:UIEvent?){
}
}

一个选项是应用遮罩并将其渲染到图像,然后更新
SKSpriteNode的
纹理。然后使用该纹理确定物理实体。然而,这一过程不会非常有效

例如,在
touchsbegind
中,您可以说:

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        guard let touch = touches.first else { return }
        if let node = self.nodeAtPoint(touch.locationInNode(self.scene!)) as? SKSpriteNode{

            let layer = self.layerFor(touch, node: node) // We'll use a helper function to create the layer

            let image = self.snapShotLayer(layer) // Helper function to snapshot the layer
            let texture = SKTexture(image:image)
            node.texture = texture

            // This will map the physical bounds of the body to alpha channel of texture. Hit in performance
            node.physicsBody = SKPhysicsBody(texture: node.texture!, size: node.size) 
        }
    }
    func layerFor(touch: UITouch, node: SKSpriteNode) -> CALayer
    {
        let touchDiameter:CGFloat = 20.0

        let layer = CALayer()
        layer.frame = CGRect(origin: CGPointZero, size: node.size)
        layer.contents = node.texture?.CGImage()

        let locationInNode = touch.locationInNode(node)

        // Convert touch to layer coordinate system from node coordinates
        let touchInLayerX = locationInNode.x + node.size.width * 0.5 - touchDiameter * 0.5 
        let touchInLayerY = node.size.height - (locationInNode.y + node.size.height * 0.5) - touchDiameter * 0.5

        let circleRect = CGRect(x: touchInLayerX, y: touchInLayerY, width: touchDiameter, height: touchDiameter)
        let circle = UIBezierPath(ovalInRect: circleRect)

        let shapeLayer = CAShapeLayer()
        shapeLayer.frame = CGRect(x: 0.0, y: 0.0, width: node.size.width, height: node.size.height)
        let path = UIBezierPath(rect: shapeLayer.frame)
        path.appendPath(circle)
        shapeLayer.path = path.CGPath
        shapeLayer.fillRule = kCAFillRuleEvenOdd

        layer.mask = shapeLayer
        return layer
    }
在这里,我们只需将当前纹理设置为
CALayer
的内容,然后为要创建的遮罩创建
CAShapeLayer
。我们希望遮罩对层的大部分是不透明的,但是我们想要一个透明的圆,所以我们创建一个矩形的路径,然后在其中添加一个圆。我们将
fillRule
设置为
kCAFillRuleEvenOdd
以填充除了我们的圆圈之外的所有内容

最后,我们将该层渲染为UIImage,以用于更新纹理

    func snapShotLayer(layer: CALayer) -> UIImage
    {
        UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, 0.0)
        let context = UIGraphicsGetCurrentContext()
        layer.renderInContext(context!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }

也许有人会提供一种更有效的方法来实现这一点,但我认为这在很多情况下都是可行的。

一个选项是应用遮罩并将其渲染到图像上,然后更新您的
SKSpriteNode的
纹理。然后使用该纹理确定物理实体。然而,这一过程不会非常有效

例如,在
touchsbegind
中,您可以说:

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        guard let touch = touches.first else { return }
        if let node = self.nodeAtPoint(touch.locationInNode(self.scene!)) as? SKSpriteNode{

            let layer = self.layerFor(touch, node: node) // We'll use a helper function to create the layer

            let image = self.snapShotLayer(layer) // Helper function to snapshot the layer
            let texture = SKTexture(image:image)
            node.texture = texture

            // This will map the physical bounds of the body to alpha channel of texture. Hit in performance
            node.physicsBody = SKPhysicsBody(texture: node.texture!, size: node.size) 
        }
    }
    func layerFor(touch: UITouch, node: SKSpriteNode) -> CALayer
    {
        let touchDiameter:CGFloat = 20.0

        let layer = CALayer()
        layer.frame = CGRect(origin: CGPointZero, size: node.size)
        layer.contents = node.texture?.CGImage()

        let locationInNode = touch.locationInNode(node)

        // Convert touch to layer coordinate system from node coordinates
        let touchInLayerX = locationInNode.x + node.size.width * 0.5 - touchDiameter * 0.5 
        let touchInLayerY = node.size.height - (locationInNode.y + node.size.height * 0.5) - touchDiameter * 0.5

        let circleRect = CGRect(x: touchInLayerX, y: touchInLayerY, width: touchDiameter, height: touchDiameter)
        let circle = UIBezierPath(ovalInRect: circleRect)

        let shapeLayer = CAShapeLayer()
        shapeLayer.frame = CGRect(x: 0.0, y: 0.0, width: node.size.width, height: node.size.height)
        let path = UIBezierPath(rect: shapeLayer.frame)
        path.appendPath(circle)
        shapeLayer.path = path.CGPath
        shapeLayer.fillRule = kCAFillRuleEvenOdd

        layer.mask = shapeLayer
        return layer
    }
在这里,我们只需将当前纹理设置为
CALayer
的内容,然后为要创建的遮罩创建
CAShapeLayer
。我们希望遮罩对层的大部分是不透明的,但是我们想要一个透明的圆,所以我们创建一个矩形的路径,然后在其中添加一个圆。我们将
fillRule
设置为
kCAFillRuleEvenOdd
以填充除了我们的圆圈之外的所有内容

最后,我们将该层渲染为UIImage,以用于更新纹理

    func snapShotLayer(layer: CALayer) -> UIImage
    {
        UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, 0.0)
        let context = UIGraphicsGetCurrentContext()
        layer.renderInContext(context!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }

也许有人会提供一种更有效的方法来实现这一点,但我认为这在很多情况下都适用。

物理实体对节点的视觉外观没有影响。您是要在节点中剪切可视孔,还是在物理体中剪切物理孔,还是两者都要剪切?我要剪切的是两个物理体对节点的可视外观都没有影响。你是想在节点上切割视觉孔,还是在物理体上切割物理孔,或者两者都切割?我想在你的解决方案工作完美的情况下切割两个孔,虽然我认为它会更小,可能更简单。我想可能有更简单的方法,对吗?尽管如此,我还是会等一段时间,看看其他人甚至你是否有更简单的方法,否则我会认为这是正确的!非常感谢!beyowulf您的解决方案非常有效,尽管我认为它会更小,可能更简单。我想可能有更简单的方法,对吗?尽管如此,我还是会等一段时间,看看其他人甚至你是否有更简单的方法,否则我会认为这是正确的!非常感谢!