So I already have gesture recongizers implemented into my game to detect the movement of my player but have found them to be not giving me the result I want, so I am looking at making the swipe gesture in the touches method and the taps also in the touches method. I have managed to make the tap functionality work in the touches method but I'm unable to implement the ability for swiping in the touches method and I can't seem to find a tutorial on how to do this. My code below shows the touches methods I'm using to try and achieve this:
class GameScene: SKScene {
var touchOrigin = CGPoint()
var player = SKSpriteNode()
override func didMove(to view: SKView) {
backgroundColor = .black
player = SKSpriteNode(texture: nil, color: .orange, size: CGSize(width: 50, height: 50))
player.position = CGPoint(x: 0, y: 0)
addChild(player)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
var currentTouchPosition = touch.location(in: self)
touchOrigin = touch.location(in: self)
if (Int(touchOrigin.x) > Int(currentTouchPosition.x)) {
player.position.x -= 50
} else if (Int(touchOrigin.x) < Int(currentTouchPosition.x)) {
player.position.x += 50
}
if touch.tapCount == 1 {
player.position.y += 50 //replace this with function :)
} else if touch.tapCount >= 2 {
player.position.y += 150 // change to run shield effect :)
}
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
How can I make the touches method recognise a swipe gesture in a certain direction? and if they swipe in a direction and not take their finger off the screen and swipe back to the origin point in one motion how can i make it so it is then recognised as a tap instead?
Here's an example of how to detect a swipe gesture.
First, define instance variables to store the starting location and time
var start:(location:CGPoint, time:TimeInterval)?
and define parameters of the swipe gesture. Adjust these accordingly:
let minDistance:CGFloat = 25
let minSpeed:CGFloat = 1000
let maxSpeed:CGFloat = 6000
In touchesBegan, save the location/time of each new touch event
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
start = (touch.location(in:self), touch.timestamp)
}
}
In touchesEnded, determine if the user's gesture was a swipe by comparing the distance and speed of the gesture. The tests for diagonal swipes are optional.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
var swiped = false
if let touch = touches.first, let startTime = self.start?.time,
let startLocation = self.start?.location {
let location = touch.location(in:self)
let dx = location.x - startLocation.x
let dy = location.y - startLocation.y
let distance = sqrt(dx*dx+dy*dy)
// Check if the user's finger moved a minimum distance
if distance > minDistance {
let deltaTime = CGFloat(touch.timestamp - startTime)
let speed = distance / deltaTime
// Check if the speed was consistent with a swipe
if speed >= minSpeed && speed <= maxSpeed {
// Determine the direction of the swipe
let x = abs(dx/distance) > 0.4 ? Int(sign(Float(dx))) : 0
let y = abs(dy/distance) > 0.4 ? Int(sign(Float(dy))) : 0
swiped = true
switch (x,y) {
case (0,1):
print("swiped up")
case (0,-1):
print("swiped down")
case (-1,0):
print("swiped left")
case (1,0):
print("swiped right")
case (1,1):
print("swiped diag up-right")
case (-1,-1):
print("swiped diag down-left")
case (-1,1):
print("swiped diag up-left")
case (1,-1):
print("swiped diag down-right")
default:
swiped = false
break
}
}
}
}
start = nil
if !swiped {
// Process non-swipes (taps, etc.)
print("not a swipe")
}
}
Related
I have a sprite node in game that when touched makes an action of the node being pressed and when the touches began event is called and then goes back to the normal size when the touches ended event is called. my problem is when i press down on the node and then move my finger outside of the node it doesn't go back to its original size after I take my finger off the screen.
I tried using multiple things in the touches moved section of the code to try and get it to go back to its original size after i've moved my finger outside of the node while holding the touch down but it didn't work. My code is below
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let node = self.atPoint(touch.location(in: self))
let pushdown = SKAction.scale(to: 0.8, duration: 0.1)
if node == mainMenu.settingsButton {
node.run(pushdown)
} else if node == mainMenu.viewMapButton {
node.run(pushdown)
}else if node == mainMenu.shopButton {
node.run(pushdown)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
var location: CGPoint = touch.location(in: self)
let node: SKNode = atPoint(location)
let pushUp = SKAction.scale(to: 1.0, duration: 0.2)
if node != mainMenu.settingsButton {
//node.run(pushUp)
} else if touch.phase == .moved || touch.phase == .cancelled {
node.run(pushUp)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let node = self.atPoint(touch.location(in: self))
let pushUp = SKAction.scale(to: 1.0, duration: 0.2)
if node == mainMenu.settingsButton {
node.run(pushUp)
//Run Sound Here
let scene = SettingsMenu(fileNamed:"SettingsMenu")
scene?.scaleMode = .aspectFill
//scene?.backgroundColor = UIColor.lightGray
let transition = SKTransition.crossFade(withDuration: 0.5)
self.scene?.view?.presentScene(scene!, transition: transition)
} else if node == mainMenu.viewMapButton {
node.run(pushUp)
}
}
How can i get it to go back to the original size after i've moved my finger outside of the nodes location while holding the touch down?
In touchesBegan, touchesMoved, touchesEnded you are referring always the current node at point. let node = self.atPoint(touch.location(in: self))
Why not to keep a reference to the initial node which was detected in touchesBegan to scale it back if the current node atPoint is not equal to the initial one. I assume this would solve your issue.
EDIT:
To capture the node on first touch ...
private var currentTouchedNode : SKNode?
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let touch: UITouch = touches.first!
currentTouchedNode = self.atPoint(touch.location(in: self))
let pushdown = SKAction.scale(to: 0.8, duration: 0.1)
if currentTouchedNode == mainMenu.settingsButton {
currentTouchedNode.run(pushdown)
} else if currentTouchedNode == mainMenu.viewMapButton {
currentTouchedNode.run(pushdown)
}else if currentTouchedNode == mainMenu.shopButton {
currentTouchedNode.run(pushdown)
}
}
In touchesMoved, touchesEnded you would compare on if the current node is equal currentTouchedNode and resize it if needed
Ok so currently in my game I have a ball that can be thrown. However, I want to make so that the user can only throw the ball past a certain point. I have been trying this for a while but I can't figure it out. How can I do this?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch?
let location = touch?.location(in: self)
if ball.frame.contains(location!) {
touchPoint = location!
touching = true
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch?
let location = touch?.location(in: self)
touchPoint = location!
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
if touching {
let dt:CGFloat = 2.8/60.0
let distance = CGVector(dx: touchPoint.x-ball.position.x, dy: touchPoint.y-ball.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
ball.physicsBody!.velocity=velocity
}
}
Here, I have an image of my game so far. Currently, the ball can be dragged, thrown, or swiped anywhere on the screen. However, I want to make so the ball can only be touched, thrown, or dragged below the middle of the screen. But I want the ball to continue being thrown if the user's finger accidently goes about the limit.
you will have to adjust the coordinates for your bottom area based on actual size and game scene anchorPoint. for example "100" in the sample assumes that your scene has an anchorPoint of (0,0) and the size of the bottom scroll area is 100.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let location = touch.location(in: self)
guard location < 100 else { return }
//if ball.frame.contains(location!) {
touchPoint = location!
touching = true
//}
}
}
I'm currently working on a game in swift using spritekit. I've got my joystick working using this library, and thats all good. I'm also moving the joystick to where the user taps using this code:
func updateJoystickPosition(pos: CGPoint) {
let posX = -(self.sceneSize.width/2) + pos.x
let posY = (self.sceneSize.height/2) - pos.y
let newJoystickPos = CGPoint(x: posX, y: posY);
self.joystick.position = newJoystickPos
}
which also works just fine, however, the joystick doesn't engage on that one tap. You have to tap on the actual joystick node for the joystick to engage, so obviously, i'd want the user to put their finger down on the screen and immediately be able to start moving it around to move the player.
the joystick it's self starts tracking with this function
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print(touches)
if let touch = touches.first, stick == atPoint(touch.location(in: self)) {
tracking = true
startHandler?()
}
}
Question: Is there anyway i can modify the touchesBegan function or any of my other functions to achieve the desired results?
You have to override touchesMoved(_ touches: ,with event:) and in this method just processing all touches.
For example:
var startTouchingPoint = CGPoint.zero // property
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
startTouchingPoint = touch.location(in: self.camera!))
yourControllerPlace.position = startTouchingPoint
stickOnYourJoystick.position = CGPoint.zero
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let point = touch.location(in: self.camera!))
let convertedPoint = CGPoint(x: point.x - startTouchingPoint.x,
y: point.y - startTouchingPoint.y)
stickOnYourJoystick.position = convertedPoint
}
}
I'm trying to do something (print "Swiped") IF the velocity of a swipe is greater than a specified criteria. Based on researching I've come up with this code. "Began" gets printed whenever I touch the screen, but "Ended" and "Swiped" only print when I first tap and hold the screen, then drag, then lift up (a rather slow process). I hope i can just flick the screen and my text gets printed, but right now it will only print if i take my time with the swipe process. Not exactly a quick swipe.. you know? Hopefully this makes sense!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("Began")
for touch in touches {
let location:CGPoint = touch.locationInView(self.view!)
start = location
startTime = touch.timestamp
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("Ended")
for touch in touches {
let location:CGPoint = touch.locationInView(self.view!)
var dx:CGFloat = location.x - start!.x;
var dy:CGFloat = location.y - start!.y;
var magnitude:CGFloat = sqrt(dx*dx+dy*dy)
if (magnitude >= kMinDistance) {
print("OK")
var dt:CGFloat = CGFloat(touch.timestamp - startTime!)
if (dt > kMinDuration) {
var speed:CGFloat = magnitude / dt;
if (speed >= kMinSpeed && speed <= kMaxSpeed) {
print("Swiped")
}
}
}
}
}
Please help!
I want to let my ball moving when the screen is touched to other position and on next touch to original position.
I only want to change X position the Y will be the same.
But when I touch the screen it is doing nothing.
This is the code, I have written for the ball.
override func didMoveToView(view: SKView) {
addball()
}
func addball() {
ball = SKSpriteNode(imageNamed: "Ball")
self.ball.position = CGPointMake(frame.size.width/4, frame.size.height/3)
self.addChild(ball)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
if touches.count % 2 == 0 {
let right = SKAction.moveToX(frame.size.width/4*3, duration: 0.1)
self.ball.runAction(right)
} else {
let left = SKAction.moveToX(frame.size.width/4, duration: 0.1)
self.ball.runAction(left)
}
}
}
Thanks for all answers:)
This may be a silly answer, but are you touching it with 2 fingers at the same time?
Try doing touches.count > 0 instead to test if it is just the touch.
If you are touching 1 finger, then another, your touch count will stay at 1 because only 1 touch began event happened.
The first touch will be part of the move state, the second touch is your begin state
Edit:
var toggle = false;
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
if toggle {
let right = SKAction.moveToX(frame.size.width/4*3, duration: 0.1)
self.ball.runAction(right)
} else {
let left = SKAction.moveToX(frame.size.width/4, duration: 0.1)
self.ball.runAction(left)
}
}
toggle = !toggle;
}