3

完成此操作的代码非常简单:

var cropNode = SKCropNode()
var shape = SKShapeNode(rectOf: CGSize(width:100,height:100))
shape.fillColor = SKColor.orange

var shape2 = SKShapeNode(rectOf: CGSize(width:25,height:25))
shape2.fillColor = SKColor.red
shape2.blendMode = .subtract
shape.addChild(shape2)

cropNode.addChild(shape)
cropNode.position = CGPoint(x:150,y:170)
cropNode.maskNode=shape
container.addChild(cropNode)

相同的代码,相同的 iOS,不同的结果 = 没有 bueno

图片

4

2 回答 2

3

这是一个使用着色器为您生成 maskNode 的方法:

func generateMaskNode(from mask:SKNode) -> SKNode
{
    var returningNode : SKNode!
    autoreleasepool
    {
        let view = SKView()
        //First let's flatten the node
        let texture = view.texture(from: mask) 
        let node = SKSpriteNode(texture:texture) 
        //Next apply the shader to the flattened node to allow for color swapping
        node.shader = SKShader(fileNamed: "shader.fsh")
        let texture2 = view.texture(from: node)
        returningNode = SKSpriteNode(texture:texture2)

    }
    return returningNode
}

它需要您创建一个名为 shader.fsh 的文件,里面的代码如下所示:

void main() {

    // Find the pixel at the coordinate of the actual texture
    vec4 val = texture2D(u_texture, v_tex_coord);

    // If the color value of that pixel is 0,0,0
    if (val.r == 0.0 && val.g == 0.0 && val.b == 0.0) {
        // Turn the pixel off
        gl_FragColor  = vec4(0.0,0.0,0.0,0.0);
    } 
    else {
        // Otherwise, keep the original color
        gl_FragColor = val;
    }
}

要使用它,它要求您使用黑色像素而不是 alpha 作为确定裁剪内容的方法,因此您的代码现在应该如下所示:

var cropNode = SKCropNode()
var shape = SKShapeNode(rectOf: CGSize(width:100,height:100))
shape.fillColor = SKColor.orange

var shape2 = SKShapeNode(rectOf: CGSize(width:25,height:25))
shape2.fillColor = SKColor.orange
shape2.blendMode = .subtract
shape.addChild(shape2)

let mask = generateMaskNode(from:shape)

cropNode.addChild(shape)
cropNode.position = CGPoint(x:150,y:170)
cropNode.maskNode=mask
container.addChild(cropNode)

减法在模拟器而不是设备上起作用的原因是因为模拟器减去了 alpha 通道,而设备却没有。该设备实际上表现正确,因为不假设要减去 alpha,所以应该忽略它。

请注意,您不必选择黑色作为裁剪颜色,您可以更改着色器以允许您选择任何颜色,只需更改行:

if (val.r == 0.0 && val.g == 0.0 && val.b == 0.0) 

到你想要的颜色。(就像你的情况一样。你可以说 r = 0 g = 1 b = 0 只在绿色上裁剪)

以上代码在设备上的结果

编辑:我想指出减去混合不是必需的,这也可以:

var cropNode = SKCropNode()
var shape = SKShapeNode(rectOf: CGSize(width:100,height:100))
shape.fillColor = SKColor.orange

var shape2 = SKShapeNode(rectOf: CGSize(width:25,height:25))
shape2.fillColor = SKColor.black
shape2.blendMode = .replace
shape.addChild(shape2)

let mask = generateMaskNode(from:shape)

cropNode.addChild(shape)
cropNode.position = CGPoint(x:150,y:170)
cropNode.maskNode=mask
container.addChild(cropNode)

现在我无法测试,这引出了一个问题,甚至需要我的功能。下面的代码理论上应该可以工作,因为它正在用上面的像素替换底层像素,所以理论上 alpha 应该转移过来。如果有人可以对此进行测试,请告诉我它是否有效。

var cropNode = SKCropNode()
var shape = SKShapeNode(rectOf: CGSize(width:100,height:100))
shape.fillColor = SKColor.orange

var shape2 = SKShapeNode(rectOf: CGSize(width:25,height:25))
shape2.fillColor = SKColor(red:0,green:0,blue:0,alpha:0)
shape2.blendMode = .replace
shape.addChild(shape2)

cropNode.addChild(shape)
cropNode.position = CGPoint(x:150,y:170)
cropNode.maskNode= shape.copy() as! SKNode
container.addChild(cropNode)

替换只替换颜色而不是 alpha

于 2017-12-12T12:35:01.327 回答
0

由于 SpriteKit 似乎并不固有地提供反向屏蔽(以在设备上工作的方式),我认为以下是最接近答案的事情:

let background = SKSpriteNode(imageNamed:"stocksnap")
background.position = CGPoint(x:65, y:background.size.height/2)
addChild(background)

let container = SKNode()
let cropNode = SKCropNode()

let bgCopy = SKSpriteNode(imageNamed:"stocksnap")
bgCopy.position = background.position
cropNode.addChild(bgCopy)

let cover = SKShapeNode(rect: CGRect(x:0,y:0,width:200,height:200))
cover.position = CGPoint(x:80,y:150)
cover.fillColor = SKColor.orange
container.addChild(cover)

let highlight = SKShapeNode(rectOf: CGSize(width:100,height:100))
highlight.position = CGPoint(x:cover.position.x+cover.frame.size.width/2,y:cover.position.y+cover.frame.size.height/2)
highlight.fillColor = SKColor.red

cropNode.maskNode = highlight
container.addChild(cropNode)

addChild(container)

这是使用上述技术的设备的屏幕截图

这只是使用背景的副本,对其进行遮罩,然后将其覆盖在同一位置以创建反向遮罩效果。在您想要复制屏幕上的任何内容的情况下,您可以使用以下内容:

func captureScreen() -> SKSpriteNode {
    var image = UIImage()
    if let view = self.view {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)

        view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)

        if let imageFromContext = UIGraphicsGetImageFromCurrentImageContext() {
            image = imageFromContext
        }
        UIGraphicsEndImageContext()
    }
    let texture = SKTexture(image:image)
    let sprite = SKSpriteNode(texture:texture)
    //scale is applicable if using fixed screen sizes that aren't the actual width and height
    sprite.scale(to: CGSize(width:size.width,height:size.height))
    sprite.anchorPoint = CGPoint(x:0,y:0)
    return sprite
}

希望有人找到更好的方法或对 SpriteKit 进行更新以支持反向屏蔽,但同时这对我的用例来说很好。

于 2017-12-12T08:03:59.187 回答