Making SKLabelNode as a crop node of SKShapeNode - swift

Can't find in web, how to make SKLabelNode cropping SKShapeNode. When i colorize the background, my goal is colorize the label too with the same method, so both of them have to colorize simultaneously. But can't imagine how to crop SKShapeNode with this label. Help me please!

But can't imagine how to crop SKShapeNode with this label.
If I understand you correctly, you can set SKLabelNode as a mask of a SKCropNode, like this:
override func didMoveToView(view: SKView) {
backgroundColor = .blackColor()
let cropNode = SKCropNode()
cropNode.position = CGPoint(x: frame.midX, y: frame.midY)
cropNode.zPosition = 1
let mask = SKLabelNode(fontNamed: "ArialMT")
mask.text = "MASK"
mask.fontColor = .greenColor()
mask.fontSize = 28
cropNode.maskNode = mask
let nodeToMask = SKSpriteNode(color: .purpleColor(), size: CGSize(width: 200, height: 200))
nodeToMask.position = CGPoint(x: 0, y: 0)
nodeToMask.name = "character"
cropNode.addChild(nodeToMask)
//Now colorize the sprite which acts like background
let colorize = SKAction.sequence([
SKAction.colorizeWithColor(.orangeColor(), colorBlendFactor: 0, duration: 1),
SKAction.colorizeWithColor(.purpleColor(), colorBlendFactor: 0, duration: 1)
])
nodeToMask.runAction(SKAction.repeatActionForever(colorize), withKey: "colorizing")
addChild(cropNode)
}
The result:

Related

UIView With Pointed Edges

I am trying to make a UIView with pointed edges like this. Did some searching around and found some questions with slanting just one edge like this one but can't find an answer with intersecting (points) edges like the one in the picture that dynamically sizes based on the UIView height.
I used Rob's answer to create something like this:
#IBDesignable
class PointedView: UIView
{
#IBInspectable
/// Percentage of the slant based on the width
var slopeFactor: CGFloat = 15
{
didSet
{
updatePath()
}
}
private let shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = 0
// with masks, the color of the shape layer doesn’t matter;
// it only uses the alpha channel; the color of the view is
// dictate by its background color
shapeLayer.fillColor = UIColor.white.cgColor
return shapeLayer
}()
override func layoutSubviews()
{
super.layoutSubviews()
updatePath()
}
private func updatePath()
{
let path = UIBezierPath()
// Start from x = 0 but the mid point of y of the view
path.move(to: CGPoint(x: 0, y: bounds.midY))
// Calculate the slant based on the slopeFactor
let slantEndX = bounds.maxX * (slopeFactor / 100)
// Create the top slanting line
path.addLine(to: CGPoint(x: slantEndX, y: bounds.minY))
// Straight line from end of slant to the end of the view
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
// Straight line to come down to the bottom, perpendicular to view
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
// Go back to the slant end position but from the bottom
path.addLine(to: CGPoint(x: slantEndX, y: bounds.maxY))
// Close path back to where you started
path.close()
shapeLayer.path = path.cgPath
layer.mask = shapeLayer
}
}
The end result should give you a view close to what you which can be modified on the storyboard
And can also be created using code, here is a frame example since the storyboard showed its compatibility with autolayout
private func createPointedView()
{
let pointedView = PointedView(frame: CGRect(x: 0,
y: 0,
width: 200,
height: 60))
pointedView.backgroundColor = .red
pointedView.slopeFactor = 50
pointedView.center = view.center
view.addSubview(pointedView)
}

How to fill out an SKLabelNode with a specific color through animation?

I've been searching the internet for a while now and I couldn't come up with any solution.
I've created a SKLabelNode in my SpriteKit project which displays level numbers.
SKLabelNode:
let levelLbl = SKLabelNode(fontNamed: "LEMON-Bold")
levelLbl.text = "1"
levelLbl.fontSize = 100
levelLbl.fontColor = .grey
levelLbl.zPosition = 2
levelLbl.horizontalAlignmentMode = .center
levelLbl.verticalAlignmentMode = .center
levelLbl.position = CGPoint(x: frame.midX, y: frame.height * 0.75)
addChild(levelLbl)
Screenshot:
Screenshot of my label
I want to fill up the number of my label smoothly with white color from the bottom to the top with an animation so that after x seconds the label is completely white.
I don't want the color to fade in but rather to be filled up similar to a cup of water.
It would be amazing if someone could help me out here!
This is how far I came by using a SKCropNode. My problem is I don't know why I don't see an animation:
func labelAnimation() {
let levelLbl = SKLabelNode(fontNamed: "LEMON-Bold")
let blackRect = SKShapeNode(rectOf: CGSize(width: levelLbl.frame.width, height: levelLbl.frame.height))
blackRect.fillColor = .grey
blackRect.strokeColor = .grey
blackRect.position = CGPoint(x: 0, y: levelLbl.frame.height / 2)
let whiteRect = SKShapeNode(rectOf: CGSize(width: levelLbl.frame.width, height: levelLbl.frame.height))
whiteRect.fillColor = .white
whiteRect.strokeColor = .white
whiteRect.position = CGPoint(x: 0, y: -levelLbl.frame.height / 2)
//Level Node
levelNode.position = CGPoint(x: 0, y: 0)
levelNode.addChild(blackRect)
levelNode.addChild(whiteRect)
//Mask Node
levelLbl.text = "\(levelNr)"
levelLbl.fontSize = 100
levelLbl.horizontalAlignmentMode = .center
levelLbl.verticalAlignmentMode = .center
//Crop Node
cropNode.addChild(levelNode)
cropNode.maskNode = levelLbl
cropNode.position = CGPoint(x: frame.midX, y: frame.midY)
cropNode.zPosition = 5
addChild(cropNode)
//action
let movelevelNode = SKAction.move(to: CGPoint(x: 0, y: frame.height / 3), duration: 8)
levelNode.run(movelevelNode)
}

Setting a repeated background in an SKScene

I am building an iOS game using the SpriteKit library. I have a background of stars. However, I don't want to upload a static image, because it might scale incorrectly depending on the device size. So I got a png that I repeat over to fill up the screen. I tried doing this:
self.backgroundColor = UIColor(patternImage: UIImage(named: "background.png")!);
However, it only sets the background to a black color. Does anyone have any suggestions on how to do this?
I think this will help you, Firstly set the background using the below code:
for index in 0..<2 {
let bg = SKSpriteNode(imageNamed: "Your image name")
bg.position = CGPoint(x: index * Int(bg.size.width), y: 0)
bg.anchorPoint = CGPoint(x: 0, y: 0)
bg.name = "background"
self.addChild(bg)
}
and then use this code to move the background:
self.enumerateChildNodes(withName: "background", using: {(node, stop) -> Void in
if let bg = node as? SKSpriteNode {
bg.position = CGPoint(x: bg.position.x - 3.0, y: bg.position.y)
if bg.position.x <= -bg.size.width {
bg.position = CGPoint(x: bg.position.x + bg.size.width * 2, y: bg.position.y)
}
}
})

How to change color of SKShapeNode randomly each time spawned?

Currently, when running this code, a colored ball runs from the top of the screen to the bottom. When it reaches the bottom it respawns at the top and drops again. I need the ball to change to a random color each time it spawns again. I have a colorChange action but it still doesn't change the color of the ball when it runs.
import SpriteKit
class GameScene: SKScene {
let ball = SKShapeNode(circleOfRadius: 20)
let label = SKLabelNode(fontNamed: "Futura")
let colorChange = SKAction.colorizeWithColor(.blueColor(), colorBlendFactor: 1, duration: 1)
let randomRed = Int(arc4random_uniform(255))
let randomGreen = Int(arc4random_uniform(255))
let randomBlue = Int(arc4random_uniform(255))
override func didMoveToView(view: SKView) {
let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = sceneBody
//Ball Transition
let ballTransition = SKAction.sequence([SKAction.fadeInWithDuration(1)])
ball.runAction(ballTransition)
ball.fillColor = UIColor(red: 100, green: 100, blue: 0, alpha: 1)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 25)
ball.physicsBody?.affectedByGravity = false
ball.position = CGPoint(x: self.frame.size.width/2, y: CGFloat(self.frame.size.height*1))
ballMovement()
self.addChild(ball)
self.addChild(label)
}
func ballMovement() {
let moveBall = SKAction.moveToY(0, duration: 3)
let goBackUp = SKAction.moveToY(self.frame.size.height, duration:0)
let keepFalling = SKAction.sequence([moveBall, goBackUp, colorChange])
ball.runAction(SKAction.repeatActionForever(keepFalling))
//Label Sprite
label.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
label.fontColor = SKColor.redColor()
label.fontSize = 30
}
override func update(currentTime: CFTimeInterval) {
label.text = "\(ball.position.y)"
if ball.position.y < 26 {
ball.position = CGPoint(x: self.frame.size.width/2, y: CGFloat(self.frame.size.height*1))
}
}
}
Instead of using an SKAction to change the color, just do
ball.color = UIColor...
To generate a random color check this out: How to make a random background color with Swift
and use that to generate a random color
ball.color = .randomColor()
I suggest you don't use an SKShape for an important piece that moves because I find that they can slow down the app and cause problems. I would use an SKSpritenode instead for the ball. If you then make the ball default as all white, you can change the colour by doing this:
ball.colorBlendFactor = 1
ball.color = SKColor(hue: randomNumberFunctionBetweenZeroAndOne(), saturation: 0.75, contrast: 0.5)
The hue should be a random value between 0 and 1.
If you put this inside a function, you could call the function to change the colour of the ball inside and SKAction or anywhere else.

Assign Multiple Masks to SKCropNode

As an example, I have a circle which travels to the left and then the right on the screen. I want the circle to only be visible if it is inside two specific squares (maskNodes). I'm using SKCropNode to try and achieve this, but the SKCropNode mask only lets me assign one mask. Does anybody know a way of assigning two or more mask to a SKCropNode, or if it is even possible to do so. Thanks!
override func didMoveToView(view: SKView) {
anchorPoint = CGPointMake(0.5, 0.5)
backgroundColor = UIColor.whiteColor()
let mask1 = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(50, 50))
mask1.position.x = -100
let mask2 = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(50, 50))
mask2.position.x = 100
let cropNode = SKCropNode()
cropNode.maskNode = mask1 // && mask2
addChild(cropNode)
let circle = SKShapeNode(circleOfRadius: 25)
circle.fillColor = UIColor.blackColor()
cropNode.addChild(circle)
// Move Circle
let moveLeft = SKAction.moveToX(-frame.size.width/2, duration: 2)
let moveRight = SKAction.moveToX(frame.size.width/2, duration: 2)
let seq = SKAction.repeatActionForever(SKAction.sequence([moveLeft, moveRight]))
circle.runAction(seq)
}
Figured it out, just has to add the two mask to a parent and then assign the parent as the SKCropNode mask.
override func didMoveToView(view: SKView) {
anchorPoint = CGPointMake(0.5, 0.5)
backgroundColor = UIColor.whiteColor()
let maskParent = SKSpriteNode()
let mask1 = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(50, 50))
mask1.position.x = -100
maskParent.addChild(mask1)
let mask2 = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(50, 50))
mask2.position.x = 100
maskParent.addChild(mask2)
let cropNode = SKCropNode()
cropNode.maskNode = maskParent
addChild(cropNode)
let circle = SKShapeNode(circleOfRadius: 25)
circle.fillColor = UIColor.blackColor()
cropNode.addChild(circle)
// Move Circle
let moveLeft = SKAction.moveToX(-frame.size.width/2, duration: 2)
let moveRight = SKAction.moveToX(frame.size.width/2, duration: 2)
let seq = SKAction.repeatActionForever(SKAction.sequence([moveLeft, moveRight]))
circle.runAction(seq)
}