Add SKShapeNode in another one - swift

I want to draw a square in another square; but I can't position the second one in the right way.
What can I do for positioning based on first square coordinates?
var tileBackground = SKShapeNode(rectOfSize: CGSize(width: screenWidth, height: screenWidth))
tileBackground.name = "tileBackground"
tileBackground.fillColor = SKColor.whiteColor()
tileBackground.position = CGPoint(x: frame.midX, y: frame.midY);
self.addChild(tileBackground)
var tile = SKShapeNode(rectOfSize: CGSize(width: tileSize, height: tileSize))
tile.name = "tile1"
tile.fillColor = SKColor.redColor()
tile.position = CGPointMake(100,100)
tileBackground.addChild(tile)

You should use SKSpriteNode instead. That way you can add it easily and without setting the position manually. Because in an SKSpriteNode, the center is always the center of the node:
var tileBackground = SKSpriteNode(color: UIColor.whiteColor(), size: CGSizeMake(width: screenWidth, height: screenWidth))
tileBackground.name = "tileBackground"
tileBackground.position = CGPoint(x: frame.midX, y: frame.midY);
self.addChild(tileBackground)
var tile = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: tileSize, height: tileSize))
tile.name = "tile1"
tileBackground.addChild(tile)

A node's position is relative to its parent position.
If you want the tile node to be in the center of its parent then set its position as:
tile.position = CGPoint(x: tileBackground.size.width / 2.0, y: tileBackground.size.height / 2.0)
which will place the tile in center of the tileBackground node.
also note that you do not need to update the tile position to "follow" the tileBackground node. Updating the tileBackground position will automatically keep the tile in its center

Related

Why will the opacity not change on this image I turned into a sprite node?

I'm making a space runner star ship game that shoots meteors. You have 4 lives and I want this image to hover over my ship and fade in opacity the fewer lives you have. I've tried the .alpha property but the image stays the same. Mask is the name of the sprite node.
func setupGame(_ size: CGSize) {
let ship = SKSpriteNode(imageNamed: "Spaceship.png")
ship.position = CGPoint (x: size.width/2.0, y: size.height/2.0)
ship.size = CGSize(width: 40.0, height: 40.0)
ship.name = SpaceshipNodeName
addChild(ship)
let mask = SKSpriteNode(imageNamed: shield)
mask.position = CGPoint (x: ship.size.width/6, y: ship.size.height/6)
mask.size = CGSize(width: 40.0, height: 40.0)
mask.alpha = 0.5
ship.addChild(mask)

Rounded rectangle with SKShapeNode

I have the following code that rounds an already existing rectangle from the scene builder...
btOk.path = UIBezierPath(roundedRect: CGRect(x: -62.5,y:-25,width: 125, height: 50), cornerRadius: 10).cgPath
However, if i try to use the same init to round the corners of another rectangle that is much larger, it does not even come close to working. It just makes the width HUUUUGE (imagine Trump).
scene.enumerateChildNodes(withName: "//*"){
node,stop in
if let name = node.name{
if name.contains("round"){
if let shapeNode = node as? SKShapeNode{
print(shapeNode.frame.width) //500
shapeNode.path = UIBezierPath(roundedRect: CGRect(x: -250,y:-100,width: 500, height: 200), cornerRadius: 50).cgPath
print(shapeNode.frame.width) //5735
}
}
}
}
btOk is a SKShapeNode as well. What am i missing between the two that is so different? One thing to note is i am enumerating through the children of the scene like this because this scene is in a SKReferenceNode. Perhaps that has something to do with it?
EDIT
Taking direction from #Ron Myschuk, i've solved this and since it's such a PITA, i also created an extension. So now i can round the corners of any SKShapeNode very easily when needed. Even if it was created in the scene editor. Note, this should only be used if there are no children of the shape node. Otherwise those children will be removed also.
extension SKShapeNode {
func roundCorners(topLeft:Bool,topRight:Bool,bottomLeft:Bool,bottomRight:Bool,radius: CGFloat,parent: SKNode){
let newNode = SKShapeNode(rect: self.frame)
newNode.fillColor = self.fillColor
newNode.lineWidth = self.lineWidth
newNode.position = self.position
newNode.name = self.name
self.removeFromParent()
parent.addChild(newNode)
var corners = UIRectCorner()
if topLeft { corners = corners.union(.bottomLeft) }
if topRight { corners = corners.union(.bottomRight) }
if bottomLeft { corners = corners.union(.topLeft) }
if bottomRight { corners = corners.union(.topRight) }
newNode.path = UIBezierPath(roundedRect: CGRect(x: -(newNode.frame.width / 2),y:-(newNode.frame.height / 2),width: newNode.frame.width, height: newNode.frame.height),byRoundingCorners: corners, cornerRadii: CGSize(width:radius,height:radius)).cgPath
}
}
And use it like so...
aShapeNode.roundCorners(topLeft: true, topRight: true, bottomLeft: false, bottomRight: false, radius: 5,parent: popup)
Not what you're going to want to hear but it's because you cannot set the width of an SKShapeNode in the Scene editor (To my knowledge). In order to get that ShapeNode to have a width of 500 you would have had to adjust the xScale. The xScale then reapplies itself to the path when you adjust it (kind of growing exponentially). If you create the SKShapeNode in code there is no problem adjust the rounded corners
let round = SKShapeNode(rect: CGRect(x: 0, y: -150, width: 500, height: 200))
round.fillColor = .red
addChild(round)
print(round.frame.width)
round.path = UIBezierPath(roundedRect: CGRect(x: -250, y: -100, width: 500, height: 200), cornerRadius: 50).cgPath
print(round.frame.width)
Edit
If you have your heart set on using the Scene editor you can place your ShapeNode and stretch it to where you want it then you could just do a small conversion in code to get the results that you want
if let round = self.childNode(withName: "biground") as? SKShapeNode {
let biground = SKShapeNode(rect: round.frame)
biground.fillColor = round.fillColor
biground.position = round.position
addChild(biground)
round.removeFromParent()
print(biground.frame.width)
biground.path = UIBezierPath(roundedRect: CGRect(x: -250, y: -100, width: 500, height: 200), cornerRadius: 50).cgPath
print(biground.frame.width)
}
this just recreates the shape in code based on what you outlined in the Scene editor and rounds the edges perfectly
edit 2
I've always been under the impression that SKShapeNodes are really inefficient (i'm pretty sure they leaked memory as well). So i always setup my round rectangles as so.
let outsideTexture = SKTexture(imageNamed: "round_square_white")
let outside = SKSpriteNode(texture: outsideTexture)
let insetX: CGFloat = 20
let insetY: CGFloat = 20
let cellSize = CGSize(width: 500.0, height: 500.0)
outside.centerRect = CGRect(x: CGFloat(insetX / outside.size.width), y: CGFloat(insetY / outside.size.height), width: CGFloat((outside.size.width - insetX * 2) / outside.size.width), height: CGFloat((outside.size.height - insetY * 2) / outside.size.height))
//outside.position = CGPointMake(gameModel.gameWidth / 2, (outside.size.height) / 2);
outside.xScale = cellSize.width / outside.size.width
outside.yScale = cellSize.height / outside.size.height
outside.zPosition = 10
outside.position = CGPoint(x: CGFloat(0), y: CGFloat(-0.35 * self.size.height / 2))
self.addChild(outside)
worth noting that this lays out a rounded square/rectangle perfectly however similar to the scale issues from the scene editor you have to place an empty cell over this to add children to, otherwise they scale to the rounded square.

What is the difference between these two statements in SpriteKit?

this part is a SKShapeNode
var rect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 30, height: 30))
self.path = CGPathCreateWithRect(rect, nil)
AND
let rect = SKShapeNode(rectOfSize: CGSize(width: 30, height: 30))
They seems to be acting differently in the simulator and I'm wondering if these are different in any way.
The origin, (0, 0) point, of a SKShapeNode is the center of the node and its path is drawn relative to that point. Therefore, a shape node created with the following CGRect
CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 30, height: 30))
will be drawn with its bottom-left corner in the center of the shape node.
The convenience method SKShapeNode(rectOfSize...) centers the rect in the shape node by offsetting the origin argument of the CGRect by half of the rectangle's width and height. In this case, the rect's origin is (-15, -15).
You can create a CGRect that is centered in an SKShapeNode by offsetting its origin:
let height = CGFloat(30)
let width = CGFloat(30)
let anchorPoint = CGPoint(x:0.5, y:0.5)
CGRect(origin: CGPoint(x: -width*anchorPoint.x, y: -height*anchorPoint.y), size: CGSize(width: width, height: height))
where anchorPoint defines the relative "point in the sprite that corresponds to the node’s position."
You can also use SKShapeNode(path:path, centered:true) which will center the path using its bounding box.

Position of a Node after addChild

I have an SKSpriteNode like a light gray square in the view, and I want to put a label inside it... I do this way:
let puntosCubo = SKSpriteNode(color: SKColor.lightGrayColor(), size: CGSize(width: gameoverTitle.frame.width, height: gameoverTitle.frame.height*4))
puntosCubo.position = CGPoint(x: CGRectGetMinX(self.frame)-100, y:y2)
I put a SKLabelNode inside puntosCubo this way:
let puntosCuboTitle1 = SKLabelNode(fontNamed: "Apple SD Gothic Neo")
puntosCuboTitle1.fontColor = SKColor.blackColor()
puntosCuboTitle1.fontSize = 20
puntosCuboTitle1.text = "Score"
puntosCubo.addChild(puntosCuboTitle1)
puntosCuboTitle1.position = CGPoint(x: 0, y: puntosCubo.position.y)
But the result is that the position of the SKLabelNode is not inside puntosCubo. I think i am using the position of puntosCubo on a wrong way...
Any ideas/help. Thanks.
Because of
puntosCubo.addChild(puntosCuboTitle1)
the position of the label puntosCuboTitle1 is relative to the postion of its parent (puntosCubo)
puntosCuboTitle1.position = CGPoint(x: 0, y: 0)
makes the position of the puntosCuboTitle1 in the middle of its parent puntosCubo

How to create a rectangle in Sprite kit using Swift

I'm trying to create a rectangle using swift on Sprite kit, since the rectangle needs to be used as an object in the scene, i assumed that i needed to create a SkSpriteNode, and them give it a size, but it did not worked, this is how i did it:
var barra = SKSpriteNode()
barra.name = "bar"
barra.size = CGSizeMake(300, 100)
barra.color = SKColor.whiteColor()
barra.position = CGPoint(x: 100, y: 100)
self.addChild(barra)
Adding barra to the screen does not change the node counting.
Any help will be appreciated!
You may want to use a SKShapeNode, like this:
var barra = SKShapeNode(rectOfSize: CGSize(width: 300, height: 100))
barra.name = "bar"
barra.fillColor = SKColor.whiteColor()
barra.position = location
self.addChild(barra)
Here's Apple's documentation on SKShapeNode.
The way I was creating the rectangle was not the best, but the problem was the position I was inserting it on the game. This is how I've fixed it:
var barra = SKSpriteNode(color: SKColor.blackColor(), size: CGSizeMake(200, 200))
barra.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
barra.zPosition = 9 // zPosition to change in which layer the barra appears.
self.addChild(barra)