NovumCoder NovumCoder - 19 days ago 15
Swift Question

SpriteKit: Howto make holes in layer with blendMode

I have a simple scene with some elements added to it.

Now i want focus on a specific element with a mask a cut a whole at the same position as the element I want to focus on. Very similar to what we can see on some games when they are started first time showing some kind of tutorial.

Basically I am adding a fullscreen layer with

alpha=0.7
(so user still see all the content) but then add a circle at a specific position as this layers child and set
blendMode=
. Subtract so it "cuts" out a circle from this fullscreen layer so within this circle you have a clear view.

I have the following code after I have added all the elements onto the screen.

// before this code i added some basic elements like circles and backgrounds
let mask = SKSpriteNode(color: .blackColor(), size: self._screenSize)
mask.anchorPoint = CGPoint.zero
mask.position = CGPoint.zero
mask.zPosition = 100
mask.alpha = 0.7

let circle = SKShapeNode(circleOfRadius: Constants.Config.playersize*2)
circle.fillColor = .blackColor()
circle.lineWidth = 0
let circle_mask = SKSpriteNode(texture: SKView().textureFromNode(circle, crop: circle.frame))
circle_mask.blendMode = .Subtract
circle_mask.zPosition = 101

// now show the layer with alpha=0.7 and the circle mask being at the same position as my player element i want to focus on
mask.addChild(circle_mask)
circle_mask.position = player.position
self.addChild(mask)


But this simply adds the fullscreen layer, no circle holes. Looks like it is ignoring the circle_mask node. What am I doing wrong?

My plan is also to keep moving the circle mask to focus on other elements on this scene. As I understand. Subtract it should subtract only from its parent node, which is the fullscreen layer with
alpa=0.7
, right?

I just tried with SKCropNode. Added the fullscreen layer as a child into the crop node, then assigned the circle as mask. But now its cutting out nearly everything but just showing a circle of my fullscreen layer, i actually need the inverted result of this crop node.

The problem with blendMode is that is runs it on the final framebuffer, but what i need is to run it on the parent node only, so it does not cut everything behind the node when using .Subtract

Answer

Here is an example on how to create a translucent layer with a hole punched into it:

-(NOTE: .subtract blend mode does not take alpha of the sprite into consideration, so you need to use a crop node)

self.backgroundColor = .red


//Our transparent overlay
let fullScreen = SKSpriteNode(color: .black, size: self.size)
fullScreen.position = CGPoint.zero
fullScreen.zPosition = 100
fullScreen.alpha = 0.7

//let's make a mask to punch circles in our shape
let mask = SKSpriteNode(color: .white, size: self.size)
mask.position = CGPoint.zero
mask.zPosition = 100
mask.alpha = 1

let circle = SKShapeNode(circleOfRadius: 20*2)
circle.fillColor = .white
circle.lineWidth = 0
circle.alpha = 1

//let circle_mask = SKSpriteNode()
circle.blendMode = .subtract
mask.addChild(circle)

//let's create the node to place on screen
let crop = SKCropNode()
crop.maskNode = mask
crop.addChild(fullScreen)

self.addChild(crop)