I have a scene that is larger than the size of the device screen, and there is a camera that follows the player around the scene. I want to move the player to the right if the user touches on the right side of the screen and to the left if they touch on the left side. But since my scene is larger than my phone screen, in the touchesBegan method there are problems when the user gets to the far edges of the scene. If the player goes to the left of the scene, even if the user touches on the right side of the screen it still registers as being a "left touch" because the entirety of the phone screen is filled only by the left side of the scene.
Here is what I have in my touchesBegan method:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isTouching = true
let touch = touches.first
if let location = touch?.location(in: self) {
print(location)
if location.x < 0 {
player.physicsBody?.applyImpulse(CGVector(dx: -8, dy: 0))
} else {
player.physicsBody?.applyImpulse(CGVector(dx: 8, dy: 0))
}
}
}
I tried changing the touch?.location(in: self) part but that seems to have no effect. Is there a way to do record where the user touches relative to the bounds of the SCREEN, not the scene?
You can use UIScreen.main.focusedView instead of self.
I want to check the area of User's touch using touch.location()
how can i divide circe with equal 10 parts and user detect area of touch's position
if user touch that area then i want display "1" at label
(image have 4 pieces but I want 10 pieces)
Following example divide a view's touch area into sectors like cutting pizzas pieces. The view's center point is considered as an original point of the coordinate. Then we calculate the radians of the line between touch point and center point to find out which piece it's in.
class ExampleView: UIView {
let numberOfPieces = 10
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first
if let location = touch?.location(in: self) {
let centerPoint = CGPoint(x:self.bounds.midX,y:self.bounds.midY)
let radians = atan2(location.y - centerPoint.y,location.x - centerPoint.x)
var degrees = radians * (180.0 / CGFloat.pi) + 180.0 // adding 180 for making the first piece at left side.
degrees = (degrees > 0.0 ? degrees : (360 + degrees))
let piece = Int(degrees / (360.0 / CGFloat(numberOfPieces))) + 1
print("piece \(piece) touched.")
}
}
}
The default UIView's coordinate system is:
270
180 0
90
So we add 180 degree to rotate it to:
90
0 180
270
Then the first piece is at left side as needed in the diagram.
I have this circle and it can rotate 360 degrees to the left and right when the user has their finger on the node and is rotating. I need to figure out how to make an if statement so when it rotates to the left I can add an action and when it rotates to the right I can add another action. Here is the code I have:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node.name == "rotatecircle" {
//lets user rotate circle like a knob when there is one finger on node.
let dy = circle.position.y - location.y
let dx = circle.position.x - location.x
let angle2 = atan2(dy, dx)
circle.zRotation = angle2
}
}
}
One approach would be to compare the angle2 value to the angle value from the previous touch. You can then create an if statement that does what you want based on the direction of the rotation.
Some pseudo-code:
In your class:
var previousAngle:Double
In touchesMoved
let delta = (angle2 - previousAngle) mod M_PI
if delta > 0 {
// do action 1
}
else {
// do action 2
}
previousAngle = angle2
You will need some logic to check if this is the first touch. In that case you don't have a previousAngle yet, so you don't want to take an action.
Context: I'm using Xcode, Swift, and SpriteKit
I've created a separate class file "Ship.swift" and within it I've got:
let RAD_PER_SEC: CGFloat = 0.1
var direction = 0
//Updates object value for determining how much to rotate object
func spin {
}
Ideally I want to make an app sort of like the old game Astroids. For now, I just a ship that rotates in the center of the screen and points towards the touch-contact point.
If you want to rotate node towards the touch location you need to know the angle to rotate to. Because in Spritekit angles are measured in radians there are some conversions that have to be done:
let Pi = CGFloat(M_PI)
let DegreesToRadians = Pi / 180
After that you have to use atan2() to calculate the angle...Here is simple example which rotates sprite using touchesMoved method:
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
let curTouch = touches.anyObject() as UITouch
let curPoint = curTouch.locationInNode(self)
let deltaX = self.yourNode.position.x - curPoint.x
let deltaY = self.yourNode.position.y - curPoint.y
let angle = atan2(deltaY, deltaX)
self.yourNode.zRotation = angle + 90 * DegreesToRadians
}
As of iOS8 Apple added SKConstraints which can be added to SKNodes. Here's the documentation: https://developer.apple.com/library/mac/documentation/SpriteKit/Reference/SKConstraint_Ref/index.html#//apple_ref/occ/cl/SKConstraint
In your case you'll want to create the constraint when the user touches the screen - SKConstraint.orientToPoint(touchLocation, offset: SKRange.rangeWithConstantValue(0))
And the add it to your sprite node using the constraints property.
UPDATE: I have solved the problem, and figured out a more simplified way to do this then the answer provided. My solution was to make the velocity of the SPACESHIP equal the distance it was from my finger touch. For faster movement, you can multiply this velocity by a constant. In this case, I used 16. I also got rid of setting lastTouch to nil in the touchesEnd event. That way, the ship will still stop even when I release my finger.
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if let touch = lastTouch {
myShip.physicsBody.velocity = CGVector(dx: (lastTouch!.x - myShip.position.x) * 16, dy: 0)
}
}
===============================
I have a SPACESHIP node with movement restricted to the X-Axis. When the user PRESSES and HOLDS somewhere on the screen, I want the SPACESHIP to be able to move to the finger's x-coordinate, and not stop moving toward the finger until the finger is RELEASED. If the SPACESHIP is close to the users finger and the users finger is still pressed down, I want it to gradually slow down and stop. I also want this smooth motion to be applied when the SPACESHIP changes direction, starts, and stops.
I am trying to figure out the best way to do this.
So far, I have created the node and it moves correctly, but there is a problem: If I press on the screen and hold down, the ship will eventually cross over my finger and keep moving. This is because the logic to change direction of the ship is only triggered if I move my finger. So essentially, moving my finger over the ship to change the ships' direction works, but if the ship crosses over my still finger, it does't change direction
I need the SPACESHIP node to recognize when it has crossed over my still finger, and either change its direction or stop based on how close it is to my finger.
Here is the relevant code:
Part 1: When the user presses down, find out where the touch is coming from and move myShip (SPACESHIP) accordingly using velocity
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
let touch = touches.anyObject() as UITouch
let touchLocation = touch.locationInNode(self)
if (touchLocation.x < myShip.position.x) {
myShip.xVelocity = -200
} else {
myShip.xVelocity = 200
}
}
Part 2 When the user moves their finger, trigger an event that checks to see if the finger has now moved to the other side of the ship. If so, change direction of the ship.
override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
let touch = touches.anyObject() as UITouch
let touchLocation = touch.locationInNode(self)
//distanceToShip value will eventually be used to figure out when to stop the ship
let xDist: CGFloat = (touchLocation.x - myShip.position.x)
let yDist: CGFloat = (touchLocation.y - myShip.position.y)
let distanceToShip: CGFloat = sqrt((xDist * xDist) + (yDist * yDist))
if (myShip.position.x < touchLocation.x) && (shipLeft == false) {
shipLeft = true
myShip.xVelocity = 200
}
if (myShip.position.x > touchLocation.x) && (shipLeft == true) {
shipLeft = false
myShip.xVelocity = -200
}
}
Part 3 When the user releases their finger from the screen, I want the ship to stop moving.
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
myShip.xVelocity = 0
}
Part 4 Update event that changes the Ship's position
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
let rate: CGFloat = 0.5; //Controls rate of motion. 1.0 instantaneous, 0.0 none.
let relativeVelocity: CGVector = CGVector(dx:myShip.xVelocity - myShip.physicsBody.velocity.dx, dy:0);
myShip.physicsBody.velocity = CGVector(dx:myShip.physicsBody.velocity.dx + relativeVelocity.dx*rate, dy:0);
Thanks for reading, and looking forward to a response!
You can save yourself a lot of trouble by using: myShip.physicsBody.applyImpluse(vector). It works by acting as if you gave myShip a push in the direction vector points. If you calculate vector as the x distance from your last touch location to myShip, then it'll accelerate, decelerate, change direction, etc. pretty close to the way you're describing because it'll be giving it little pushes in the right direction on each update.
Basically you store the last touch location then, in your update function, you calculate the CGVector pointing from myShip to lastTouch and apply that as an impulse to your physics body.
Something like:
var lastTouch: CGPoint? = nil
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as UITouch
let touchLocation = touch.locationInNode(self)
lastTouch = touchLocation
}
override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
let touch = touches.anyObject() as UITouch
let touchLocation = touch.locationInNode(self)
lastTouch = touchLocation
}
// Be sure to clear lastTouch when touches end so that the impulses stop being applies
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
lastTouch = nil
}
override func update(currentTime: CFTimeInterval) {
// Only add an impulse if there's a lastTouch stored
if let touch = lastTouch {
let impulseVector = CGVector(touch.x - myShip.position.x, 0)
// If myShip starts moving too fast or too slow, you can multiply impulseVector by a constant or clamp its range
myShip.physicsBody.applyImpluse(impulseVector)
}
}
You'll also probably want to play with the linearDamping and angularDamping values on myShip.physicsBody. They'll help determine how fast myShip accelerates and decelerates.
I maxed out the values at 1.0 in my app:
myShip.physicsBody.linearDamping = 1.0
myShip.physicsBody.angularDamping = 1.0
If myShip doesn't stop fast enough for you, you can also try applying some breaking in your update function:
override func update(currentTime: CFTimeInterval) {
// Only add an impulse if there's a lastTouch stored
if let touch = lastTouch {
let impulseVector = CGVector(touch.x - myShip.position.x, 0)
// If myShip starts moving too fast or too slow, you can multiply impulseVector by a constant or clamp its range
myShip.physicsBody.applyImpluse(impulseVector)
} else if !myShip.physicsBody.resting {
// Adjust the -0.5 constant accordingly
let impulseVector = CGVector(myShip.physicsBody.velocity.dx * -0.5, 0)
myShip.physicsBody.applyImpulse(impulseVector)
}
}
For 2017 here's the easy way to do what is explained in the correct answer here.
There's no need to store the previous position, it is given to you...
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let t: UITouch = touches.first! as UITouch
let l = t.location(in: parent!)
let prev = t.previousLocation(in: parent!)
let delta = (l - prev).vector
physicsBody!.applyImpulse(delta)
}
That's it.
Two notes. (A) properly you should divide the delta distance by the deltaTime to get the correct impulse. If you're a hobbyist really just multiply by "about 100" and you'll be fine. (B) note that of course you will need an extension or function to convert CGPoint to CGVector, it's impossible to do anything without that.
In your thuchesBegan and touchesMoved store the touch location as the "target". In the update then check the position of your ship and reset the xVelocity to 0 if the ship has reached/passed the target.
Since you are only interested in the x coordinate you could also store just touchLocation.x. You can also reverse the velocity but I think that would look strange. Note that if the user moves the finger again, your ship will start moving again because the touchMoved will be triggered again.
On a side note, within touchesMoved you are also setting the shipLeft property but this is not set in your touchesBegan. If this property is used elsewhere you should sync its use.