Node rotation doesn't follow a finger - ios

I'm trying to rotate an arrow to follow a finger movement but it performs weirdly. It is definitely not following it. I'm trying to do it in touchesMoved. I tried to do this:
var fingerLocation = CGPoint()
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
fingerLocation = touch.locationInNode(self)
let currentOrient = arrow.position
let angle = atan2(currentOrient.y - fingerLocation.y, currentOrient.x - fingerLocation.y)
let rotateAction = SKAction.rotateToAngle(angle + CGFloat(M_PI*0.5), duration: 0.0)
arrow.runAction(rotateAction)
}
}
And also tried this:
var fingerLocation = CGPoint()
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
fingerLocation = touch.locationInNode(self)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
var radians = atan2(fingerLocation.x, fingerLocation.y)
arrow.zRotation = -radians
}
I also tried SKConstraint.orientToPoint but had no luck in it either. What am I doing wrong? Every answer to similar question is suggestion atan2, but it doesn't seem to work for me.

If you want to rotate the sprite towards to touch location, it should be simple as :
let touchLocation = touch.locationInNode(self)
var dx = hero.position.x - positionInScene.x;
var dy = hero.position.y - positionInScene.y ;
var angle = atan2(dy,dx) + CGFloat(M_PI_2)
hero.zRotation = angle
It worked when I tried, so it can give you an basic idea where to start. Or I misunderstood what you are trying to achieve...
EDIT:
Currently what you will get if you try to convert angle to degrees is angle in range from -90 to 270 degrees. Its described here why. If you want to work with angle in range of 0 to 360, you can change to code above to:
var dx = missile.position.x - positionInScene.x ;
var dy = missile.position.y - positionInScene.y;
var angleInRadians = atan2(dy,dx) + CGFloat(M_PI_2)
if(angleInRadians < 0){
angleInRadians = angleInRadians + 2 * CGFloat(M_PI)
}
missile.zRotation = angleInRadians
var degrees = angleInRadians < 0 ? angleInRadians * 57.29577951 + 360 : angleInRadians * 57.29577951
Here is the result with debugging data:

Related

SpriteKit: calculate angle of joystick and change sprite based on that

I am making a RPG Birds-eye style game with SpriteKit. I made a joystick because a D-Pad does not give the player enough control over his character.
I cannot wrap my brain around how I would calculate the neccessary data needed to change the Sprite of my character based on the angle of the joystick thumb Node.
Here is my code I am using
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if isTracking == false && base.contains(location) {
isTracking = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location: CGPoint = touch.location(in: self)
if isTracking == true {
let v = CGVector(dx: location.x - base.position.x, dy: location.y - DPad.position.y)
let angle = atan2(v.dy, v.dx)
let deg = angle * CGFloat(180 / Double.pi)
let Length:CGFloat = base.frame.size.height / 2
let xDist: CGFloat = sin(angle - 1.57079633) * Length
let yDist: CGFloat = cos(angle - 1.57079633) * Length
print(xDist,yDist)
xJoystickDelta = location.x * base.position.x / CGFloat(Double.pi)
yJoystickDelta = location.y * base.position.y / CGFloat(Double.pi)
if base.contains(location) {
thumbNode.position = location
} else {
thumbNode.position = CGPoint(x: base.position.x - xDist, y: base.position.y + yDist)
}
}
}
}
Update method
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if xJoystickDelta > 0 && yJoystickDelta < 0 {
print("forward")
}
}
The way I have set up right now tests the positive or negative state of the Joystick position in a cross method based on where the thumb Node is inside of the four marked sections below
I dont want it to do that
How can I set it up so that it changes the sprite based on where the thumb node is actually pointing inside my joysticks base like so.
I have been struggling with this for 3 days now so any help would be appreciated.
That looks far too complicated. Just compare the x and y components
of the difference vector v. Something like this:
if v.dx > abs(v.dy) {
// right
} else if v.dx < -abs(v.dy) {
// left
} else if v.dy < 0 {
// up
} else if v.dy > 0 {
// down
}

Swift: Agar.io-like smooth SKCameraNode movement?

If anyone here has played Agar.io before, you'll probably be familiar with the way the camera moves. It slides toward your mouse depending on the distance your mouse is from the center of the screen. I'm trying to recreate this movement using an SKCameraNode and touchesMoved:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let pos = touches.first!.location(in: view)
let center = view!.center
let xDist = pos.x - center.x
let yDist = pos.y - center.y
let distance = sqrt((xDist * xDist) + (yDist * yDist)) * 0.02
camera?.position.x += xDist * distance * 0.02
camera?.position.y -= yDist * distance * 0.02
}
Surprisingly, this doesn't look too bad. However, there are two problems with this.
The first is that I want to use a UIPanGestureRecognizer for this, since it'd work far better with multiple touches. I just can't imagine how this could be done, since I can't think of a way to use it to get the distance from the touch to the center of the screen.
The second problem is that this movement isn't smooth. If the user stops moving their finger for a single frame, there is a huge jump since the camera snaps to an absolute halt. I'm awful at maths, so I can't think of a way to implement a nice smooth decay (or attack).
Any help would be fantastic!
EDIT: I'm now doing this:
private var difference = CGVector()
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let pos = touches.first!.location(in: view)
let center = view!.center
difference = CGVector(dx: pos.x - center.x, dy: pos.y - center.y)
}
override func update(_ currentTime: TimeInterval) {
let distance = sqrt((difference.dx * difference.dx) + (difference.dy * difference.dy)) * 0.02
camera?.position.x += difference.dx * distance * 0.02
camera?.position.y -= difference.dy * distance * 0.02
}
All I need to know now is a good way to get the difference variable to smoothly increase from the time the user touches the screen.
EDIT 2: Now I'm doing this:
private var difference = CGVector()
private var touching: Bool = false
private var fade: CGFloat = 0
private func touched(_ touch: UITouch) {
let pos = touch.location(in: view)
let center = view!.center
difference = CGVector(dx: pos.x - center.x, dy: pos.y - center.y)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = true
touched(touches.first!)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
touched(touches.first!)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
touched(touches.first!)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
if touching {
if fade < 0.02 { fade += 0.0005 }
} else {
if fade > 0 { fade -= 0.0005 }
}
let distance = sqrt((difference.dx * difference.dx) + (difference.dy * difference.dy)) * 0.01
camera?.position.x += difference.dx * distance * fade
camera?.position.y -= difference.dy * distance * fade
}
Can someone please help me before it gets any worse? The fade variable is incremented in an awful way and it's not smooth, and I just need someone to give me a slight hint of a better way to do this.
Try Linear Interpolation. Linear interpolation can make it so your object will slowly and smoothly speed up or slow down over time.

Calculate rotation velocity and naturally rotate view iOS

I have a circle-shaped view and have it rotate via the following code overriding touchesBegan(touches:, withEvent:) and touchesMoved(touches:, withEvent:).
var deltaAngle = CGFloat(0)
var startTransform: CGAffineTransform?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touchPoint = touches.first!.locationInView(view)
let dx = touchPoint.x - view.center.x
let dy = touchPoint.y - view.center.y
deltaAngle = atan2(dy, dx)
startTransform = arrowPickerView.transform
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touchPoint = touches.first!.locationInView(view)
let dx = touchPoint.x - view.center.x
let dy = touchPoint.y - view.center.y
let angle = atan2(dy, dx)
let angleDifference = deltaAngle - angle
let transform = CGAffineTransformRotate(startTransform!, -angleDifference)
arrowPickerView.transform = transform
}
I want to override touchesEnded(touches:, withEvent:) to calculate the velocity and have the view naturally rotate a little (similar to continuous scrolling). I currently save the original transform and calculate the delta angle. How can I implement this? Thanks in advance!
In my example below, the CGAffineTransformRotate depends on:
direction (if the user moves from left to right, up or down etc)
rotation (a factor dependent on the time between touchesBegan and touchesEnded)
PI
This creates an CGAffineTransformRotate and this rotates with duration 1 (s), to create a feel of kinetic scrolling the UIViewAnimationOptions.CurveEaseOut property is used
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touchPointEnd = touches.first!.locationInView(view)
self.dateTouchesEnded = NSDate()
let delay : NSTimeInterval = NSTimeInterval(0)
let timeDelta : Double = self.dateTouchesEnded!.timeIntervalSince1970 - self.dateTouchesStarted!.timeIntervalSince1970
let horizontalDistance : Double = Double(touchPointEnd.x - self.touchPointStart!.x)
let verticalDistance : Double = Double(touchPointEnd.y - self.touchPointStart!.y)
var direction : Double = 1
if (fabs(horizontalDistance) > fabs(verticalDistance)) {
if horizontalDistance > 0 {
direction = -1
}
} else {
if verticalDistance < 0 {
direction = -1
}
}
let rotation : Double = (0.1 / timeDelta < 0.99) ? 0.1 / timeDelta : 0.99
let duration : NSTimeInterval = NSTimeInterval(1)
UIView.animateWithDuration(duration, delay: delay, options: UIViewAnimationOptions.CurveEaseOut, animations: {
let transform = CGAffineTransformRotate(self.imageView.transform, CGFloat(direction) * CGFloat(rotation) * CGFloat(M_PI))
self.imageView.transform = transform
}, completion: nil)
}
I added the variables to keep track of the start and end time of touches and added the CGPoint location of the touchesBegan function
var dateTouchesStarted : NSDate?
var dateTouchesEnded : NSDate?
var touchPointStart : CGPoint?
In touchesBegan i added:
self.touchPointStart = touchPoint
To set the variable as explained above.

SpriteKit friction not seeming to working properly

On a project in Xcode 7 I have a few SKSpriteNodes that move back and forth on the screen and another one, called user, that is meant to jump from sprite to sprite, progressively up the screen. However, when user lands on one of the moving sprites the moving sprite just slides right out from under it and user falls back down. I thought that this meant that I needed to increase the friction property on the nodes so that user would "stick" to the nodes, but this just makes it bounce on the other nodes. My problem is that the nodes moving back and forth seem to "slippery," and user just doesn't stay on them.
Here's my code:
My class for user:
class UserNode: SKSpriteNode
{
class func newNode(position position: CGPoint) -> UserNode
{
let position = position
let sprite = UserNode(imageNamed: "userImage")
sprite.position = position
sprite.size = CGSize(width: sprite.size.width * 2, height: sprite.size.height * 2)
sprite.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "userImage"), size: sprite.size)
sprite.physicsBody?.affectedByGravity = true
sprite.physicsBody?.dynamic = true
sprite.physicsBody?.allowsRotation = false
sprite.physicsBody?.friction = 0.2
return sprite
}
}
and for moving user (the methods in my gamescene)
let scale: CGFloat = 2.0
let damping: CGFloat = 0.98
var point = CGPoint?()
func moveNodeToPoint(sprite: SKSpriteNode, point: CGPoint)
{
let dx = (point.x - sprite.position.x) * scale
let dy = (point.y - sprite.position.y) * scale
sprite.physicsBody?.velocity = CGVectorMake(dx, dy)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
/* Called when a touch begins */
for _: AnyObject in touches
{
if !welcomeNode.hidden
{
let fadeAway = SKAction.fadeOutWithDuration(0.3)
welcomeNode.runAction(fadeAway)
directionsNode.runAction(fadeAway)
touchStartNode.runAction(fadeAway)
welcomeNode.hidden = true
directionsNode.hidden = true
touchStartNode.hidden = true
}
//////////
point = CGPointMake(self.frame.midX, user.position.y + 300)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)
{
point = nil
}
override func update(currentTime: CFTimeInterval)
{
/* Called before each frame is rendered */
if (point != nil)
{
moveNodeToPoint(user, point: point!)
}
else
{
let dx = user.physicsBody!.velocity.dx * damping
let dy = user.physicsBody!.velocity.dy * damping
user.physicsBody?.velocity = CGVectorMake(dx, dy)
}
}
and for moving the platforms:
let screenSize = UIScreen.mainScreen().bounds
let width = screenSize.size.width * 2
let firstAction = SKAction.moveBy(CGVector(dx: width, dy: 0), duration: 2)
let secondAction = SKAction.moveBy(CGVector(dx: -width, dy: 0), duration: 2)
let actions = [firstAction, secondAction]
let barAction = SKAction.sequence(actions)
let mainBarAction = SKAction.repeatActionForever(barAction)
platform.runAction(mainBarAction)

Dragging and flicking/throwing skspritenode [duplicate]

This question already has answers here:
how to throw SKSpriteNode?
(3 answers)
Closed 7 years ago.
I have ball sprite that i want to be able to flick towards other sprites. Right now, I have the function TouchesMoved below, but it moves my sprite towards any touch movement on the screen, when I want my user to first touch the sprite and drag their finger towards the target, causing the sprite to move. This is the dysfunctional function.. Any help would be appreciated!
EDIT: I wanted the ball velocity to remain constant no matter the flick length.. not addressed by other answers.
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
if firstTimerStarted == false {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
sceneTouched(touchLocation)
}
This should work, I just dug it out from an old project.
CGFloat dt is for changing the speed/power of the movement.
var touching = false
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let location = touch.locationInNode(self)
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let location = touch.locationInNode(self)
touchPoint = location
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
touching = false
}
override func update(currentTime: CFTimeInterval) {
if touching {
if touchPoint != sprite.position
{
let dt:CGFloat = 0.15
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let vel = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity = vel
}
}
}
EDIT:
The reason it gets stronger the farther the distance, is because the vector IS the distance between the the sprite and the touch point.
Try popping this in as the update function. It should work...
override func update(currentTime: CFTimeInterval) {
if touching {
if touchPoint != sprite.position
{
let pointA = touchPoint
let pointB = sprite.position
let pointC = CGPointMake(sprite.position.x + 2, sprite.position.y)
let angle_ab = atan2(pointA.y - pointB.y, pointA.x - pointB.x)
let angle_cb = atan2(pointC.y - pointB.y, pointC.x - pointB.x)
let angle_abc = angle_ab - angle_cb
let vectorx = cos(angle_abc)
let vectory = sin(angle_abc)
let dt:CGFloat = 15
let vel = CGVector(dx: vectorx * dt, dy: vectory * dt)
sprite.physicsBody!.velocity = vel
}
}
}
With the touchPoint (pointA), and the sprite's position (pointB) we can create an angle.
atan2, is a very famous function from C, it creates the angle between two points.
BUT, it's 0 degrees is in a different location than usual.
So, we need our own 0 degrees marker, I use the mid-right of the point as my marker.
It's the common 0 degree placement:
Since it's to the right of the sprite's position, we create a point just to the right of the sprite (pointC).
We now use atan2 to find the angle.
To create a vector from an angle, we just use cos and sin for the x and y values.

Resources