Swift - Delay for dragging sknode - swift

My SceneKit app has both
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
...
}
#objc func handleButtonMovement(recognizer : UIPanGestureRecognizer) {
...
}
The SKSpriteNode when touched doesn't trigger the handleButtonMovement unless you move slightly in any direction - but TouchesBegan is launched.
Also, if I touch the SKSpriteNode and move too quickly, the handleButtonMovement isn't triggered either.
Is there some way to adjust the sensitivity so that as soon as you touch the SKSpriteNode, it triggers handleButtonMovement as a drag event?

Related

Spritekit: Dragging a SKCameraNode()

I am rather new to Swift and SpriteKit. I made a plane move around on a big background. What I am trying to do now is DRAG the "camera" so i can see the plane when its outside my view.
I made a SKCameraNode()
I can change its position without problem. And then I tried this for drag:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
camera?.position = CGPoint(x: location.x, y: location.y)
}
}
The camera moves when i drag, but super fast and far far far away...
Some ideas? Thanks!

removeFromParent() strange behavior

I have really strange behavior with function removeFromParent
lazy var buttonAds: SKSpriteNode = {
let n = SKSpriteNode(imageNamed: "ButtonAds")
n.position = CGPoint(x: size.width / 2, y: 600)
n.zPosition = 100
n.setScale(1.4)
return n
}()
in didMove(...) add this button with addChild(buttonAds), and latter in touchesBegan:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
if buttonAds.contains(touch.location(in: self)) {
// ...
doAds()
buttonAds.removeFromParent()
}
}
If you tap on button for ads, will be removed, but if tap on that place again, this will call function doAds() again... it's strange, buttonAd don't exist on scene.
Initial:
and after tap:
Thanks
What you want to do is check if the node you touch is of the type it should be. Change your code to this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
if nodeAtPoint(touch.locationInNode(self)) == buttonAds {
doAds()
buttonAds.removeFromParent()
}
}
This should do the trick!
edit: as to why this works, you're removing the node from the scene but it is still an object in memory (otherwise you wouldn't be able to use buttonAds.contains(...) on it) so it also still has its position stored.

Using Swipe Recognizer in SpriteKit

I'm trying to implement swipe gestures left and right into a game in SpriteKit where the player collects falling objects spawning from the top of the screen. The problem I'm facing is trying to keep a continuous movement of the player while the finger is on the screen until the touch is ended and the player stays where the last touch ended. There might be a better way to implement this other than swipe gestures hence why I'm asking you guys! Any help would be great, thank you all!
This is not something you want to use a swipe (I think you are using pan) gesture for. What you want to do is override the touchesBegan, touchesMoved, and touchesEnded calls on the scene, and plan your movement according to these 3 methods.
You are probably going to want to use SKAction.move(to:duration:) with these methods, and figure out the math to keep a constant speed.
E.G.
func movePlayer(to position:CGPoint)
{
let distance = player.position.distance(to:position)
let move = SKAction.move(to:position, duration: distance / 100) // I want my player to move 100 points per second
//using a key will cancel the last move action
player.runAction(move,withKey:"playerMoving")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches.first
let position = touch.location(in node:self)
movePlayer(to:position)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches.first
let position = touch.location(in node:self)
movePlayer(to:position)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let position = touch.location(in node:self)
movePlayer(to:position)
}

TouchEvents in watchOS 3 SpriteKit?

When using SpriteKit in watchOS 3, how do you handle the touch events? I am porting the SpriteKit games from iOS and the codes below won't work. Or you have to control the WKInterfaceController somehow?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {}
After a lot of frustration with the same issue, I wound up using gesture recognizers, and calculating the position from there.
#IBAction func handleSingleTap(tapGesture: WKTapGestureRecognizer) {
let location = tapGesture.locationInObject()
if yourSpriteNodeRect.contains(location) {
//do stuff
}
}
One tip though: A strange bug I found though when creating the method to handle the gesture recognizer was that the gesture wasn't being passed by default into the method. I had to ctrl + drag to create the method, and then modify the signature to contain the gesture. If I created the method with the gesture in the signature, Xcode wouldn't allow me to attach the gesture recognizer to it.
It would seem that there would be a better way to detect the touches, but that's the workaround I found for now.
To get something close to touch down events use a WKLongPressGestureRecognizer with super-short duration (0.001). The IBAction connected to this recognizer will get touch down events pretty much right away. The location is found in the locationInObject property of the recognizer. If the goal is to get moving touches then use the pan recognizer.
So to do this I first hooked up a gesture recognizer in the watch storyboard. Created an IBAction, then called a method on the SpriteKit Scene passing in gesture.locationInObject(). Then within the scene method for the conversion, here's some code.
let screenBounds = WKInterfaceDevice.current().screenBounds
let newX = ((location.x / screenBounds.width) * self.size.width) - (self.size.width / 2)
let newY = -(((location.y / screenBounds.height) * self.size.height) - (self.size.height / 2))
The newX and newY are the touch coordinates in the scene.

SpriteKit Animations

I have this code that does the animation that I want to be executed over the entire time I'm holding down a button that I've created. However I would like this to repeat while the button is being held. Once I've let go it would return the sprite back to a standing position.
func runForward()
{
let run = SKAction.animateWithTextures([
SKTexture(imageNamed: "walk1"),
SKTexture(imageNamed: "walk2"),
SKTexture(imageNamed: "walk3"),
SKTexture(imageNamed: "walk4")
], timePerFrame: 0.09)
_hero!.runAction(run)
}
If I put this code inside of update it updates every frame causing the animation to only finish once I lift my finger off the button. If I start this animation once the button is clicked it only performs it at the very beginning. I was wondering how I get this to run consecutively until I lift my finger off the button.
Heres the code of the button which is just a Sprite Node placed on screen.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
// Loop over all the touches in this event
for touch: AnyObject in touches {
// Get the location of the touch in this scene
let location = touch.locationInNode(self)
// Check if the location of the touch is within the button's
if (right.containsPoint(location)) {
_right = true
runForward()
}
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
_right = false
}
What you want to do is start the animation when your touch begins (touchesBegan) and end the animation when your touch ends (touchesEnded).
Therefore, you should perform an action that repeats forever once your touch begins. This action will have a key (or a name). Once your touch ends, you can use the key (or name) to cancel the action that is running forever (thus it will stop the animation)
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let run = SKAction.animateWithTextures([
SKTexture(imageNamed: "walk1"),
SKTexture(imageNamed: "walk2"),
SKTexture(imageNamed: "walk3"),
SKTexture(imageNamed: "walk4")
], timePerFrame: 0.09)
hero.runAction(SKAction.repeatActionForever(SKAction.sequence([
run,
SKAction.waitForDuration(0.001)
])
), withKey: "heroRunning"
)
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
hero.removeActionForKey("heroRunning")
}
}