How to crop out a SKLabelNode from a SKShapeNode?
If you invert your thinking, this will be much easier.
Put the Red layer at the back/bottom. Lowest zPosition. Let's say -1.0
for this example.
Put the picture on top of this, at a zPosition of 0.0
Put the letter above, at a zPostion of 1.0
So, from the screen of the iPhone, it's like this:
- Screen
- Letter
- Picture
- Red BG
You want to use the letter to mask out the picture.
Having done that, whenever you move the letter, it reveals the contents beneath itself, from the picture. Everything else is RED.
How to rotate SKLabelNode in company with its parent SKShapeNode?
You need to set your friction to a number other than 0.
Also, concerning the shape node performance:
look at the draw count at the bottom of 50 shapes:
for _ in 1...50 {
let x = arc4random_uniform(UInt32(frame.maxX));
let xx = CGFloat(x) - size.width/4
let y = arc4random_uniform(UInt32(frame.maxY))
let yy = CGFloat(y) - size.width/4
let shape = SKShapeNode(circleOfRadius: 50)
shape.strokeColor = .blue
shape.lineWidth = 1
shape.position = CGPoint(x: xx, y: yy)
addChild(shape)
}
But now compare that to this image of only 2 draws with only a few lines of refactoring:
func addFiftySprites() {
let shape = SKShapeNode(circleOfRadius: 50)
shape.strokeColor = .blue
shape.lineWidth = 1
let texture = SKView().texture(from: shape)
for _ in 1...50 {
let x = arc4random_uniform(UInt32(frame.maxX));
let xx = CGFloat(x) - size.width/4
let y = arc4random_uniform(UInt32(frame.maxY))
let yy = CGFloat(y) - size.width/4
let sprite = SKSpriteNode(texture: texture)
sprite.position = CGPoint(x: xx, y: yy)
addChild(sprite)
}
}
The magic here is using let texture = SKView().texture(from: <SKNode>)
to convert the shape to a sprite :) let sprite = SKSpriteNode(texture: texture)
Crop SKTexture with SKLabel text
Knight0fDragon have reason in his comment, this is a good suggestion. There is another solution you can do without textureFromNode
.
You can prepare your cropNode
with the background and the label, then make another background clone with the button and your cropNode over all:
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let background = SKSpriteNode.init(imageNamed: "background.png")
background.zPosition = 1
let background2 = background.copy() as! SKSpriteNode
//label
let titleLabel = SKLabelNode(fontNamed: "AvenirNext-Bold")
titleLabel.fontSize = 150
titleLabel.fontColor = SKColor.black
titleLabel.text = "RATE"
titleLabel.horizontalAlignmentMode = .center
titleLabel.zPosition = 5
titleLabel.blendMode = .subtract
//cropNode
let cropNode = SKCropNode()
cropNode.addChild(background)
cropNode.maskNode = titleLabel
self.addChild(cropNode)
cropNode.zPosition = 5
//button
let button = SKShapeNode(rect: CGRect(x: -300, y: -50, width: 600, height: 200), cornerRadius: 100)
button.zPosition = 4
button.fillColor = SKColor.white
self.addChild(background2)
self.addChild(button)
}
}
Image used:
Output:
Another background to test:
Output 2:
Is it possible to use a circle (SKShapeNode) as a mask in Sprite Kit?
After much swearing, scouring the web, and experimentation in Xcode, I have a really hacky fix.
Keep in mind that this is a really nasty hack - but you can blame that on Sprite Kit's implementation of SKShapeNode
. Adding a fill to a path causes the following:
- adds an extra node to your scene
- the path becomes unmaskable - it appears 'over' the mask
- makes any non-
SKSpriteNode
sibling nodes unmaskable (e.g.SKLabelNode
s)
Not an ideal state of affairs.
Inspired by Tony Chamblee's progress timer, the 'fix' is to dispense with the fill altogether, and just use the stroke of the path:
SKCropNode *cropNode = [[SKCropNode alloc] init];
SKShapeNode *circleMask = [[SKShapeNode alloc ]init];
CGMutablePathRef circle = CGPathCreateMutable();
CGPathAddArc(circle, NULL, CGRectGetMidX(self.frame), CGRectGetMidY(self.frame), 50, 0, M_PI*2, YES); // replace 50 with HALF the desired radius of the circle
circleMask.path = circle;
circleMask.lineWidth = 100; // replace 100 with DOUBLE the desired radius of the circle
circleMask.strokeColor = [SKColor whiteColor];
circleMask.name=@"circleMask";
[cropNode setMaskNode:circleMask];
As commented, you need to set the radius to half of what you'd normally have, and the line width to double the radius.
Hopefully Apple will look at this in future; for now, this hack is the best solution I've found (other than using an image, which doesn't really work if your mask needs to be dynamic).
SKCropNode fails when I add extra SKNode children in hierarchy
SKShapeNode is bugged, best to avoid it at all costs. Use it to create your shapes, then convert it to a texture for use with SKSpriteNode
Related Topics
Swift Cannot Infer Type from Context
Swiftui: How to Switch to a New Navigation Stack with Navigationviews
Playing Hls (M3U8) in Cocoa Os X Avplayer - Swift
Swift 4.1 Deinitialize and Deallocate(Capacity:) Deprecated
How to Print Escape Sequence Characters in Swift
Core Data with Pre-Filled .Sqlite (Swift3)
Big O of Accessing a String with an Index in Swift 3.0
Xcode - Build Setting "Excluded_Source_File_Names" Not Working
How to Get the Url from Webview in Swift
How to Read File Data Applications Document Directory in Swift
How to Customize the Title/Subtitle Font in Callout from Mkannotationview or Just Hide Them
Sktexture Nearest Filtering Mode Doesn't Work (Making Pixel Art)
Swift: Differencebetween a Typealias and an Associatedtype with a Value in a Protocol