Apply impulse to a SKSpriteNode from a user swipe - sprite-kit

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.

Related

drag a SKSprite Node or a UIImage by my finger more accurately, maintain offset?

I am trying to drag a SKSprite Node or a UIImage by my finger more accurately. For example if a touch is registered in the bottom corner of the node and moved, I want to the image to maintain the distance between your finger location and the center of node. Currently the center location jumps to the finger location.
Here is my code, it seems the offset code (touchl - previousLocation) isn't being maintained.
Any suggestions?
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchl = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
triangle.position.x = touchl.x + (touchl.x - previousLocation.x)
triangle.position.y = touchl.y + (touchl.y - previousLocation.y)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchl = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
let dx = touchl.x - previousLocation.x
let dy = touchl.y - previousLocation.y
triangle.position.x = triangle.position.x + dx
triangle.position.y = triangle.position.y + dy
}
}

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)
}

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

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.

How to detect UITouch location visible in Swift 3

I am trying to detect a UITouch event visible.When touch event begins.Currently, I am using below code to detect the touch location.From below code,I can able to print the touch location.Any,help would be greatly appreciated.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
if let touch = touches.first {
let position :CGPoint = touch.location(in: view)
print(position.x)
print(position.y)
}
}
Note: I am not trying to draw a line or anything like a drawing app.I,Just want to see the touch event visibly.When happens.
Thanks in advance.
If you want a circle or something to appear when the user taps the screen, try this:
var touchIndicators: [UIView] = []
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: view)
let touchIndicator = UIView(frame: CGRect(x: location.x - 10, y: location.y - 10, width: 20, height: 20))
touchIndicator.alpha = 0.5
touchIndicator.backgroundColor = UIColor.red
touchIndicator.layer.cornerRadius = 10
self.view.addSubview(touchIndicator)
touchIndicators.append(touchIndicator)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for indicator in touchIndicators {
indicator.removeFromSuperview()
}
touchIndicators = []
}
Pretty straightforward. Add circular views when the user touches the screen and remove them when the user lifts his/her fingers. You can also do this using UITapGestureRecognizer.

Drag Rotate a Node around a fixed point

I'm trying to create a spinable node similar to the "prize wheel" in this question. So far I have the flinging capability, adding angular impulses on a physics body using a UIPanGestureRecognizer that works really well. I can also stop the spinning using a touch.
Now I'm trying to allow fine adjustment of the wheel using a drag or swipe gesture so if the player isn't happy with what they end up with they can manually spin/drag/rotate it to their favoured rotation.
Currently I save the location of the touch in the touchesBegan and try to increment the zRotation of my node in the update loop.
The rotation doesn't follow my finger and is jerky. I'm not sure if I'm getting an accurate enough read on the finger movement or if the change position of the finger isn't being accurately translated into radians. I suspect detecting the touch and then dealing with it in the update isn't a great solution.
Here's my code.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
if let touch = touches.first as? UITouch {
var location = touch.locationInView(self.view)
location = self.convertPointFromView(location)
mostRecentTouchLocation = location
let node = nodeAtPoint(location)
if node.name == Optional("left") && node.physicsBody?.angularVelocity != 0
{
node.physicsBody = SKPhysicsBody(circleOfRadius:150)
node.physicsBody?.applyAngularImpulse(0)
node.physicsBody?.pinned = true
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if mostRecentTouchLocation != CGPointZero{
let node = nodeAtPoint(mostRecentTouchLocation)
if node.name == Optional("left")
{
var positionInScene:CGPoint = mostRecentTouchLocation
let deltaX:Float = Float(positionInScene.x) - Float(node.position.x)
let deltaY:Float = Float(positionInScene.y) - Float(node.position.y)
let angle:CGFloat = CGFloat(atan2f(deltaY, deltaX))
let maths:CGFloat = angle - (CGFloat(90) * (CGFloat(M_PI) / 180.0))
node.zRotation += maths
mostRecentTouchLocation = CGPointZero
}
}
}
I've spread some of the maths across multiple lines in the update to make debugging a bit easier.
I can add the PanGestureRecognizer code if needed but I'll try to keep it short for now.
EDIT
Here is my latest code based on GilderMan's recommendation. I think it's working better but the rotation is far from smooth. It's jumping in large increments and not following the finger well. Does this mean there's something wrong with my angle calculation?
override func didSimulatePhysics() {
if mostRecentTouchLocation != CGPointZero {
let node = nodeAtPoint(mostRecentTouchLocation)
if node.name == Optional("left")
{
var positionInScene:CGPoint = mostRecentTouchLocation
let deltaX:Float = Float(positionInScene.x) - Float(node.position.x)
let deltaY:Float = Float(positionInScene.y) - Float(node.position.y)
let angle:CGFloat = CGFloat(atan2f(deltaY, deltaX))
node.zRotation += angle
println(angle)
mostRecentTouchLocation = CGPointZero
}
}
}
The following code simulates a prize wheel that spins based on touch. As the user's finger moves, the wheel rotates proportionately to the speed of the finger. When the user swipes on the wheel, the wheel will spin proportionately to the velocity of the swipe. You can change the angularDamping property of the physics body to slow or increase the rate at which the wheel comes to a stop.
class GameScene: SKScene {
var startingAngle:CGFloat?
var startingTime:TimeInterval?
override func didMove(to view: SKView) {
let wheel = SKSpriteNode(imageNamed: "Spaceship")
wheel.name = "wheel"
wheel.setScale(0.5)
wheel.physicsBody = SKPhysicsBody(circleOfRadius: wheel.size.width/2)
// Change this property as needed (increase it to slow faster)
wheel.physicsBody!.angularDamping = 0.25
wheel.physicsBody?.pinned = true
wheel.physicsBody?.affectedByGravity = false
addChild(wheel)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in:self)
let node = atPoint(location)
if node.name == "wheel" {
let dx = location.x - node.position.x
let dy = location.y - node.position.y
// Store angle and current time
startingAngle = atan2(dy, dx)
startingTime = touch.timestamp
node.physicsBody?.angularVelocity = 0
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let location = touch.location(in:self)
let node = atPoint(location)
if node.name == "wheel" {
let dx = location.x - node.position.x
let dy = location.y - node.position.y
let angle = atan2(dy, dx)
// Calculate angular velocity; handle wrap at pi/-pi
var deltaAngle = angle - startingAngle!
if abs(deltaAngle) > CGFloat.pi {
if (deltaAngle > 0) {
deltaAngle = deltaAngle - CGFloat.pi * 2
}
else {
deltaAngle = deltaAngle + CGFloat.pi * 2
}
}
let dt = CGFloat(touch.timestamp - startingTime!)
let velocity = deltaAngle / dt
node.physicsBody?.angularVelocity = velocity
// Update angle and time
startingAngle = angle
startingTime = touch.timestamp
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
startingAngle = nil
startingTime = nil
}
}
The zRotation of a SKNode is in radians. You can remove your conversion to degrees.
You may wish to do the angle adjustment in the didSimulatePhysics in order to compute the zRotation after physics have been applied. (This may not apply directly to this situation, but is good practice further down the line.)
I tried two set of coodes below.
Code Sample 1
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let node = atPoint(location)
guard let safeNode = node.parent else { break }
if safeNode.name == "wheel" {
let wheel = node.parent!
let dx = location.x
let dy = location.y
startingAngle = atan2(dx, dy)
startingTime = touch.timestamp
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let node = atPoint(location)
guard let safeNode = node.parent else { break }
if safeNode.name == "wheel" {
let wheel = node.parent!
if !isRotating {
let dx = location.x
let dy = location.y
let angle = atan2(dx, dy)
wheel.zRotation = -angle
//update angle and time
startingAngle = angle
startingTime = touch.timestamp
}
}
}
}
Code Sample 2
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let node = atPoint(location)
guard let safeNode = node.parent else { break }
if safeNode.name == "wheel" {
let wheel = node.parent!
let dx = location.x
let dy = location.y
startingAngle = atan2(dx, dy)
startingTime = touch.timestamp
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let node = atPoint(location)
guard let safeNode = node.parent else { break }
if safeNode.name == "wheel" {
let wheel = node.parent!
if !isRotating {
let dx = location.x
let dy = location.y
let angle = atan2(dx, dy)
let deltaAngle = angle - startingAngle!
wheel.zRotation -= deltaAngle
//update angle and time
startingAngle = angle
startingTime = touch.timestamp
}
}
}
}
The only difference between Code Sample 1 & 2 is the method of finding zRotation of the wheel.
Code Sample 1 uses wheel.zRotation = -angle. It ensures wheel follows the finger position exactly. But there is a bug that the wheel will jump position when you touch two distant locations in wheel then move your fingure. I am still unable to fix this bug.
Code Sample 2 uses wheel.zRotation -= deltaAngle to stack the change in angle of each finger movement. It supposes to generate the same result of code sample 1 but it does not exactly follow the finger movement.
I found out the reason by comparing the zPosition of Code Sample 1 and 2 with same finger movement. The deltaAngle in Code Sample 2 is slightly less (up to 8 decimal places)so the wheel does not exactly follow the fingure movement.