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.
Related
Is there an easy method to slice an SKShapeNode into 2 seperate SKShapeNodes?
In this example I have created an SKShapeNode using a closed bezier path as well as a single line.
let squareShape = SKShapeNode()
squareShape.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 256, height: 256), cornerRadius: 10).cgPath
squareShape.position = CGPoint(x: frame.midX-128, y: frame.midY+200-128)
squareShape.fillColor = UIColor.green
squareShape.strokeColor = UIColor.blue
squareShape.lineWidth = 2
addChild(squareShape)
let lineShape1 = SKShapeNode()
let line1 = UIBezierPath()
line1.move(to: CGPoint(x: -200, y: 300))
line1.addLine(to: CGPoint(x: 200, y: 100))
lineShape1.path = line1.cgPath
lineShape1.strokeColor = UIColor.white
lineShape1.lineWidth = 2
addChild(lineShape1)
How can I transform the existing SKShapeNode into 2 seperate SKShapeNodes separated by the line?
I need to be able to do this with varying line positions and shapes types (or even with multiple shapes and a single line).
Thanks
I need to add simple line using ShapeBody to interact with.
I'am trying code below, by Xcode gives me next response:
PhysicsBody: Could not create physics body.
let testPath = UIBezierPath()
testPath.move(to: CGPoint(x:-100, y: 200))
testPath.addLine(to: CGPoint(x:100, y: 200))
let testShape = SKShapeNode()
testShape.path = testPath.cgPath
testShape.position = CGPoint(x:0, y:250)
testShape.zPosition = 5
testShape.lineWidth = 5
testShape.strokeColor = .red
testShape.physicsBody = SKPhysicsBody(polygonFrom: testPath.cgPath)
Used edgeChainFrom instead of polygonFrom, and it works!
testShape.physicsBody = SKPhysicsBody(edgeChainFrom: testPath.cgPath)
A path cannot intersect any of its lines with a SKPhysicsBody, your line needs to be a thin rectangle
let testPath = UIBezierPath()
testPath.move(to: CGPoint(x:-100, y: 200))
testPath.addLine(to: CGPoint(x:100, y: 200))
testPath.addLine(to: CGPoint(x:100, y: 201))
testPath.addLine(to: CGPoint(x:-100, y: 201))
testPath.close()
let testShape = SKShapeNode()
testShape.path = testPath.cgPath
testShape.position = CGPoint(x:0, y:250)
testShape.zPosition = 5
testShape.lineWidth = 5
testShape.strokeColor = .red
testShape.physicsBody = SKPhysicsBody(polygonFrom: testPath.cgPath)
I added a subview (with a black border) in a view and centered it.
Then I generate 2 identical triangles with CAShapeLayer and add one to the subview and the other to the main view.
Here is the visual result in Playground where we can see that the green triangle is totally off and should have been centered.
And here is the code:
let view = UIView()
let borderedView = UIView()
var containedFrame = CGRect(x: 0, y: 0, width: 100, height: 100)
func setupUI() {
view.frame = CGRect(x: 0, y: 0, width: 300, height: 600)
view.backgroundColor = .white
borderedView.frame = containedFrame
borderedView.center = view.center
borderedView.backgroundColor = .clear
borderedView.layer.borderColor = UIColor.black.cgColor
borderedView.layer.borderWidth = 1
view.addSubview(borderedView)
setupTriangles()
}
private func setupTriangles() {
view.layer.addSublayer(createTriangle(color: .red)) // RED triangle
borderedView.layer.addSublayer(createTriangle(color: .green)) // GREEN triangle
}
private func createTriangle(color: UIColor) -> CAShapeLayer {
let layer = CAShapeLayer()
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: -containedFrame.width, y: 0))
bezierPath.addLine(to: CGPoint(x: 0, y: -containedFrame.height))
bezierPath.close()
layer.position = borderedView.center
layer.path = bezierPath.cgPath
layer.fillColor = color.cgColor
return layer
}
Note: All position (of view, the borderedView and both triangles) are the same (150.0, 300.0)
Question: Why is the green layer not in the right position?
#DuncanC is right that each view has its own coordinate system. Your problem is this line:
layer.position = borderedView.center
That sets the layer's position to the center of the frame for the borderedView which is in the coordinate system of view. When you create the green triangle, it needs to use the coordinate system of borderedView.
You can fix this by passing the view to your createTriangle function, and then use the center of the bounds of that view as the layer position:
private func setupTriangles() {
view.layer.addSublayer(createTriangle(color: .red, for: view)) // RED triangle
borderedView.layer.addSublayer(createTriangle(color: .green, for: borderedView)) // GREEN triangle
}
private func createTriangle(color: UIColor, for view: UIView) -> CAShapeLayer {
let layer = CAShapeLayer()
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: -containedFrame.width, y: 0))
bezierPath.addLine(to: CGPoint(x: 0, y: -containedFrame.height))
bezierPath.close()
layer.position = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
layer.path = bezierPath.cgPath
layer.fillColor = color.cgColor
return layer
}
Note: When you do this, the green triangle appears directly below the red one, so it isn't visible.
Every view/layer uses the coordinate system of it's superview/superlayer. If you add a layer to self.view.layer, it will be positioned in self.view.layer's coordinate system. If you add a layer to borderedView.layer, it will be in borderedView.layer's coordinate system.
Think of the view/layer hierarchy as stacks of pieces of graph paper. You place a new piece of paper on the current piece (the superview/layer) in the current piece's coordinates system, but then if you draw on the new view/layer, or add new views/layer inside that one, you use the new view/layer's coordinate system.
I am making a game where there are two balls on the scene, but the user is going to tap on the correct ball. However, when user taps on the correct ball, I want to set a random position for my second ball that when the user taps on the correct ball, I want to also want the second ball to move randomly.
Here is my code for setting up the random positions of the ball:
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
let shape = SKShapeNode()
currentBall.fillColor = pickColor()
//Rectangle
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160,height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
self.addChild(shape)
func randomBallPosition() -> CGPoint {
let xPosition = view!.scene!.frame.midX - viewMidX + CGFloat(arc4random_uniform(UInt32(viewMidX*2)))
let yPosition = view!.scene!.frame.midY - viewMidY + CGFloat(arc4random_uniform(UInt32(viewMidY*2)))
return CGPoint(x: xPosition, y: yPosition)
}
func handleTap() {
currentBall.position = randomBallPosition()
shape.position = randomBallPosition()
}
The issue is that you're setting the exact same position, which you have used for the first ball. As far as I understand both ball should appear at random independent positions. You should create a function to return a new position and call it for each ball:
class MyScene: SKScene {
var currentBall: SKShapeNode!
var shape: SKShapeNode!
override func didMove(to view: SKView) {
super.didMove(to: view)
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
currentBall.fillColor = pickColor()
let shape = SKShapeNode()
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160,height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
self.addChild(shape)
}
func handleTap() {
//your code here, except that you don't create new nodes, just modify existing ones
//at some point you will change balls' positions like this:
currentBall.position = randomBallPosition()
secondBall.position = randomBallPosition()
}
func randomBallPosition() -> CGPoint {
let xPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxX)! + 1)))
let yPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxY)! + 1)))
return CGPoint(x: xPosition, y: yPosition)
}
}
Hope this helps
I'm trying to add an ovalPath to my view as per below.
let ovalPath = UIBezierPath(ovalInRect: CGRect(x: self.view.bounds.width*0.5 - 280.5*0.5, y: 276.5, width: 280.5, height: 214.5))
let ovalShapeLayer = CAShapeLayer()
ovalShapeLayer.fillColor = UIColor.clearColor().CGColor
ovalShapeLayer.strokeColor = UIColor.lightGrayColor().CGColor
ovalShapeLayer.lineWidth = 1.5
ovalShapeLayer.path = ovalPath.CGPath
self.view.layer.insertSublayer(ovalShapeLayer, atIndex: 1)
When I rotate the device the path doesn't update its position.
If I put this on viewDidLayoutSubviews the it gets called twice.
Thanks.