Move SpriteNode in direction of a swipe at a preset speed - ios

So basically I have the coin object, and I want to launch it across the screen. I have the following code which runs some calculations on the swipe, but not sure what is relevant for my current situation:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
touching = true
let touch = touches.first
let loc = touch?.locationInNode(self)
if coin.containsPoint(loc!){
touchPoint = loc!
touchTime = (touch?.timestamp)!
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
touching = false
let touch = touches.first
let loc = touch?.locationInNode(self)
var xDiff = (loc?.x)! - touchPoint.x
var yDiff = (loc?.y)! - touchPoint.y
let distance = sqrt(xDiff * xDiff + yDiff * yDiff)
}
I want the coin to be essentially thrown in the direction of the swipe, but I want it to reach a certain y-coordinate on the screen each time, before falling back down due to gravity. So I guess I need to somehow calculate the perfect speed each time to make it reach that y-point, and then push the coin object in the swipe direction at that speed?
Any help much appreciated! I'm online for the next few hours, so ask for more info and I'll be able to get back to you pretty quick.

It depends if you want the coin to be interacting with other physics bodies afterward. You have attached several "physics" tags to the question so I assume you want that :)
I'd go for a mathematics-based approach indeed. So basically you are just getting a direction from the swipe. The speed itself is dependent on the gravity that's affecting the coin.
You can separate the speed vector in the vertical and horizontal component.
The vertical distance to cover is let verticalDistance = targetHeight - coin.position.y. The coin has to have zero vertical speed at the highest point, and we know that during the animation it decelerates with a constant -9.81 pt/s*s (off the top of my head).
So the speed graph looks like this:
with formula f(x) = -9.81 * x + v where v is the vertical starting speed we are solving for. Algebra tells us that f(x) intersects with the x-axis with x-value v / 9.81.
The total distance covered up until the point where f(x) intersects with the x-axis is equal to the surface area of the triangle under f(x). This triangle's surface is 1/2 * v/9.81 * v (which is 1/2 * width * height).
Since you know what this distance should be (verticalDistance from previous paragraph), algebra dictates v = sqrt(2 * 9.81 * d). Now you know you starting vertical speed, based on the height the coin has to travel!
Since you know the coins angle (dictated by user's swipe) and the vertical speed, you can calculate the horizontal speed. I'm leaving that to you ;) Note that we are of course ignoring drag / friction which could impact the coin speed. Also my algebra might be rusty.
As a side note
I would replace let touch = touches.first with
guard let touch = touches.first else {
return
}
In this way, touch is a non-optional.

Related

Swift - SKCameraNode jitters when it zooms and moves

I have created an SKCameraNode which is is able to move around and look at a to-scale version of the solar system.
I am having an issue when the camera zooms in and moves around as shown here:
https://gfycat.com/HighScarceHorsemouse
This is the code that moves the position of the camera:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let previousPointOfTouch = touch.previousLocation(in: self)
let amountDraggedY = (pointOfTouch.y - previousPointOfTouch.y)
let amountDraggedX = (pointOfTouch.x - previousPointOfTouch.x)
hud.touchesMoved(x: amountDraggedX, y: amountDraggedY)
}
}
hud.touchesMoved calls this:
func touchesMoved(x: CGFloat, y:CGFloat){
print("Previous Position:" + cam.position.debugDescription)
print("Position Change: (\(x), \(y))")
cam.position.x = cam.position.x - x
cam.position.y = cam.position.y - y
print("New Position: " + cam.position.debugDescription)
}
The output of the print function gives me this when the camera is zoomed out:
Previous Position:(-2940752.25, 86727.8203125)
Position Change: (-2940752.25, -2940752.25)
New Position: (-2940752.25, 86727.8203125)
When the camera zooms in, and moves around I get these values
Previous Position:(-2940770.0, 86966.34375)
Position Change: (0.0, -0.0859375)
New Position: (-2940770.0, 86966.4296875)
For some reason when I am moving my finger across the screen, the touchesMoved function doesn't give a value for the change in position in the x-axis, and instead gives zero. Once I move my finger a certain amount left or right and it snaps to the next position.
I tried getting the location of the touch in the view (instead of the SKScene), and then multiplied it by the camera scale to get a more precise number to move the camera by. But for some reason the position of the camera refused to move until it was given a value big enough to snap left or right.
Also, the video only shows it jittering only along the x-axis. This is when the earth was almost directly left of the sun. When the earth completes 1/4th of its orbit and is above the sun, the jittering and movement issues will only happen along the y-axis. This also means that the jitteriness does not occur when the camera is near (0, 0).
I then tried scaling down the entire solar system because I thought the large numbers were causing the issue. But it still didn't fix it. Then I tried using an SKAction to move the camera but that did not work either.
Could this be an issue with using CGFloats. I thought maybe they are not giving precise enough value and instead rounding the position values of the camera.
Any clues or indications as to what this could be would be extremely helpful.
EDIT:
So it looks like this a problem when setting a CGPoint equal to an SKNode's position
func updatePos(delta:CGFloat, ct:CGFloat){
let t = CGFloat(Int(ct*10000) % Int(T*10000))/10000
let newPos = getPos(at: t)
self.prevPos = self.position
self.position = newPos
self.vel = calcVel()
if(self.name == "Earth"){
print("P1: " + newPos.debugDescription)
print("P2: " + self.position.debugDescription)
//self.position.
}
}
This prints:
P1: (-147027315091.13248, 4715744568.852638)
P2: (-147027312640.0, 4715744768.0)
P1: (-147027315075.2827, 4715745071.273114)
P2: (-147027312640.0, 4715745280.0)
P1: (-147027315059.33746, 4715745576.7201805)
P2: (-147027312640.0, 4715745792.0)
P1: (-147027315043.39218, 4715746082.167245)
P2: (-147027312640.0, 4715746304.0)
P1: (-147027315027.54242, 4715746584.587657)
P2: (-147027312640.0, 4715746816.0)
P1: (-147027315011.59717, 4715747090.034788)
P2: (-147027312640.0, 4715747328.0)
P1: (-147027314995.65192, 4715747595.481853)
P2: (-147027312640.0, 4715747840.0)
My calcPos() function will calculate a very accurate position, however when I set it equal to the the node's position it all of a sudden gets rounded. This definitely is the cause of the issue. Is there a way to fix or get around this?

SpriteKit move physics body on touch (air hockey game)

I'm trying to make an air hockey game using SpriteKit. I trying to make the pucks draggable but I can't make them continue to move after the touch has ended. Right now I am binding the touch and the puck and setting it's position when the touch moves.
using the physics system:
override func update(currentTime: NSTimeInterval) {
for (touch, node) in draggingNodes {
let targetPosition = touch.locationInNode(self)
let distance = hypot(node.position.x - targetPosition.x, node.position.y - targetPosition.y)
var damping = sqrt(distance * 100)
if (damping < 0) {
damping = 0.0
}
node.physicsBody!.linearDamping = damping
node.physicsBody!.angularDamping = damping
let translation = CGPointMake(targetPosition.x - node.position.x, targetPosition.y - node.position.y)
node.physicsBody!.velocity = CGVectorMake(translation.x * 100, translation.y * 100);
}
}
You're likely going to need to do a lot more reading. Either you'll use the physics system:
In which case you'll impart an impulse onto the puck on the touch end event, having calculated the speed based on a delta in position and delta in time from last frame to current frame (or some type of average over more than 1 frame).
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Physics/Physics.html
[OR]
You'll manually set velocity on the puck (not using the physics system), and then manually update the puck's position per frame based on that velocity, then recalculate its vector when it comes into contact with another object based on angle of of incidence.

How do I detect if there is a tap on right or left side of the iPhone screen?

I'm a fairly new beginner into the iOS world, so forgive me if I leave out some details or if I'm not being clear enough. I have a ball placed on the screen at the bottom and would like to know how to make it go left if the user taps on the left half of iPhone and go right if the user taps on the right half of the iPhone.
The code that I'm trying to make work is this:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationOfTouch(<#touchIndex: Int#>, inView: <#UIView?#>)
}
ball!.physicsBody!.applyImpulse(CGVectorMake(25.0, 40.0))
}
I know there is code missing, but I can't seem to understand how to approach it. Am I doing this right? I will deeply appreciate your help.
so I did figure out the code. This is what I did:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch = touches.first as! UITouch
var point = touch.locationInView(self.view)
if point.x < size.width / 2 {
ball!.physicsBody!.applyImpulse(CGVectorMake(-5.0, 10.0))
}
else {
ball!.physicsBody!.applyImpulse(CGVectorMake(5.0, 10.0))
}
}
Now, I'm coming across another problem, which might not be as complicated as I'm thinking to solve it. So, initially the ball is stationary when the app launches, and it goes in (-5,10) direction if tapped left, and (5,10) if tapped right. The problem is, when I tap right, when the app starts, it goes in (5,10) direction, but it doesn't go in left in the same direction. If I tap on right first when app launches and ball starts moving towards (5,10) direction, I want it to move left in the exact same direction from which it started while moving forward, almost in like a zig zag format. Something like this format /V, if you were to look at that in portrait view, except the line will be the ball going left and right. I hope it makes sense :)
I will keep trying to figure it out and hopefully have it figured out by the time you read it.
I'm also fairly new so I don't know of a more advanced solution.
What I would do is draw/add a line in the center of the screen. For example, I would use a SKSpriteNode and place it in the center of the screen.
var middleLine = SKSpriteNode(imageNamed: "CenterLine")
middleLine.alpha = 0 //you don't want the line to show
middleLine.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
This would place the line sprite at the center of the screen. And then, inside the touchesBegan, I would make an if statement concerning if the tap is less than the y coordinate of the center line or greater. If it's less, then the tap would be on the left, if it's greater than the y coordinate, the tap would be on the right.
You would need to make it return the location of the touch where the user tapped in order to compare.
EDIT: To make it easier, you don't even have to add the sprite, just compare the touch x and y to the CGRectGetMidX(self.frame) and CGRectGetMidY(self.frame)
I used this in my game when figuring out if an object was leaving the screen. I compared its x,y coordinates if it was greater than the coordinates inside the frame (or less than).

How to attach sprites that collide?

I essentially want the "sprites" to collide when they stick together. However, I don't want the "joint" to be rigid; I essentially want the sprites to be able to move around as long as they are in contact with each other. Imagine two circles connected, and you can move one circle around the other, as long as it remains in contact.
I found this question: How to make one body stick to another moving object in SpriteKit and a lot of other resources that explain how to make sprites stick upon collision, but they all use SKJoints, which are rigid are not really flexible.
I guess another way to phrase it would be to say that I want the sprites to stick, but I want them to be able to "slide" on each other.
Well, I can think of one workaround, but this wouldn't work with non-normal polygons.
Sticking (pun unintended) with your circles example, what if you lock the position of the circle?
let circle1 = center circle
let circle2 = movable circle
Knowing the width of both circles, you can place in the update function that the position should be exactly the distance of:
((circle1.frame.width / 2) + (circle2.frame.width / 2))
If you're up to it, here's some code to help you on your way.
override func update(currentTime: CFTimeInterval) {
{
let distance = hypotf(Float(circle1.position.x - circle2.position.x), Float(circle1.position.y - circle2.position.y))
//calculate circle distances from each other
let radius = ((circle1.frame.width / 2) + (circle2.frame.width / 2))
//distance of circle positions
if distance != radius
{
//if distance is less or more than radius
let pointA = circle1.position
let pointB = circle2.position
let pointC = CGPointMake(pointB.x + 2, pointB.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
//get angle of circles from each other using atan2
let vectorx = cos(angle_abc)
let vectory = sin(angle_abc)
//convert angle into vectors
let x = circle1.position.x + radius * vectorx
let y = circle1.position.y + radius * vectory
//get new coordinates from vector, radius and center circle position
circle2.position = CGPointMake(x, y)
//set new position
}
}
Well you need to write code to make sure the movable circle, is well movable.
But, this should work.
I haven't tested this yet though, and I haven't even learned geometry let alone trig in school yet.
If I'm reading your question as you intended it, you can still use joints- just create actions with Inverse Kinematic constraints that allow rotation and translation around the contacting circles' joint.
https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKAction_Ref/index.html#//apple_ref/doc/uid/TP40013017-CH1-SW72

Enforce single-axis velocity

I'm trying to build a game, using SpriteKit, in which there's a ball that bounces up and down. Now I want to let the player control the balls movement in the X axis and let the physics engine control the Y velocity.
For example, when the ball hits a corner it starts moving sideways on it's own. I would like it to bounce of the corner and then quickly stabilize and stop moving side-ways. Is there anyway of doing this without trying to counteract any sideways movement by applying an impulse? Would it be easier to just manually control the ball's movement up and down?
I've tried applying a counteracting force without much success (the ball freaks out):
override func update(currentTime: NSTimeInterval) {
let ballDx = ball?.physicsBody?.velocity.dx
if let ballVelocityX = ballDx {
if ballVelocityX != 0 {
ball?.physicsBody?.applyForce(CGVectorMake(ballVelocityX * -1, 0))
}
}
}
Sounds like you need to apply linear damping in the x direction. Here's an example of how to do that:
// Adjust this value as needed. It should be in [0,1], where a value of 1 will
// have no effect on the ball and a value of 0 will stop the ball immediately.
let xAlpha:CGFloat = 0.95
override func update(currentTime: CFTimeInterval) {
/* Apply damping only in x */
let dx = sprite.physicsBody!.velocity.dx * xAlpha
let dy = sprite.physicsBody!.velocity.dy
sprite.physicsBody?.velocity = CGVectorMake(dx, dy)
}

Resources