Move node left and right without snapping to touch - swift

I have a sprite node on the screen, that the player is able to move left and right. I know how to make it move left and right using touchesMoved, however, if the player touches a location the node snaps to that location.
For example, if the node was on the left side of the screen, and the player touched the right side of the screen, the node would immediately move to the touch location. I don't want this behavior. I only want to the node to move when the player is moving their finger left or right, not when they touch a location. Below is the code I currently use. There isn't any code that would affect the playerNode x position in the touchesBegan or touchesEnded function.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches
{
if game.state == .game
{
let position = touch.location(in: playableArea)
game.playerNode.position.x = position.x
}
}
}
How can I stop this behavior from happening, and only have the node move when the players finger is moving left or right?

I ended up resolving my issue. I knew that I had to essentially move the node relative to the touch location, but I wasn't sure how to do that. This is how I got it working.
First I created a variable that would be equal to the players touch location. This variable gets set in touchesBegan.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
if game.state == .game
{
let position = touch.location(in: playableArea)
playerTouchPosition = position
}
}
I use this variable as the starting point of the touch. When the player moves their finger, it calculates how much the player moved their finger relative to the starting point. For example, if the player touched at the X value of 100, and moved their finger to the X value of 120, it would calculate that the player moved their finger by 20. After finding that value, I set the playerTouchPosition to the position of touchesMoved. This is to make the next move be relative to the previous touch. Then I move the players node by the amount the player moved their finger.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches
{
if game.state == .game
{
let position = touch.location(in: playableArea)
let newX : CGFloat!
if position.x > playerTouchPosition.x
{
newX = position.x - playerTouchPosition.x
}else
{
newX = -(playerTouchPosition.x - position.x)
}
playerTouchPosition = position
game.playerNode.position.x = game.playerNode.position.x + newX
}
}
}
This stopped the players node from immediately moving to where ever the touch locations X value was.

Related

Swift SpriteKit add physics to user-drawn line

I want to create a game in which the player draws a line with physics, similar to the game Happy Glass.
What I have tried:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let location = touch.location(in: self)
touchPosition = location
}
}
func draw(pos: CGPoint){
let node = SKSpriteNode(imageNamed: "circle")
node.position = CGPoint(x: pos.x, y: pos.y)
node.size = CGSize(width: 10, height: 10)
node.physicsBody = SKPhysicsBody(circleOfRadius: node.frame.width / 2)
//set up the physics
self.addChild(node)
}
override func didSimulatePhysics() {
if touching{
draw(pos: touchPosition)
}
}
So I basically change the touchPosition variable every time a touch is moved(when the user drags their finger across the screen) then I have didSimulatePhysics as repeating timer to add small circles at the touchPosition with physics on them.
The Problem:
This all works. The only problem is that they are too spread out. I want it to be a continuous line but the touchesMoved method is not recognizing enough touches so it looks like this.
Is there a way to recognize touches more frequently so I can fill an entire line or some way to possibly do this with a CGPath? I just can't have these gaps in between these points basically because I can't have other nodes with physicBodies falling between the cracks.
The other thing is that the points are constantly moving off the screen with a SKAction, so I can just draw a CGPath line from the current touch to the last touch because the point from the last touch will have moved slightly so i'm kind of confused in what to do and any suggestions would be helpful.
Thanks

spritekit my player node doesn't move according to my touches?

I've been trying to figure out this bug in my code that whenever my player node is to the most left/right side of my screen it tends to behave weirdly i.e. it'd go off to the right/left even though when i've touched left/right
Here's my code:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let action = SKAction.moveTo(x:location.x, duration: 0.45)
if location.x > (player?.position.x)! {
player?.run(action)
moveLeft = false
} else {
player?.run(action)
moveLeft = true
}
}
canMove = true
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
canMove = false
}
Here's the moveleft in Player.swift
// Move function
func move(left: Bool) {
if left {
position.x -= 10
// Set boundary to minX so it wont go below
if position.x < minX {
position.x = minX
}
} else {
position.x += 10
// Set boundary to maxX so player cannot move over the right boundary
if position.x > maxX {
position.x = maxX
}
}
}
1) Your actions are stacking on top of each other, so every time you touch, you add a new action. If you touched left, right, left your character would act erratic. To combat this, assign a key player?.run(action, withKey:"moving") (Do not assign different keys for left and right, use only 1 key for moving in any direction)
2)You are touching based on left or right of the player, when the player is all the way to the left, even touching the left side of the screen will move him right. You may want to switch this to simply if x is negative go left, if x is positive go right (Assuming the center of your scene is (0,0)
3)Not sure when move(left:Bool) is getting called, I am not seeing it in the code.
This is not even needed, I would avoid having this bool check all together.

Swift touch recognition not recognising one half of screen being touched

I'm trying to detect whether the user is touching either the left or right hand side of the screen in an SKScene.
I've put the following code together however it is only outputting "Left" regardless of where is touched.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if(location.x < self.frame.size.width/2){
print("Left")
}
else if(location.x > self.frame.size.width/2){
print("Right")
}
}
}
If you are using the default scene for a SpriteKit project, the anchor point is (0.5, 0.5) which places the origin of the scene to the center. If you want to test the touch position is left or right, you should change the condition checking to (this will work regardless of anchor point):
for touch in touches {
let location = touch.location(in: self)
if(location.x < frame.midX){
print("Left")
}
else if(location.x > frame.midX){
print("Right")
}
}
More details at: SKScene

Spritekit move camera only if not dragging from sprite

In a game I'm building a user is able to move the camera around by dragging around the screen similar to a scrollview. This is all handled in the main scene file by the touchesBegan and touchesMoved method. However I also want the ability for a user to drag from an SKSpriteNode (not actually moving it) to another SKSpriteNode - I then perform an action based on the 2 nodes. I can do both of these fine separately. But when a user is dragging from one node to another, I don't want the camera to pan.
Again I've managed to resolve this using a boolean flag that's set on touchesBegan if they have touched a node. However the screen then doesn't pan if a user starts touching on one of the sprites but finishes off of one e.g they actually quickly performed a pan that happened to start on a node. Does anyone have any good solutions for this?
One thought I had was to store all the touch events in touchesMoved, and then loop through them in touchesEnded performing the same movement logic, provided they didn't start and end on a sprite. e.g
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
movingTouches.append(touch)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if startedOnSprite && !endedOnSprite || !(startedOnSprite && endedOnSprite) {
let location = movingTouches.last().location(in: self)
let previousLocation = movingTouches.first().location(in: self)
let deltaY = location.y - previousLocation.y
let deltax = location.x - previousLocation.x
gameCamera?.position.y -= deltaY
view.frame.size.height
gameCamera?.position.y = (gameCamera?.position.y)! > viewHeight/2 ? viewHeight/2 : (gameCamera?.position.y)!
gameCamera?.position.y = (gameCamera?.position.y)! < -(viewHeight/2) ? -(viewHeight/2) : (gameCamera?.position.y)!
gameCamera?.position.x -= deltax
gameCamera?.position.x = (gameCamera?.position.x)! < -(viewWidth/2) ? -(viewWidth/2) : (gameCamera?.position.x)!
gameCamera?.position.x = (gameCamera?.position.x)! > (viewWidth/2) ? (viewWidth/2) : (gameCamera?.position.x)!
}
movingTouches = [UITouch]()
endedOnSprite = false
startedOnSprite = false
}
To complicate matters I also want the Sprites to have a tap event on them as well as dragging. I'm struggling to find a good way to do all this in SpriteKit
Unfortunately the above isn't very smooth at all, and in think it appears to not even scroll the distance I would expect either (the exact same code does scroll correctly if I'm not bothered about whether or not it starts on a Sprite)
So to be clear, I want scrolling behaviour, provided a user isn't actually using a finger to 'draw' from one sprite to another
Gesture recognisors on your scene would do what you need, keeping taps and swipes separated.

How to recognize continuous touch in Swift?

How can I recognize continuous user touch in Swift code? By continuous I mean that the user has her finger on the screen. I would like to move a sprite kit node to the direction of user's touch for as long as the user is touching screen.
The basic steps
Store the location of the touch events (touchesBegan/touchesMoved)
Move sprite node toward that location (update)
Stop moving the node when touch is no longer detected (touchesEnded)
Here's an example of how to do that
Xcode 8
let sprite = SKSpriteNode(color: SKColor.white, size: CGSize(width:32, height:32))
var touched:Bool = false
var location = CGPoint.zero
override func didMove(to view: SKView) {
/* Add a sprite to the scene */
sprite.position = CGPoint(x:0, y:0)
self.addChild(sprite)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touched = true
for touch in touches {
location = touch.location(in:self)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
location = touch.location(in: self)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// Stop node from moving to touch
touched = false
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if (touched) {
moveNodeToLocation()
}
}
// Move the node to the location of the touch
func moveNodeToLocation() {
// Compute vector components in direction of the touch
var dx = location.x - sprite.position.x
var dy = location.y - sprite.position.y
// How fast to move the node. Adjust this as needed
let speed:CGFloat = 0.25
// Scale vector
dx = dx * speed
dy = dy * speed
sprite.position = CGPoint(x:sprite.position.x+dx, y:sprite.position.y+dy)
}
Xcode 7
let sprite = SKSpriteNode(color: SKColor.whiteColor(), size: CGSizeMake(32, 32))
var touched:Bool = false
var location = CGPointMake(0, 0)
override func didMoveToView(view: SKView) {
self.scaleMode = .ResizeFill
/* Add a sprite to the scene */
sprite.position = CGPointMake(100, 100)
self.addChild(sprite)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Start moving node to touch location */
touched = true
for touch in touches {
location = touch.locationInNode(self)
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Update to new touch location */
for touch in touches {
location = touch.locationInNode(self)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Stop node from moving to touch
touched = false
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (touched) {
moveNodeToLocation()
}
}
// Move the node to the location of the touch
func moveNodeToLocation() {
// How fast to move the node
let speed:CGFloat = 0.25
// Compute vector components in direction of the touch
var dx = location.x - sprite.position.x
var dy = location.y - sprite.position.y
// Scale vector
dx = dx * speed
dy = dy * speed
sprite.position = CGPointMake(sprite.position.x+dx, sprite.position.y+dy)
}
The most difficult thing about this process is tracking single touches within a multitouch environment. The issue with the "simple" solution to this (i.e., turn "istouched" on in touchesBegan and turn it off in touchesEnded) is that if the user touches another finger on the screen and then lifts it, it will cancel the first touch's actions.
To make this bulletproof, you need to track individual touches over their lifetime. When the first touch occurs, you save the location of that touch and move the object towards that location. Any further touches should be compared to the first touch, and should be ignored if they aren't the first touch. This approach also allows you to handle multitouch, where the object could be made to move towards any finger currently on the screen, and then move to the next finger if the first one is lifted, and so on.
It's important to note that UITouch objects are constant across touchesBegan, touchesMoved, and touchesEnded. You can think of a UITouch object as being created in touchesBegan, altered in touchesMoved, and destroyed in touchesEnded. You can track the phase of a touch over the course of its life by saving a reference to the touch object to a dictionary or an array as it is created in touchesBegan, then in touchesMoved you can check the new location of any existing touches and alter the object's course if the user moves their finger (you can apply tolerances to prevent jitter, e.g., if the x/y distance is less than some tolerance, don't alter the course). In touchesEnded you can check if the touch in focus is the one that ended, and cancel the object's movement, or set it to move towards any other touch that is still occurring. This is important, as if you just check for any old touch object ending, this will cancel other touches as well, which can produce unexpected results.
This article is in Obj-C, but the code is easily ported to Swift and shows you what you need to do, just check out the stuff under "Handling a Complex Multitouch Sequence": https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/multitouch_background/multitouch_background.html
Below is the code to drag the node around on X position (left and right), it is very easy to add Y position and do the same thing.
let item = SKSpriteNode(imageNamed: "xx")
var itemXposition = 50
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// updates itemXposition variable on every touch
for touch in touches {
let location = touch.location(in: self)
itemXposition = Int(location.x)
}
}
// this function is called for each frame render, updates the position on view
override func update(_ currentTime: TimeInterval) {
spaceShip.position = CGPoint(x: self.itemXposition , y: 50 )
}