Moving SKSpriteNode over screen - swift

I want to move an SKSpriteNode over the screen with touch. The issue I have is that if I lock onto one sprite and then my finger drags it over another sprite the first one is let go and my finger starts to drag the second one. But once I touch the first sprite this is the only one I want to move.
There must be a simple way to resolve this?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
let touch: UITouch = touches.first as UITouch!
touchLocation = touch.locationInNode(self)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch: UITouch = touches.first as UITouch!
let newTouchLocation = touch.locationInNode(self)
let targetNode = self.nodeAtPoint(touchLocation)
if targetNode.physicsBody!.categoryBitMask != PhysicsCategory.Ball {
targetNode.runAction(SKAction.moveBy(CGVector(point: newTouchLocation - touchLocation), duration: 0.0))
touchLocation = newTouchLocation
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
//I don't do anything with this yet
}

This is happening because your touchesMoved function is running every time your finger moves and if your finger moves over a new node, that node will be assigned to targetNode. You can fix this by changing this line:
let targetNode = self.nodeAtPoint(touchLocation)
To this:
let targetNode = self.nodeAtPoint(touch)
This way the node at the first touch will be the one that follows your finger and not the one at the last touch.

Thanks to TheCodeComposer, I found the solution:
var targetNode: SKNode!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
let touch: UITouch = touches.first as UITouch!
touchLocation = touch.locationInNode(self)
targetNode = self.nodeAtPoint(touchLocation)
objectTouched(touch.locationInNode(self))
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch: UITouch = touches.first as UITouch!
let newTouchLocation = touch.locationInNode(self)
// let targetNode = self.nodeAtPoint(touchLocation)
if targetNode.physicsBody!.categoryBitMask != PhysicsCategory.Ball {
targetNode.runAction(SKAction.moveBy(CGVector(point: newTouchLocation - touchLocation), duration: 0.0))
touchLocation = newTouchLocation
}
}
I only define
targetNode
once and then continue to use it instead of redefining it in touchesMoved:

Related

How to detect cgPath in cgRect

I can detect only start point(touchBegan) that contain in c1 and end point(touchEnded) that contain in c2 but I can't detect path between them in c3, How should I do ?
Application image
override func layoutSubviews() {
self.clipsToBounds = true
self.isMultipleTouchEnabled = false
self.contentMode = .scaleToFill
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
swiped = false
lastPoint = touch?.location(in: self)
firstPoint = lastPoint
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
swiped = true
let currentPoint = touch?.location(in: self)
lastPoint = currentPoint
linePoint = lastPoint
drawShapeLayer(from: lastPoint, to: currentPoint!)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
drawShapeLayer(from: lastPoint, to: lastPoint!)
}
if c1.contains(firstPoint) && c3.contains(linePoint) && c2.contains(lastPoint){
AlertView.instance.showAlert(title: "Hooray!", message: "You made it!", alertType : .success)
}
else {
AlertView.instance.showAlert(title: "Oops!", message: "You've almost got it.", alertType : .failure)
}
}
}
As per my comments above, I think the there is a logic error in touchesMoved and you need to reorder the code if you are trying to draw a three point line, and then later check that each point is in a specific view area.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
swiped = true
currentPoint = touch.location(in: self)
drawShapeLayer(from: lastPoint, to: currentPoint)
lastPoint = currentPoint
linePoint = currentPoint
}
}
(Also safely unwrapped touch to remove the optional and avoid crashes in the v unlikely scenario where touches.first is nil)

How do I drag and drop a sprite in Swift 3.0?

All i'm trying to do is be able to drag and drop a sprite across the screen. I've tried the following code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
if ball.containsPoint(location) {
ball.position = location
}
}
}
This code does work, however, when I drag the ball quite fast, I guess it detects that the "ball" no longer contains the point "location" and the ball stops, meaning I have pick the ball up again. I want the ball to be able to respond to my touches quickly, so that the ball wont stop moving. How would I do this?
This is the correct way to do it in Sprite Kit. Like I said in my comment, you need to assign the moving node to an activate state, in this case I use a variable called movableNode to act is my activate state. When you touch the ball, it becomes activated by assigning it to movableNode. Then as you drag your finger, movableNode will go with the drag, Then once you release, you enter a deactivate state by setting movableNode to nil. Note that this code will only work on single touch applications, if you want to handle multitouch, then you need to track which touch is doing the dragging.
var movableNode : SKNode?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
if ball.containsPoint(location) {
movableNode = ball
movableNode!.position = location
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first where movableNode != nil {
movableNode!.position = touch.locationInNode(self)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first where movableNode != nil {
movableNode!.position = touch.locationInNode(self)
movableNode = nil
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
movableNode = nil
}
}
KnightOFDragon solution works just fine. I just added few lines if you don't want to move sprite centre position to position where your finger touched the screen and you would like to move sprite from its centre original position.
var movableNode : SKNode?
var ballStartX: CGFloat = 0.0
var ballStartY: CGFloat = 0.0
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
if (map?.contains(location))! {
movableNode = ball
ballStartX = (movableNode?.position.x)! - location.x // Location X of your sprite when touch started
ballStartY = (movableNode?.position.y)! - location.y // Location Y of your sprite when touch started
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, movableNode != nil {
let location = touch.location(in: self)
movableNode!.position = CGPoint(x: location.x+ballStartX, y: location.y+ballStartY) // Move node around with distance to your finger
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let _ = touches.first, movableNode != nil {
movableNode = nil
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
movableNode = nil
}
I have an implementation where I've subclassed a UIImageView and called it a "DraggableImage"
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
originalPosition = self.center
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self.superview)
self.center = CGPoint(x: position.x, y: position.y)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.center = originalPosition
}

How can i alter my program to allow this object to only react to a single touch?

I am making a basketball game. My problem is when I shoot the ball, i can then touch the ball in mid-air again, which i do not want. Once the user shoots the ball I do not want touch to have an effect. Here is my code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?
{
touching = false
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if ball.frame.contains(location){
touchingPoint = location
touching = true
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
touchingPoint = location
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if touching{
let dt:CGFloat = 8.25/29.5
let distance = CGVector(dx: touchingPoint.x-ball.position.x, dy: touchingPoint.y-ball.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
ball.physicsBody!.velocity=velocity
}
}
What can i add to my program so that a touch won't have any effect once the ball has been shot?
after you shoot the ball disable userInteraction
ball.userInteractionEnabled = false
then enable it again when you want them to be able to touch it

Move skspriteNode on swipe actions

What I want to do is making a SkspiteNode move from left to right and back. This happens on touch.
I have this code:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
touchLocation = touch.locationInNode(self)
mainGuy.position.x = touchLocation.x;
}
}
This works. But when I have/move my finger at the right side of the screen and the mainGuy is at the left side of the screen it moves to my finger. This is not what I want to happen.
When I move my finger over the screen it needs to 'follow' my finger from the current position of the mainGuy.
How can I do this?
Do something like:
var touchesBeganPosition: CGPoint?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
touchesBeganPosition = touches.first?.locationInNode(self)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
touchLocation = touch.locationInNode(self)
if let touchesBeganPosition = touchesBeganPosition {
mainGuy.position.x = touchLocation.x - touchesBeganPosition.x
}
}
}
This is untested of of course, but should be enough to get the general idea.

Using Touchesbegan once

I have a touchesbegan and a touchesmoved that move a UIImageView around the screen. But I want to make it so I can't start moving around the screen then go to the opposite side using touchesbegan. I don't want to be able to just tap my way to other side. Is that even possible? Here's my code:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
var touch : UITouch! = touches.anyObject() as UITouch
location = touch.locationInView(self.view)
Player.center = location
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
var touch : UITouch! = touches.anyObject() as UITouch
location = touch.locationInView(self.view)
Player.center = location
}
Thanks.