How to rotate a sprite along a circular path? - swift

This is a wheel I have consisting of four individual sprites. My aim is to rotate them as a wheel. I have tried 'UIBezier' arccenter class but the end result is the individual sprite moving along the center but rotating its shape too, leading to an unwanted result. Here is the relavant code. the 'orientToPath' when set to true, rotates the sprite itself.
//path rotation
let dx = whlPurple.position.x - self.size.width/2
let dy = whlPurple.position.y - (whlRed.size.height/2 + (whlPurple.position.y - whlRed.size.height))
let rad = atan2(dy, dx)
let radius = sqrt((dx*dx) + (dy*dy))
let path = UIBezierPath(arcCenter: CGPoint(x: self.size.width/2, y: (whlRed.size.height/2 + (whlPurple.position.y - whlRed.size.height))), radius: radius, startAngle: rad, endAngle: rad + CGFloat(M_PI * 6), clockwise: true)
let follow = SKAction.follow(path.cgPath, asOffset: false, orientToPath: true, speed: 200)
whlPurple.run(SKAction.repeatForever(follow))

Create an SKNode and place it at the center of your circle.
Then add your sprite to it (addChild) at a position of CGPoint(x:radius,y:0). you can set the zRotation of the SKNode to chose your starting position on the circle.
When you rotate the SKNode, your sprite will move in a perfect circle. For example, to have the spites go around in 5 seconds, you can use SKAction.rotate(by: 2*pi, duration:5) and repeat that forever (on the SKNode containing the sprite (not on the sprite itself).

Related

How can I make an SKPhysicsBody for this image using CGPath?

I am trying to make an SKPhysics body for this SKSpriteNode using a CGPath polygon.
The problem is that when I check for a collision between this node and the player node, the didBeginContact method is executed even though they did not touch each other. I believe their is something wrong with the coordinates but I cannot see the polygon lines, which makes it difficult for me too see the accuracy of the lines.
Here is the code that I am using:
let triangle = SKSpriteNode(imageNamed: "Triangle_ZigZag")
let trianglePath = CGMutablePath()
trianglePath.addLines(between: [CGPoint(x: triangle.size.width,
y: triangle.size.height),
CGPoint(x: triangle.size.width,
y: - triangle.size.height),
CGPoint(x: -triangle.size.width,
y: triangle.size.height / 2)])
trianglePath.closeSubpath()
triangle.physicsBody = SKPhysicsBody(polygonFrom: trianglePath)
Can someone please help me figure out what am I doing wrong ?
Thank You
FYI physics lines are green, so a green sprite probably isn't the best choice you can't see the lines very well.
your sprite has a centre anchorPoint or an anchorPoint of (0, 0) by default. Therefore your physics points need to take that into account. top right corner would be half the width from centre and half the height from centre etc. You have full width from centre and full height from centre, that is the issue.
trianglePath.addLines(between: [CGPoint(x: triangle.size.width / 2, y: triangle.size.height / 2), CGPoint(x: triangle.size.width / 2, y: -triangle.size.height / 2), CGPoint(x: -triangle.size.width / 2, y: 0)])

Spawning sprites on screen edges while keeping anchor point in center

I'm developing a simple game where there's a player sprite in the middle of my screen and monster sprites spawn at random locations on the screen edge and slowly move toward the player sprite. Originally, this worked just fine by setting the GameScene anchor point to the lower left with self.anchorPoint = CGPoint(x: 0.0, y: 0.0), and the positioning the player in the center with player.position = CGPoint(x: size.width * 0.5, y: size.height * 0.5). However, because I changed the anchor point, when I rotate my iPad, the player ends up being off center.
I think it's easier to work with a (0,0) anchor point, because then I can spawn monsters randomly from the edges using lines like randomEdgePosition = CGPoint(x: 0.0, y: randomFloat(min: 0.0, max: 1.0) * size.height). But it's easier for my player's positioning to keep the anchor point centered. How can the screen edges be calculated with a central anchor point? Alternatively, how could I keep my player centered with a (0,0) anchor point?
Keep the anchor point in the center of your screen and spawn your monsters using this
randomEdgePosition = CGPoint(x: 0 - size.width / 2, y: randomFloat(min: 0.0, max: 1.0) * size.height)
The problem with randomizing on an edge is that you will see more enemies spawning in the corners than in the centers of the edges because there is more edge on the corners. To evenly distribute around the entire scene, you want to position based on a circle, and use the longest edge / 2 + the longest edge / 2 of the enemy (I assume you want to spawn off screen) as your radius
let theta = CGFloat(randomFloat(min: 0.0, max: 1.0) * Float.pi * 2)
let radius = max(size.width, size.height)/2 + max(enemy.size.width, enemy.size.height)/2
let randomPosition = CGPoint(x: cos(theta) * radius, y: sin(theta) * radius)
This will also solve your issue with the anchorPoint, and allows you to keep it at the center of the screen (0.5,0.5), making it easier to detect which way the enemies are coming from without having to do additional subtraction. (E.G. enemy at -x is left of the player, no need to check if x < player.x0

Starting position of a Sprite Node

I am using to following code to make a sprite node move in a circle in Xcode spritekit.
let circleDiameter = CGFloat(100)
// center our path based on our sprites initial position
let pathCenterPoint = CGPoint(
x: object.position.x - circleDiameter,
y: object.position.y - circleDiameter/2)
// create the path our sprite will travel along
let circlePath = CGPath(ellipseIn: CGRect(origin: pathCenterPoint, size: CGSize(width: circleDiameter, height: circleDiameter)), transform: nil)
// create a followPath action for our sprite
let followCirclePath = SKAction.follow(circlePath, asOffset: false, orientToPath: false, duration: 3)
// make our sprite run this action forever
object.run(SKAction.repeatForever(followCirclePath).reversed(), withKey: “moving”)
world.addChild(object)
The problem is, the sprite node starting position is always positioned on the RIGHT side of the circle path. I know I can use .reversed() to change direction of the sprite node but that is not what I am asking.
How can I change the "starting point" to the LEFT side of the circle path?
Thanks Guys! Please refer to the picture below :D
CGPath(ellipseIn:...) is an utility method which creates a path
of an ellipse, where the "argument" or "angle" runs from 0 to 2π.
You cannot add an "offset" to the follow action, but you can define
the path in a way that it starts at the "left end" of the circle:
let circlePath = CGMutablePath()
circlePath.addArc(center: pathCenterPoint, radius: circleDiameter,
startAngle: -.pi, endAngle: .pi, clockwise: false)

SKSpriteNode animation to appear bottom-up

According to the following code, i am displaying a mountain (hill) in the scene at x = 400 and y = self.frame.height / 2
However, I want the hill to appear from bottom-up (from below the scene) like an animation. How can I code this ?
let hill = SKSpriteNode(imageNamed: "hill")
hill.position = CGPoint(x: 400, y: self.frame.height / 2 )
hill.setScale(1)
hill.physicsBody = SKPhysicsBody(rectangleOfSize: hill.size)
hill.physicsBody?.categoryBitMask = 1
hill.physicsBody?.collisionBitMask = 2
self.addChild(hill)
To create an animation that makes the sprite slide up from "under" the scene, do this:
Set its initial position to below the scene:
hill.position = CGPoint(x: 400, y: self.frame.height / CGFloat(2) - self.frame.height)
Note: - self.frame.height is used to place the node 1 full frame height below the desired location
Then start an action that moves it up to its desired position in didMoveToView, or wherever you want the animation to start:
let moveUpAnimation = SKAction.move(to: CGPoint(x: 400, y: self.frame.height / CGFloat(2)), duration: 0.75)
hill.run(moveUpAnimation)
This example creates an action that takes 0.75 seconds to move the sprite up one full frame height. You can set its initial position to anywhere below the visible scene to create this effect, and change the duration to any desired speed.

Drawing circle outline with small gap Sprite Kit Swift

I am currently creating a game in SpriteKit. In this game, I am trying to draw an outline of a circle with a small gap(s) in the outline of this circle (possibly about 1/8 the entire circumference of the circle). Here is a drawn picture of what I am visualizing.
How would I do this using SpriteKit? Would I use SKShapeNode? Any help would be appreciated. Thank you.
You can get something similar using the SKShapeNode in combination with SKEffectNode, like this:
override func didMoveToView(view: SKView) {
let effectNode = SKEffectNode()
let gap = CGFloat(M_PI_4 / 4.0)
for i in 0...3 {
let shape = SKShapeNode(circleOfRadius: 50)
shape.fillColor = .clearColor()
shape.lineWidth = 6.8
shape.strokeColor = .darkGrayColor()
let startAngle:CGFloat = CGFloat(i) * CGFloat(M_PI_2) + gap
let endAngle:CGFloat = startAngle + CGFloat(M_PI_2) - gap * 2
print("Iteration \(i) : start angle (\(startAngle * 180 / CGFloat(M_PI)), end angle (\(endAngle * 180 / CGFloat(M_PI)))")
shape.path = UIBezierPath(arcCenter: CGPointZero, radius: -50, startAngle: startAngle, endAngle: endAngle, clockwise: true).CGPath
effectNode.addChild(shape)
}
effectNode.position = CGPoint(x: frame.midX, y: frame.midY)
effectNode.shouldRasterize = true
addChild(effectNode)
}
The result:
Or you could make a mask programatically and apply it to a SKCropNode. The SKCropNode of course will be a parent of a ring (SKShapeNode with fillColor set to .clearColor() and strokeColor set to appropriate value).