Ios Swift SpriteKit反向掩蔽不';不在设备上工作,但在模拟器上工作

Ios Swift SpriteKit反向掩蔽不';不在设备上工作,但在模拟器上工作,ios,swift,sprite-kit,mask,skcropnode,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 = .

实现这一点的代码非常简单:

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


由于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
}
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;
    }
}

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

这里有一个方法可以使用着色器为您生成掩蔽:

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的文件,其中的代码如下所示:

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
}
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)
subtract在模拟器上工作而不是在设备上工作的原因是因为模拟器减去alpha通道,而设备不这样做。设备实际上运行正常,因为alpha不应该被减去,所以应该被忽略

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

if (val.r == 0.0 && val.g == 0.0 && val.b == 0.0) 
给你想要的颜色。(就像你的情况一样。你可以说r=0G=1B=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

我很惊讶模拟器“工作正常”。令人惊喜的是,其他人期望/想要反向掩蔽。不幸的是,目前在SpriteKit中不可能实现。请看这里:谢谢你的评论。看看下面我的答案。这是一个黑客行为,但希望它能帮助其他遇到同样问题的人。这行得通吗?你做反掩蔽吗?而且。。。如果是,您是如何变得如此精通着色器的?而且。。。做得好!它不做反向掩蔽,它所做的只是交换一种颜色并使其alpha为0。无法从我能找到的着色器中进行混合。我仍然希望苹果(有一天)会给雪碧套件一些爱。和反向掩蔽。还有一些更抽象的东西,所以它更像是一个游戏引擎,而不是一个框架,对于那些更喜欢编码而不是工作的人来说。他们首先需要停止破坏东西,我甚至不相信他们有反向屏蔽。现在是他们开放源代码sprite工具包的时候了,因为他们显然不再关心它了。他们测试了这个,它在模拟器上工作,但在设备上不工作:(