How do I make a node move in sprite kit swift - swift

I have been wondering how to do this for a long time I am using Sprite Kit swift, my problem is that don't know how to make a node move with SKActions so basicly when the go on the scene that I put it on they see a node moving (name the node sprite) ,I do not understand how it works can someone please show me an explained example on how to do this, thank you in advance!

To move a Sprite in Sprite-Kit you can use SKActions.
For example:
let action = SKAction.moveByX(3, y: 2, duration: 10)
This will make the sprite move 3 units along the x-axis and 2 units along the y axis in 10 seconds.
If you want your sprite to move to a specific place, you can do:
let action2 = SKAction.moveTo(location: CGPoint, duration: NSTimeInterval)
Hope this helped!

As I understand you have a scene with some node (e.g. name="myNode").
First of all, you need to access this node:
override func didMoveToView(view: SKView) {
let myNode = childNodeWithName(homeButtonName)!
...
}
Now you have a reference to your node.
Next step is to add action to move this node.
For example, let's move this node +20 horizontally and -30 vertically in 3 seconds:
let dX = 20
let dY = -30
let moveAction = SKAction.moveByX(CGFloat(dX), y: CGFloat(dY), duration: 3.0)
myNode.runAction(moveAction)
You can change many node's properties, not only position, e.g. size, alpha, rotation etc.

You can give actions to a Node or Sprite in Swift 3.0... First, we will change the size of the Node, or Sprite.
let nodeSize = SKAction.scale(to: 0.5, duration: 2)
In two seconds, this will change the size of the object from whatever it is to half that size. Next, to move the Node or Sprite to a different place, use...
let nodeSet = SKAction.moveBy(x: -3.0, y: 2.0, duration: 2)
In two seconds, the object will move to the left 3 units, and up 2.
If you want to assign these actions to a specific node, you can first create a node var parentNode = SKShapeNode() and then you can tell them to run the action.
parentNode.run(nodeSize)
parentNode.run(nodeSet)
Hope that this helps.

Related

SKSpriteNode - constant speed, and detect which SKSpriteNode does something

I have two questions:
How can I make an SKSpriteNode move at a constant speed and direction?
I'm currently using item.physicsBody?.velocity, but I want just a constant speed, in the direction to the left.
let item = SKSpriteNode(color: .black, size: CGSize(width: 32, height: 16))
item.position = CGPoint(x: 600, y: self.heights[random])
item.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:32, height: 16))
addChild(item)
item.physicsBody?.velocity = CGVector(dx: -360, dy: 0)
item.physicsBody!.affectedByGravity = false
I have the above code in touchesBegan(). I want to add some code to update(), to detect when one of these created SKSpriteNodes does a certain thing (e.g. reaches a specified x position), and then make a change to that SKSpriteNode (e.g. delete it). How do I do that, so that I get that particular SKSpriteNode?
Just manually update your sprite's position. Define a speed in points per second, use a timer in update() to see how much time has passed since it was last called and with a bi of maths, work out its new position. I've done this and it works.
I'm not sure what the relevance of the touchesBegan() code is to this point (are you creating a sprite and setting it moving when you touch the screen?), but in general you could iterate over the array of child nodes in the scene using enumerateChildNodes(). When you have a node that matches your criteria (specific x position etc.), perform the action on the node.

spritekit how to selectively scale nodes

as background lets assume I have a map- literally a road map being rendered inside my SKScene. Roads are represented by SKShapenodes with path set to an array of CGPoints. I want the user to be able to zoom in/out so I created a camera node:
var cam: SKCameraNode = SKCameraNode()
and as the user wants to zoom in/out by scrolling on the trackpad:
let zoomInAction = SKAction.scale(to: CGFloat(scale), duration: 0.0)
camera?.run(zoomInAction)
This works great however I have an additional complexity which I'm not sure how to handle. I want some nodes (for examples road name labels, icons, map legend) to be exempt from scaling- such that as a user zooms in/out the road name label remains the same size while the road shape scales proportionally.
Not sure how to handle this? Can I have a hierarchy of scenes so one layer scales and the other doesnt scale? Can that be achieved by attaching the camera node to the "scalable" layer? Any help appreciated!
Here is the case. If you want the node scale won't change with camera, just add the node to the tree of camera. Don't forget add cameraNode to scene, otherwise, those nodes connected to camera won't be rendered.
In the following, label is rendered via camera and won't change scale.
let label = SKLabelNode.init(text: "GFFFGGG")
label.fontSize = 30
label.fontColor = UIColor.black
label.name = "cool"
label.zPosition = 100
let camera = SKCameraNode()
camera.addChild(label)
scene.addChild(camera)
scene.camera = camera
camera.position = CGPoint.init(x: 0, y: 0)
camera.xScale = 2.0
If you have nodes connecting to scene before,
you may remove the node from parent and then add to camera.
If using a function to batch handling them should not be as hard as thought.
Maybe not necessary:
You may transfer them to cameraNode tree via camera.convert(point: , from:) etc.

Spritekit: Animate node with running atlas and move node

I want animate a character as running using .atlas and simultaneously also move the character on screen.
I was able to simulate running using atlas but don't know how to simultaneously move it.
Right now I use this:
let animation = SKAction.animateWithTextures(frames, timePerFrame: 0.2)
monsterNode.runAction((animation)).
You can, for instance, apply force to the node's physicsBody. This is great for a platformer, as obstacles and the landmark would slow down the character, making it more realistic.
let force = CGVector(dx: 9000, dy: 0)
self.physicsBody?.applyForce(force)
For a space racer or other more simple movement, you could simply apply velocity to it.
self.physicsBody?.velocity = CGVector(dx: 100, dy:10)
See SKPhysicsBody reference for more.
Update:
For a very simple movement, you can use the SKAction's moveBy and moveTo methods and group the movement and the animation.
let vector = CGVector(dx:100, dy: 10)
let moveAction = SKAction.move(by: vector, duration: 1)
yourNode.run(SKAction.group([moveAction, animationAction])

Swift 3 (SpriteKit): Locking the x axis of a SKSpriteNode and its physicsBody

I really need to know how to lock the x axis of an SKSpriteNode and its physicsBody. I need to keep the SKSpriteNode dynamic and affectedByGravity. The node is on a slope, so this is why it's x axis is moved due to gravity. However, I don't want the x axis of this SKSpriteNode to move due to gravity. Is there a way to lock the x axis in order to achieve this?
Thanks for any help :D
Edit: I have tried to apply a constraint to the x value like this:
let xConstraint = SKConstraint.positionX(SKRange(constantValue: 195))
node.constraints?.append(xConstraint)
However this doesn't work and I'm not sure why and how to fix it. Anyone know of a solution?
Edit 2: SKPhysicsJointPin is actually looking more promising. In the comments of the first response/answer to this question, I have been trying to figure how to properly use it in my situation.
An example of my code:
let node = SKSpriteNode(imageNamed: "node")
enum collisionType: UInt32 {
case node = 1
case ground = 2
case other = 4 //the other node is unrelated to the ground and node
}
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
//Setup node physicsBody
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
node.physicsBody?.categoryBitMask = collisionType.node.rawValue
node.physicsBody?.collisionBitMask = //[other node that isn't the ground or the node]
node.physicsBody?.contactTestBitMask = //[other node that isn't the ground or the node]
node.physicsBody?.isDynamic = true
node.physicsBody?.affectedByGravity = true
addChild(node)
//Physics Setup
physicsWorld.contactDelegate = self
}
The node is on top of the ground, and the ground is composed of various SKSpriteNode lines that have a volumeBased physicsBody. The ground keeps adding new lines at the front, and removing the ones at the back, and changing the x value of each line by a negative (so the ground appears to moving - this process is performed in an SKAction). These lines (the parts of the ground) are on an angle which is why the node's x axis moves. I want the node to always be at the front of the ground (e.g. always on top of the newly created line). Currently, setting the position of the node like this locks the x axis (solving my issue):
override func didSimulatePhysics() {
//Manage node position
node.position.x = 195
node.position.y = CGFloat([yPosition of the first line of the ground - the yPosition keeps changing])
}
Note: ^This^ function is inside the GameScene class
The x axis actually stays the same like this. However, the issue is that now the physicsBody of the node is lower than the centre of the node (which didn't happen before).
A node's constraints property is nil by default. You'll need to create an array of one or more constraints and assign it to the property. For example
let xConstraint = SKConstraint.positionX(SKRange(constantValue: 195))
node.constraints = [xConstraint]
Update
You may want to use a camera node instead of moving the ground in the scene. With a camera node, you move the main character and the camera instead of the ground.
I think you could set the linearDamping property to 0.0
The linearDamping is a property that reduces the body’s linear velocity.
This property is used to simulate fluid or air friction forces on the
body. The property must be a value between 0.0 and 1.0. The default
value is 0.1. If the value is 0.0, no linear damping is applied to the
object.
You should pay attention also to the other forces applied to your SKSpriteNode. The gravitational force applied by the physics world for example where dx value, as you request , should be setted to 0.0:
CGVector(dx:0.0, dy:-4.9)
Remember also that when you apply other forces vectors like velocity you should maintain the dx property to 0.0 as constant if you want to block the x axis.
You could find more details to the official docs
Update (after your details to the comments below):
You could also anchored your sprite to the ground with an SKPhysicsJoint (I don't know your project so this is only for example):
var myJoint = SKPhysicsJointPin.joint(withBodyA: yourSprite.physicsBody!, bodyB: yourGround.physicsBody!, anchor: CGPoint(x: yourSprite.frame.minX, y: yourGround.frame.minY))
self.physicsWorld.add(myJoint)
You can work with the anchor property to create a good joint as you wish or adding more joints.

Swift 3 (SpriteKit): Making a projectile move

I was wondering how to apply a force or thrust to a projectile (specifically an SKSpriteNode) to make it continuously move until it hits something. I have tried some ways like setting the velocity like this:
let node = SKSpriteNode(imageNamed: "node")
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
node.physicsBody.velocity = CGVector(dx: 5, dy: 5)
or instead of changing the node's velocity value at the end, I tried:
node.physicsBody?.applyImpulse(CGVector(dx: 5, dy: 5))
or
node.physicsBody?.applyForce(CGVector(dx: 5, dy: 5))
None of these actually move the projectile. I have even tried adding the same impulse and force forever each frame with an SKAction and there is still no movement. I would really appreciate any help :) Thanks.
Try adding a sure fire dynamism to the object:
let node = SKSpriteNode(imageNamed: "node")
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
// add this, to ensure dynamism:
node.physicsBody.isDynamic = true
Now all forces and impulses should work. Although I'd suggest using much larger numbers. Depends on the size and mass of the object, but you might need numbers a couple of orders of magnitude higher to get rapid movement.