Draw a hole in a rectangle with SpriteKit? - swift

How to draw a hole in a rectangle?
I want to draw a rectangle which has a empty hole inside it and the background can be show.
I am using SKShapeNode to create a rectangle, but I have no idea how to make a hole (circle) inside it.
This is what I run your code, but my circle is not empty, the circle is black, I want it to be empty.
Is there any mistake I didn't mention?

Here's the code.
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let effect = SKEffectNode()
addChild(effect)
let rect = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 400, height: 200))
rect.fillColor = .green
effect.addChild(rect)
let hole = SKShapeNode(circleOfRadius: 40)
hole.position = CGPoint(x: 200, y: 100)
hole.fillColor = .white
hole.blendMode = .subtract
rect.addChild(hole)
}
}
As you can see I create an SKEffectNode. Then I add to it the rectangle. Finally I add the hole to the rectangle.

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)
}

SKCropNode doesn't display anything

I can't get SKCropNode to cooperate. I've played with this for HOURS I've copied verbatim hackingwithswifts guide and the only difference being my image and it will not work. So then I dumbed it down for myself with the code below and it still doesn't work.
I've tried the guide directly from hacking with swift and it didn't work. I copied code directly from apple documentation that also didn't work. I've wasted wayyyy too much time on the simple task of cropping an image.
import SpriteKit
import GameplayKit
import UIKit
class ProcGenPlanet: SKScene{
//Background
//let background = SKSpriteNode()
var square = SKSpriteNode()
override func didMove(to view: SKView) {
square = SKSpriteNode(imageNamed: "redSquare")
square.size = CGSize(width: 1000, height: 1000)
square.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
let mask = SKSpriteNode(color: SKColor.black, size: CGSize(width: 100, height: 100))
let cropNode = SKCropNode()
cropNode.maskNode = mask
cropNode.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
cropNode.addChild(square)
}
}
I am creating 1000x1000 red square. The whole image is red. Then I am trying to crop it to 100x100 with the following code but instead NOTHING shows up.

Why is my SKShapeNode(s) reversed?

I'm training my SpritKit skills, but I can't figure out why my SKShapeNode are "reversed", I think I'm missing something.
I was trying example from this question: Question
So, I tried the example with 2 rounded corners, and another example which is a half-circle.
I don't understand why the two corners curved are not the one specified on the UIBezierPath object.
class GameScene: SKScene {
override func didMove(to view: SKView) {
let rectangle = CGRect(x: 0, y: 0, width: 400, height: 400)
let path = UIBezierPath(roundedRect: rectangle, byRoundingCorners: [.topLeft, .bottomRight], cornerRadii: CGSize(width: 50, height: 50))
let shape = SKShapeNode(path: path.cgPath, centered: true)
shape.lineWidth = 3
addChild(shape)
}
}
And why, my half circle are not oriented in the good direction either.
While in the documentation - Apple documentation - It says:
For example, specifying a start angle of 0 radians, an end angle of π radians, and setting the clockwise parameter to true draws the bottom half of the circle.
class GameScene: SKScene {
override func didMove(to view: SKView) {
let path = UIBezierPath(arcCenter: CGPoint(x: 0, y: 0), radius: 200, startAngle: 0, endAngle: CGFloat(M_PI), clockwise: true)
let shape = SKShapeNode(path: path.cgPath, centered: true)
shape.lineWidth = 3
addChild(shape)
}
}
Finally, can some one explain me how the array of 'corners' is considered as a mask?

Best way to create a drawing app which can use images as fill

I'm trying to create a section of an app where the user can draw freestyle shapes and fill these shapes with an image.
I have tried using UIBezierPaths and CAShapeLayers, and have been able to create a drawing app which fills all images with a single colour.
I want each shape to be able to be filled with a different colour, so eventually the individual shapes can be filled with separate images.
I'm having trouble separating each drawing so they can be filled individually, and have so far been unable to fill a drawing with an image.
If anyone has suggestions of other ways I could go about doing this, I would appreciate any ideas!
Have you thought about maybe using SpriteKit for the drawing portion of your app? Using Spritekit you can create your shapes using SKShapeNode and fill them with different colors using the fillColor property of SKShapeNode. For filling the newly create shape with an image you can place your image in an SKSpriteNode then crop it using SKCropNode and set the maskNode property of SKCropNode to the SKShapeNode you created.
Eample:
import UIKit
import SpriteKit
class DrawView: SKScene {
override func didMoveToView(view: SKView) {
scaleMode = .ResizeFill
backgroundColor = UIColor.whiteColor()
//creating a triangle and filling it with the color blue
let path = CGPathCreateMutable()
CGPathAddArc(path, nil, 160, 284, 0, 0, CGFloat(M_PI*2), true)
CGPathAddLineToPoint(path, nill, 60, 184)
CGPathAddLineToPoint(path, nill, 260, 184)
CGPathAddLineToPoint(path, nill, 60, 284)
let triangle = SKShapeNode()
triangle.lineCap = .Round
triangle.lineJoin = .Round
triangle.fillColor = UIColor.blueColor()
triangle.path = path
addChild(triangle)
//placing image into SKSpriteNode
let image = SKSpriteNode(imageNamed: "yourImage")
image.size = CGSizeMake(320, 568)
image.position = CGPointMake(160, 284)
//creating an SKCropNode
let imageCrop = SKCropNode()
imageCrop.position = CGPointMake(0, 0)
imageCrop.addChild(image) //adding image to the SKCropNode
imageCrop.maskNode = triangle //using the triangle created as a mask
addChild(imageCrop)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
//create spritekit scene
let skView = SKView(frame: CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height))
skView.center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2)
self.view.addSubview(skView)
//add drawing view to the scene
let drawView = DrawView()
sKView.presentScene(drawView)
}
}
The tricky part is going to be repositioning the SKSpriteNode and SKCropNode to fill the SKShapeNode.

SKSpriteNode image not displaying

When the view is presented, I would like the image named "Player" (set) which is in my images.xcassets to show on the scene.
Currently the scene just loads up blue. Not sure why, as even when adding a color to change the image color did nothing.
import SpriteKit
class CharacterScene: SKScene {
var Circle = SKSpriteNode(imageNamed: "Player")
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor()
Circle.size = CGSize(width: 40, height: 40)
Circle.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
Circle.color = SKColor.brownColor()
self.addChild(Circle)
}
}
You need to initialize the size of CharacterScene before presenting it on the screen. Here you go:
self.scene!.view!.presentScene(CharacterScene(size: self.scene!.size), transition: SKTransition.pushWithDirection(SKTransitionDirection.Up, duration: 0.3))