Sprite-kit: Moving an element in circular path
+ follow:duration: produce the same effect as follow:asOffset:orientToPath:duration: with both, asOffset
and orientToPath
parameters set to true
.
About asOffset parameter from the docs:
If true, the points in the path are relative offsets to the node’s
starting position. If false, the points in the node are absolute
coordinate values.
So, you should explicitly set it to false:
let circularMove = SKAction.follow(circle.CGPath, asOffset: false, orientToPath: true, duration: 5)
Sprite-kit: How to position sprites on a circular path
Like I said, you need to know where is the center of the path (in your case that is CGPoint(x: frame.midX, y:frame.midY)
which is "center" of the screen) and you have to know the radius (you have it already calculated when you was creating the path) and you need an angle that the ray from center (frame.midX,frame.midY)
to the point on the circumference (coinX,coinY)
makes with positive x axis:
import SpriteKit
class GameScene: SKScene {
let player = SKSpriteNode(color: .purpleColor(), size: CGSize(width: 30, height: 30))
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// Adding the big circle
let runway = SKSpriteNode(color: .orangeColor(), size: CGSize(width: 150, height: 150))
runway.position = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame))
addChild(runway)
// Adding the player
player.position = CGPointMake( CGRectGetMidX(frame) , (CGRectGetMidY(frame) + runway.size.width/2) )
addChild(player)
// Calculating the initial position of the player and creating a circular path around it
let dx = player.position.x - frame.width / 2
let dy = player.position.y - frame.height / 2
let radius = (runway.frame.size.width / 2) - 20.0
let radian = atan2(dy, dx)
let playerPath = UIBezierPath(
arcCenter: CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)),
radius: radius,
startAngle: radian,
endAngle: radian + CGFloat(M_PI * 4.0),
clockwise: true)
let follow = SKAction.followPath(playerPath.CGPath, asOffset: false, orientToPath: true, speed: 200)
player.runAction(SKAction.repeatActionForever(follow))
let numberOfCoins = 8
for i in 0...numberOfCoins {
let coin = SKSpriteNode(color: .yellowColor(), size: CGSize(width: 10, height: 10))
let angle = 2 * M_PI / Double(numberOfCoins) * Double(i)
let coinX = radius * cos(CGFloat(angle))
let coinY = radius * sin(CGFloat(angle))
coin.position = CGPoint(x:coinX + frame.midX, y:coinY + frame.midY)
addChild(coin)
}
}
}
Just a sidenote : In Sprite-Kit the angle of 0 radians specifies the positive x axis. And the positive angle is in the counterclockwise direction. The source - Building Your Scene.
How to rotate a sprite along a circular path?
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).
How to make a sprite that follow a circular path while its size changes?
I think you could make some corrections that can help you for the rest of the project.
This below works well with Swift 2.x, don't try it with Swift 3.x:
First of all I want to introduce this extension that can be useful :
extension SKNode
{
func runAction( action: SKAction!, withKey: String!, optionalCompletion: dispatch_block_t? )
{
if let completion = optionalCompletion
{
let completionAction = SKAction.runBlock( completion )
let compositeAction = SKAction.sequence([ action, completionAction ])
runAction( compositeAction, withKey: withKey )
}
else
{
runAction( action, withKey: withKey )
}
}
func actionForKeyIsRunning(key: String) -> Bool {
if self.actionForKey(key) != nil {
return true
} else {
return false
}
}
}
Usage:
self.runAction(myAction,withKey: "myActionName",optionalCompletion: {
// do whatever you want when action is finished..
})
if self.actionForKeyIsRunning("myActionName") {
// this action is up
}
Where to place this extension:
You can place this extension also after the last brace of your class, like this example:
class MyClass: SKScene {
...
} // end of MyClass
extension SKNode
{
...
}
This line could become:
circuloPrincipal.runAction(SKAction.repeatActionForever(follow),withKey: "followTrackForever"))
and the scaleTo
lines could be:
if !circuloFondo.actionForKeyIsRunning("scaleTo") {
let scale = SKAction.scaleTo(0.5, duration: 5)
circuloFondo.runAction(scale,withKey:"scaleTo",optionalCompletion: {
// scaleTo is terminated
// stop followTrackPath
self.circuloPrincipal.removeActionForKey("followTrackForever")
// re-run in the new path
let follow = SKAction.followPath(playerPath.CGPath, asOffset: false, orientToPath: true, speed: 200)
self.circuloPrincipal.runAction(SKAction.repeatActionForever(follow),withKey: "followTrackForever"))
})
}
Moving a SKSpriteNode in a downward loop, using Swift
I've put together some sample code which you can adapt for your purposes. I've based the code off the equation for an Archimedean Spiral:
r = a + bθ
Where a
is the starting radius; b
is the radius the spiral will increase by per revolution and θ
is the current angle.
A spiral is basically a glorified circle (IMO), so to move your node in a spiral you need to be able to calculate point on a circle using an angle, radius and center point:
func pointOnCircle(#angle: CGFloat, #radius: CGFloat, #center: CGPoint) -> CGPoint {
return CGPoint(x: center.x + radius * cos(angle),
y: center.y + radius * sin(angle))
}
Next, extend SKAction
so you can easily create a spiral action:
extension SKAction {
static func spiral(#startRadius: CGFloat, endRadius: CGFloat, angle
totalAngle: CGFloat, centerPoint: CGPoint, duration: NSTimeInterval) -> SKAction {
// The distance the node will travel away from/towards the
// center point, per revolution.
let radiusPerRevolution = (endRadius - startRadius) / totalAngle
let action = SKAction.customActionWithDuration(duration) { node, time in
// The current angle the node is at.
let θ = totalAngle * time / CGFloat(duration)
// The equation, r = a + bθ
let radius = startRadius + radiusPerRevolution * θ
node.position = pointOnCircle(angle: θ, radius: radius, center: centerPoint)
}
return action
}
}
Finally, an example of use. In didMoveToView
:
let node = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 10, height: 10))
node.position = CGPoint(x: size.width / 2, y: size.height / 2)
addChild(node)
let spiral = SKAction.spiral(startRadius: size.width / 2,
endRadius: 0,
angle: CGFloat(M_PI) * 2,
centerPoint: node.position,
duration: 5.0)
node.runAction(spiral)
Moving SpriteNodes outwards in a circular pattern
if the bullets are moving faster from the bottom left then the top right, that means your anchor points are (0,0) not (0.5,0.5)
Zig zac path in sprite kit
Try the following code to get a zig zag path using bezier curves.
var cgpath: CGMutablePathRef = CGPathCreateMutable();
var xStart: CGFloat = CGFloat(200.0);
var xEnd: CGFloat = CGFloat(200.0);
var yStart : CGFloat = 0
var yEnd : CGFloat = self.size.height
let numberOfIntermediatePoints = 10
let xOffset : CGFloat = 400
let intervalY = (yEnd - yStart)/CGFloat(numberOfIntermediatePoints)
var s: CGPoint = CGPointMake(xStart,yStart);
CGPathMoveToPoint(cgpath,nil, s.x, s.y);
for i in stride(from: 0, through: numberOfIntermediatePoints - 1, by: 1) {
let yOff = intervalY * CGFloat(i) + yStart
let controlPointInterval = intervalY/3
var cp1X: CGFloat = CGFloat(xStart + xOffset);
var cp1Y: CGFloat = CGFloat(yStart + yOff + controlPointInterval);
var cp2X: CGFloat = CGFloat(xStart - xOffset);
var cp2Y: CGFloat = CGFloat(yStart + yOff + controlPointInterval * 2);
var e: CGPoint = CGPointMake(xEnd, yStart + yOff + intervalY);
var cp1: CGPoint = CGPointMake(cp1X, cp1Y);
var cp2: CGPoint = CGPointMake(cp2X, cp2Y);
CGPathAddCurveToPoint(cgpath, nil, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
}
var ball:SKSpriteNode = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(40, 40));
var followTrack = SKAction.followPath(cgpath, asOffset: false, orientToPath: true, duration: 5.0);
var forever: SKAction = SKAction.repeatActionForever(followTrack);
ball.runAction(forever);
self.addChild(ball);
let shapeNode = SKShapeNode(path: cgpath)
self.addChild(shapeNode);
Change numberOfIntermediatePoints
and xOffset
values to change the number of zig zags and the amount of distance moved in each zig zag.
Crop/Mask circular image node in sprite kit gives jagged edges
UPDATE:
Ok, so here's one solution I came up. Not super ideal but it works perfectly. Essentially, I have a to size/scale, and cut the image exactly the way it would go on the SKSpriteNode so I would not have to use SKCropNode or some variation of SKShapeNode.
I used these UIImage extensions by Leo Dabus to resize/shape the image exactly as needed. Cut a UIImage into a circle Swift(iOS)
var circle: UIImage? {
let square = CGSize(width: min(size.width, size.height), height: min(size.width, size.height))
let imageView = UIImageView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: square))
imageView.contentMode = .ScaleAspectFill
imageView.image = self
imageView.layer.cornerRadius = square.width/2
imageView.layer.masksToBounds = true
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, scale)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
imageView.layer.renderInContext(context)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
func resizedImageWithinRect(rectSize: CGSize) -> UIImage {
let widthFactor = size.width / rectSize.width
let heightFactor = size.height / rectSize.height
var resizeFactor = widthFactor
if size.height > size.width {
resizeFactor = heightFactor
}
let newSize = CGSizeMake(size.width/resizeFactor, size.height/resizeFactor)
let resized = resizedImage(newSize)
return resized
}The final codes look like this:
//create/shape image
let image = UIImage(named: "TestImage")
let scaledImage = image?.resizedImageWithinRect(CGSize(width: 100, height: 100))
let circleImage = scaledImage?.circle
//create sprite
let sprite = SKSpriteNode(texture: SKTexture(image: circleImage!))
sprite.position = CGPoint(x: view.frame.width/2, y: view.frame.height/2)
//set texture/image
sprite.texture = SKTexture(image: circleImage!)
sprite.physicsBody = SKPhysicsBody(texture: SKTexture(image: circleImage!), size: CGSizeMake(100, 100))
if let physics = sprite.physicsBody {
//add the physic properties
}
//scale node
sprite.setScale(1.0)
addChild(sprite)
So if you have a perfectly scaled asset/image, then you probably dont need to do all this work, but I'm getting images from the backend that could come in any sizes.
sprite follow another sprite's path - sprite kit
You don't provide any code to go with your question... You should try to post something, to see how we can do things with what you've got. Anyways...
You should store the first sprite's X and Y position values, many of them in a set of time (a buffer, if you will). Register the sprite's XY positions in an array, where the older positions get deleted and the new positions take their place.
Then assign those positions to the second sprite, with a delay.
Edit:
I'm guessing the x and y positions of the first sprite are the
self.player.position.x, self.player.position.y
so you would store these at regular time intervals in an array, and delete the older positions as you assign them to the second sprite.
This should be very easy to do, provided that you understand the code that you're using.
Related Topics
Swift + Nsviewcontroller Background Color (MAC App)
How to Fix ' *Pod* Does Not Support Provisioning Profiles' in Azure Devops Build Agent
Can't Able to Get Video Tracks from Avurlasset for Hls Videos(.M3U8 Format) for Avplayer
Create PDF of Dynamic Size with Typography Using Uiview Template(S)
Can You Use String/Character Literals Within Swift String Interpolation
How to Show an Alert from Another Class in Swift
Xcode 6 Beta/Swift - Playground Not Updating
Add an Extension/Method to All Objects in Swift
Swift Only -- Reading from Nsinputstream
How to Use Swift Package Manager in Xcode 9 Playground
Convincing Swift That a Function Will Never Return, Due to a Thrown Exception
Check If Variable Is a Block/Function/Callable in Swift
Swift Subtle Difference Between Curried and Higher Order Function
Xcode 8 Swift Update with Error "Use Legacy Swift Language Version"