Swift SpriteKit反向屏蔽在设备上不起作用,但在Simulator中有效

时间:2017-12-11 18:58:07

标签: ios swift sprite-kit mask skcropnode

完成此任务的代码非常简单:

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

image

2 个答案:

答案 0 :(得分:2)

这是一个使用着色器为您生成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只能在绿色上裁剪)

Result of above code on a device

编辑:我想要注意减去混合不是必需的,这也可行:

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

答案 1 :(得分: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)

Here is a screenshot from a device using above technique

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

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进行更新以支持反向屏蔽,但与此同时,这对我的用例很有用。