(Xcode Swift SpriteKit) How do I move a sprite in the direction it is facing - sprite-kit

I have a sprite in the centre of the screen which can be rotated left or right which is determined by touching the left or right part of the screen.
What I would like to do is have the sprite continuously moving forwards but always in the direction it is facing. I know how to do basic movement with SKActions etc... but have no idea how I calculate the movement to be continuously in the direction that the sprite has rotated to?
Maths has never been my strong point so would very much appreciate some sample code to help me along.
var player = SKSpriteNode()
override func didMove(to view: SKView) {
player = SKSpriteNode(imageNamed: "4B.png")
player.setScale(0.3)
player.zPosition = 100
self.addChild(player)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self)
if position.x < 0 {
let rotate = SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(M_PI), duration: 2))
player.run(rotate, withKey: "rotating")
} else {
let rotate = SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(-M_PI), duration: 2))
player.run(rotate, withKey: "rotating")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
player.removeAction(forKey: "rotating")
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}

You will need to use sin and cos. An SKAction may not be the best thing for you, so I would just do this in the update method for now till you find a better spot:
sprite.position = CGPoint(x:sprite.position.x + cos(sprite.zRotation) * 10,y:sprite.position.y + sin(sprite.zRotation) * 10)
Where 10 is the magnitude that you want the sprite to move (aka move 10 pixels)
This assumes that angle 0 means the sprite is looking right, angle 90 (PI/2) is looking up, angle 180 (PI) is looking left, and angle 270 (3PI/2) is looking down.

Related

Changing the angle of a SKSpriteNode during a rotation

I am trying to make a drifting game. In order for the car to drift, the car (when turning) needs to be at an angle. I have tried rotation, however this conflicts with the code I already have for turning the car. Here is my code, any help?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.previousLocation(in: self)
let position = touch.location(in: self)
let node = self.nodes(at: location).first
if position.x < 0 {
let rotate = SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(M_PI), duration: 0.8))
car.run(rotate, withKey: "rotating")
} else {
let rotate = SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(-M_PI), duration: 0.8))
car.run(rotate, withKey: "rotating")
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
car.position = CGPoint(x:car.position.x + cos(car.zRotation) * 3.0,y:car.position.y + sin(car.zRotation) * 3.0)
}
}
This code currently turns the car without adding any angle, or "drifting" effect.
one approach is you could nest your car inside another SKNode as a container. apply your steering rotation to the car node, as you're doing it now. then apply the drift rotation to the container node. the result effect will be the sum of the two.
//embed car inside a SKNode container so you can apply different rotations to each
let car = SKShapeNode(ellipseOf: CGSize(width: 20, height: 40)) //change to how you draw your car
let drift_container = SKNode()
drift_container.addChild(car) //embed car inside the container
self.addChild(drift_container) //`self` here is a SKScene
//apply angle rotation to the container
func drift(byAngle angle:CGFloat) {
let rotate = SKAction.rotate(toAngle: angle, duration: 0.2)
drift_container.run(rotate)
}
then in update be sure to update drift_container.position instead of the car's position

In Xcode how would I get a SkSpriteNode to move up when clicked on?

How do I get the SkSpriteNode to move up when clicked?
-Thanks
https://i.stack.imgur.com/fcOvr.png
lets say your node is called box
use the touches began function
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for _ in touches{
let touch:UITouch = touches.first!
let touchLocation = touch.location(in: self)
box.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
box.physicsBody?.applyImpulse(CGVector(dx: 0, dy:300))
}
}
the applyImpusle method takes an x value to move along the x axis(left or right) and takes a y value to move along the y axis (up or down). set dy accordingly.
rest of it depends on your scene properties like gravity , friction , mass of the box and so on
Without using a physicsBody you can use this code and just change how you move it up (I do it manually just to keep it simple).
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if sprite.contains(location){
sprite.position = CGPoint(x: 0, y: 10)
}

How Make a Sprite Node Rotate Around Another Node? (One More Line)

So, I am trying to challenge my self by recreating the popular App Store game, One More Line using SpriteKit and Swift 3; however, I am immediately having a big issue.
1)How do I get my node to rotate around another node when the user is tapping
2) I want my node to continue moving in the same direction it is facing when the user releases their finger (hope that makes sense...).
kinda like this
In my GameScene.sks I created a center node that is constantly rotating and a player. Then I did this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
player.move(toParent: center)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
player.move(toParent: self)
}
override func update(_ currentTime: TimeInterval) {
let dx = 5 * cos(player.zRotation)
let dy = 5 * sin(player.zRotation)
player.physicsBody?.applyImpulse(CGVector(dx: dx, dy: dy))
}
player moves to the right and when I tap it circles around the node, when I release it continues to move to the right.
I know that I am going about this all wrong. Any help would be very much appreciated. Thanks!
I'm not sure I completely understand what you are looking for, but if you simply want a sprite to rotate evenly around a centre sprite, add the centre sprite, another centre sprite (invisible) and add your outer sprite to the invisible centre sprite. Then rotate your two centre sprites. Adjust the outerObject's x position to bring the outerObject closer or further from the centre object.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
anchorPoint = CGPoint(x: 0.5, y: 0.5)
let centerObject = SKSpriteNode(texture: nil, color: UIColor.blue, size: CGSize(width: 20, height: 20))
addChild(centerObject)
let invisibleParent = SKSpriteNode()
addChild(invisibleParent)
let outerObject = SKSpriteNode(texture: nil, color: UIColor.red, size: CGSize(width: 50, height: 50))
outerObject.position.x = 100
invisibleParent.addChild(outerObject)
centerObject.run(SKAction.repeatForever(SKAction.rotate(byAngle: 1, duration: 1)))
invisibleParent.run(SKAction.repeatForever(SKAction.rotate(byAngle: -1, duration: 1)))
}
}

SKCameraNode working weird

I'm trying to create a simple SpriteKit game. I have a player that starts from the bottom-center of the screen.
I added to my GameScene a camera to follow the player but it does not work. when I run it with this code:
class GameScene: SKScene {
let mPlayer = Player(circleOfRadius: 45, fillColor: myColors.blue, strokeColor: myColors.red)
let cameraNode = SKCameraNode()
override func didMove(to view: SKView) {
setup()
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
moveCamera()
Player.movePlayerUp(mPlayer: mPlayer)
}
func setup(){
mPlayer.position = CGPoint(x: self.frame.midX - PLAYER_WIDTH, y: self.frame.minY + PLAYER_HEIGHT)
self.camera?.position = mPlayer.position
self.camera = cameraNode
self.addChild(cameraNode)
self.addChild(mPlayer)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let location = touch.location(in: self)
mPlayer.run(SKAction.moveTo(x: location.x, duration: 0.1))
}
}
func moveCamera(){
let moveAction = SKAction.moveTo(y: mPlayer.position.y, duration: 0.1)
cameraNode.run(moveAction)
}
}
the player is starting from the bottom and immediately moves to the center of the screen like in this picture:
How can I make the camera to follow the player from the start and move with it to the top?
Hope my question is clear and sorry for the size of the picture.
This code works for me in a empty Xcode project. You are probably thinking its not because your background image is 1 color and you are only moving the player briefly.
Also in your update method you set to move the player constantly to the middle, which is not necessary.
Try adding some stripes to the background to make it easier to see if you move and than add this code to the update method do slowly move the player up constantly.
let action = SKAction.move(by: CGVector(dx: 0, dy: 5), duration: 1)
mPlayer.run(action)
You will see the camera is following the player correctly.
If you need to center your camera differently to the player e.g on the y axis than you just have to increase/decrease the y position of the camera.
let moveAction = SKAction.moveTo(y: mPlayer.position.y + 200, duration: 0.1)
cameraNode.run(moveAction)
Hope this helps

Apply impulse to a SKSpriteNode from a user swipe

Can anyone tell me why when I try to apply an impulse to a node like this it disappears?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch:UITouch = touches.first! as UITouch
startPoint = touch.location(in: view)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
defer {
startPoint = nil
}
guard touches.count == 1, let startPoint = startPoint else {
return
}
if let touch = touches.first {
let endPoint = touch.location(in: view)
//Calculate your vector from the delta and what not here
direction = CGVector(dx: endPoint.x - startPoint.x, dy: endPoint.y - startPoint.y)
L1Ball.physicsBody?.applyImpulse(CGVector(dx: direction.dx.hashValue, dy: direction.dy.hashValue))
}
}
I think it has something to do with the way I am using hashValue. I don't know another way to get the values from the CGVector direction. If I try to use direction itself as the impulse vector it just crashes. Any insight? Thanks!
Your code worked for me when I removed the .hashValue from direction.dx and direction.dy (hash values are used for comparisons). Oh and your ball node isn't disappearing. Those hash values are large enough that when you apply them on a ball as a force, the speed of the ball is big enough that by the time the next frame is rendered the ball is off the screen.