SKCropNode fails when I add extra SKNode children in hierarchy - ios

Update: It looks like iOS 10 has fixed this issue. I upgraded to Swift 3 and Xcode 8 and everything is working as expected.
I've run into this issue a couple times now and I can't tell if it's a bug in SKCropNode or if I'm just misusing it. Perhaps there's some bit of documentation I'm missing to explain why this is happening?
I have a crop node with a 100x100 rectangle shape as the mask. If I place a blue circle inside it, it gets cropped properly.
// Create a crope node with a small square.
let cropNode = SKCropNode()
let cropNodeMask = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 100, height: 100))
cropNodeMask.fillColor = UIColor.whiteColor()
cropNode.maskNode = cropNodeMask
self.addChild(cropNode)
// Create a blue circle and put it in the crop node.
let blueCircle = SKShapeNode(circleOfRadius: 110)
blueCircle.fillColor = UIColor.blueColor()
blueCircle.strokeColor = UIColor.clearColor()
cropNode.addChild(blueCircle)
Now, when I place that same circle inside of an otherwise empty SKNode and place that container inside the same crop node, cropping fails.
// Create a crope node with a small square.
let cropNode = SKCropNode()
let cropNodeMask = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 100, height: 100))
cropNodeMask.fillColor = UIColor.whiteColor()
cropNode.maskNode = cropNodeMask
self.addChild(cropNode)
// Create a container to hold the circle.
let container = SKNode()
cropNode.addChild(container)
// Create a blue circle and put it in the container.
let blueCircle = SKShapeNode(circleOfRadius: 110)
blueCircle.fillColor = UIColor.blueColor()
blueCircle.strokeColor = UIColor.clearColor()
container.addChild(blueCircle)
But a sprite in that same container seems to be cropped fine.
// Create a crope node with a small square.
let cropNode = SKCropNode()
let cropNodeMask = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 100, height: 100))
cropNodeMask.fillColor = UIColor.whiteColor()
cropNode.maskNode = cropNodeMask
self.addChild(cropNode)
// Create a container to hold the sprite.
let container = SKNode()
cropNode.addChild(container)
// Create a spaceship and add it to the container.
let spaceshipNode = SKSpriteNode(imageNamed: "Spaceship")
spaceshipNode.anchorPoint = CGPointZero
container.addChild(spaceshipNode)

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

SKSpriteNode.centerRect does not work with SKTextureAtlas

I'm adding SKSpriteNode to my scene object using the following code.
let atlas = SKTextureAtlas(named: "Ubiquity")
let node = SKSpriteNode(texture: atlas.textureNamed("roundedBorder"))
node.zPosition = 2000
node.centerRect = CGRect(x: 0.4, y: 0, width: 0.2, height: 1)
addChild(node)
After all, my texture looks distorted (please see attached image wrong image)
But, if I either remove node.centerRect setting or putting my image to xcasset directly the below way
let node = SKSpriteNode(texture: SKTexture(imageNamed: "ubiquity-roundedBorder"))
node.zPosition = 2000
node.centerRect = CGRect(x: 0.4, y: 0, width: 0.2, height: 1)
addChild(node)
everything works as expected (please see attached another image correct image)
I am seeing the same behaviour. This looks like a bug.

How do I constrain an SKShapeNode to the device edges in SpriteKit?

Just to test things out I have created a blue square and placed it at the center of the screen like this:
let mySquare = SKShapeNode(rectOf: CGSize(width: 50, height: 50))
mySquare.fillColor = SKColor.blue
mySquare.lineWidth = 1
let myPoint = CGPoint(x: UIScreen.main.nativeBounds.midX, y: UIScreen.main.nativeBounds.midY)
mySquare.position.x = 0
mySquare.position.y = 0
self.addChild(mySquare)
Works great. Now, I would like to use constraints and set up the square constraints to the edges of the device screen. I have tried this, but the blue square doesn't appear, so I think I have the wrong idea on how to capture the CGPoint of the screen edges.
let mySquare = SKShapeNode(rectOf: CGSize(width: 50, height: 50))
mySquare.fillColor = SKColor.blue
mySquare.lineWidth = 1
let myPoint = CGPoint(x: UIScreen.main.nativeBounds.maxX, y: UIScreen.main.nativeBounds.maxY)
let range = SKRange(lowerLimit: 10.0, upperLimit: 10.0)
let myConstraints = SKConstraint.distance(range, to: myPoint)
mySquare.constraints = [myConstraints]
self.addChild(mySquare)
How do I capture the screen edges and constrain the square to those?
SKConstraint doesn't work equally as UIKit Constraints.
SKConstraint functionality is really specific:
Please take a look here: https://developer.apple.com/documentation/spritekit/skconstraint
Anyway, can give you some recommendations:
Transform screen position to scene position:
self.view?.convert(myPoint, to: self)
You can start with this example and get node on a corner
let mySquare = SKShapeNode(rectOf: CGSize(width: 50, height: 50))
mySquare.fillColor = SKColor.blue
mySquare.lineWidth = 1
let myScreenPoint = CGPoint(x: UIScreen.main.bounds.maxX, y: UIScreen.main.bounds.maxY)
if let myScenePoint = self.view?.convert(myScreenPoint, to: self) {
mySquare.position = myScenePoint
}
self.addChild(mySquare)
With this logic, you can get each side of the screen and decrease or increase margin and make each 4 sides; or 1 constraint for the center.

Use an SKShapeNode for the mask of an SKCropNode (Swift)

How can I use an SKShapeNode for the mask of an SKCropNode? My code currently creates an SKShapeNode circle and then sets the crop node mask to it
shapeNode = SKShapeNode(circleOfRadius: 50)
shapeNode.fillColor = UIColor.red
shapeNode.lineWidth = 1
shapeNode.position = CGPoint(x: 0, y: 0)
shapeNode.zPosition = 4
cropNode = SKCropNode()
cropNode.maskNode = shapeNode
cropNode.addChild(Node)
But when I add the Node as a child of the cropNode it doesnt draw anything but a red circle (exactly as i set it to do)
Is there any way to use an SKShapeNode as the mask of an SKCropNode?
-Update-
I figured out how to make an SKSpriteNode drawn from the SKShapeNode
shapeNode = SKShapeNode(circleOfRadius: 60)
shapeNode.fillColor = UIColor.red
let shapeTexture = view.texture(from: shapeNode)
textureNode = SKSpriteNode(texture: shapeTexture)
textureNode.position = CGPoint(x: 0, y: 0)
textureNode.zPosition = 3
cropNode.maskNode = textureNode
cropNode.addChild(tileMap2)
self.addChild(cropNode)
But now the SKCropNode isnt working when applied to the SKSpriteNode how do I fix this?

Programmatically create constrained region of physics, SpriteKit

I would like two regions, as shown in the image below, where the yellow region is to contain sprites. For example I'd like to have balls in the yellow region bouncing and reflecting off the boundaries of the yellow region. How can I programmatically do this without using an sks file?
You create an edge based physics body using the +bodyWithEdgeLoopFromRect:
override func didMoveToView(view: SKView) {
//Setup scene's physics body (setup the walls)
physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
let yellowSprite = SKSpriteNode(color: .yellowColor(), size: CGSize(width: 300, height: 300))
yellowSprite.position = CGPoint(x: frame.midX, y: frame.midY)
//Create the rectangle which will represent physics body.
let rect = CGRect(origin: CGPoint(x: -yellowSprite.size.width/2, y: -yellowSprite.size.height/2), size: yellowSprite.size)
yellowSprite.physicsBody = SKPhysicsBody(edgeLoopFromRect: rect)
addChild(yellowSprite)
//Add Red ball "inside" the yellow sprite
let red = SKShapeNode(circleOfRadius: 20)
red.fillColor = .redColor()
red.strokeColor = .clearColor()
red.position = yellowSprite.position
red.physicsBody = SKPhysicsBody(circleOfRadius: 20)
red.physicsBody?.restitution = 1
red.physicsBody?.friction = 0
red.physicsBody?.affectedByGravity = false
addChild(red)
red.physicsBody?.applyImpulse(CGVector(dx: 20, dy: 15))
}
About rect parameter:
The rectangle that defines the edges. The rectangle is specified
relative to the owning node’s origin.
Hope this helps!

How to create a node in another node

I have a circle, an SKShapeNode, and I want to create a child node inside it. How I can do this?
func AddCircle() {
Circle = SKShapeNode(circleOfRadius: circleRadius)
Circle.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
Circle.strokeColor = UIColor.whiteColor()
self.addChild(Circle)
Circle.addChild(BooCharacter)
}
Main Character:
func AddCharacter() {
BooCharacter.size = CGSize(width: 30, height: 30)
BooCharacter.anchorPoint.y = 0
BooCharacter.zRotation = CGFloat(-M_PI_2)
BooCharacter.position.y += circleRadius
}
I want to create an object in my circle but I don't know how you write it.
For example, I've tried to add a rect inside of my circle:
func AddRect() {
Rect = SKShapeNode()
Rect.path = UIBezierPath(roundedRect: CGRect(x:0, y: 250, width: 256, height: 256), cornerRadius: 64).CGPath
Rect.fillColor = UIColor.whiteColor()
}
func AddCircle() {
Circle = SKShapeNode(circleOfRadius: circleRadius)
Circle.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
Circle.strokeColor = UIColor.whiteColor()
self.addChild(Circle)
Circle.addChild(BooCharacter)
Circle.addChild(Rect)
but it's still not working.
I want to create and add triangles inside of my circle like this
You're doing exactly the right thing, although I don't see where BooCharacter is being created – are you definitely creating that somewhere? Using addChild() to add a node to your scene, or to add one node to another node, is correct, and should work for all types of SKNode.

Resources