SpriteKit Scrolling SKCameraNode Very Glitchy - ios

When I scroll through on this scene with my code, it is extremely glitchy looking. Everything kind of jumps around very quickly until you let go. Is there a better way of doing this?
var lastTouch: CGPoint!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch : UITouch = touches.first!
lastTouch = touch.locationInNode(self)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch : UITouch = touches.first!
let touchLocation : CGPoint = touch .locationInNode(self)
self.camera!.position = CGPointMake(self.camera!.position.x + (lastTouch!.x - touchLocation.x), self.camera!.position.y + (lastTouch!.y - touchLocation.y))
lastTouch = touchLocation;
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch : UITouch = touches.first!
let touchLocation : CGPoint = touch .locationInNode(self)
self.camera!.position = CGPointMake(self.camera!.position.x + (lastTouch!.x - touchLocation.x), self.camera!.position.y + (lastTouch!.y - touchLocation.y))
print(touches.count)
lastTouch = touchLocation;
}
I can upload a gif/video if really necessary. Is this just a bug with SKCameraNode as it is so new? If anyone knows what I am doing wrong I'd love to hear. Thanks!
EDIT: Click here to see a video of the issue

I have found the answer, as it was a very simple mistake. As the camera node moved, the location in which you are touching would also move. I created a node that moves with the camera and get the touch's location in that node.

Related

multitouch in iOS [duplicate]

So I've been messing around trying to get the coordinates of touches on the screen. So far I can get the coordinates of one touch with this:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject()! as UITouch
let location = touch.locationInView(self.view)
println(location)
}
But when touching with two fingers I only get the coordinates of the first touch. Multi-touch works (I tested with this little tutorial: http://www.techotopia.com/index.php/An_Example_Swift_iOS_8_Touch,_Multitouch_and_Tap_Application). So my question is, how do I get the coordinates of the second (and third, fourth...) touch?
** Updated to Swift 4 and Xcode 9 (8 Oct 2017) **
First of all, remember to enable multi-touch events by setting
self.view.isMultipleTouchEnabled = true
in your UIViewController's code, or using the appropriate storyboard option in Xcode:
Otherwise you'll always get a single touch in touchesBegan (see documentation here).
Then, inside touchesBegan, iterate over the set of touches to get their coordinates:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self.view)
print(location)
}
}
the given touches argument is a set of detected touches.
You only see one touch because you select one of the touches with :
touches.anyObject() // Selects a random object (touch) from the set
In order to get all touches iterate the given set
for obj in touches.allObjects {
let touch = obj as UITouch
let location = touch.locationInView(self.view)
println(location)
}
You have to iterate over the different touches. That way you can access every touch.
for touch in touches{
//Handle touch
let touchLocation = touch.locationInView(self.view)
}
In Swift 1.2 this has changed, and touchesBegan now provides a Set of NSObjects.
To iterate through them, cast the touches collection as a Set of UITouch objects as follows:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touchSet = touches as! Set<UITouch>
for touch in touchSet{
let location = touch.locationInView(self.view)
println(location)
}
}
For Swift 3, based on #Andrew's answer :
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchSet = touches
for touch in touchSet{
let location = touch.location(in: self.view)
print(location)
}
}
EDIT, My bad, that's not answering your question, had the same problem and someone linked me to this previous answer :
Anyway, I had to change few things to make it works in swift 3, here is my current code :
var fingers = [String?](repeating: nil, count:5)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
for touch in touches{
let point = touch.location(in: self.view)
for (index,finger) in fingers.enumerated() {
if finger == nil {
fingers[index] = String(format: "%p", touch)
print("finger \(index+1): x=\(point.x) , y=\(point.y)")
break
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
for touch in touches {
let point = touch.location(in: self.view)
for (index,finger) in fingers.enumerated() {
if let finger = finger, finger == String(format: "%p", touch) {
print("finger \(index+1): x=\(point.x) , y=\(point.y)")
break
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
for touch in touches {
for (index,finger) in fingers.enumerated() {
if let finger = finger, finger == String(format: "%p", touch) {
fingers[index] = nil
break
}
}
}
}
I still have a little problem but I think it's linked to my GestureRecognizer in my code.
But that should do the trick.
It will print you the coordinate of each point in your consol.
In Swift 3,4
Identify touch pointer by its hash:
// SmallDraw
func pointerHashFromTouch(_ touch:UITouch) -> Int {
return Unmanaged.passUnretained(touch).toOpaque().hashValue
}

Translate location in UIView to SKScene

I want to use the touchBegan functionality in my UIView and I should trigger SKScene methods. Everything is almost working expect the fact that the startingPosition of my SKNode is different from the touch in the UIView. I read about it here: Confusing reversed touch event in Swift however they do not give a solution how to translate a UIView touch gesture to a SKScene touch gesture that correspons with the touch location. This works:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
var currentPoint = touch.location(in: scene)
executeSpawningStar(startPosition: currentPoint)
}
}
But I place a UIView above it, and this function does not work anymore. When I change it to this:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
var currentPoint = touch.location(in: newUIView)
executeSpawningStar(startPosition: currentPoint)
}
}
The spawning looks very weird. The coordinaties of the spawning star is not correct. How can I transfer the correct coordinates from the UIView to SpriteKit settings to match the real position of the touch gesture?
Good news! SKView has methods for converting to and from SKScene coordinates.
func convert(_ point: CGPoint, from scene: SKScene) -> CGPoint
func convert(_ point: CGPoint, to scene: SKScene) -> CGPoint
So you could do something like:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
var currentPoint = touch.location(in: newUIView)
currentPoint = gameScene.convertPoint(fromView: currentPoint)
executeSpawningStar(startPosition: currentPoint)
}
}
https://developer.apple.com/reference/spritekit/skview/1520328-convert
https://developer.apple.com/reference/spritekit/skview/1519847-convert

Drag and drop SKNode without touching it

I have an SKNode on SpriteKit, which I basically want to be able to drag around the screen, but without having to touch it! Imagine I press the screen anywhere. I then want my SKNode to keep its distance to my finger, so that when I drag it I can see it.
I have this working but the object snaps to the touch.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
circle.position = location
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
circle.position = location
}
}
It's a classic basic problem in game engineering.
There are two solutions...
First solution: when finger first goes down, make a note of the "grab" delta, being the delta from the position of the object, to, the finger.
When the finger moves each time to new position P, subtract the "grab" delta from P before setting the position of the object to P.
"It's that easy"
Second solution: each time the finger moves, don't bother at all with the position P of the finger.
Instead, calculate the delta the finger moved from the previous frame.
(So, simply store the previous position each time so you can calculate that - some systems give you the previous position as a property since it's so common, indeed some systems just give you the delta automatically as a property!)
Then just move the object by that delta.
"It's that easy"
Here is precisely the first solution, in iOS/SpriteKit
class FingerFollower: SKSpriteNode {
var grab: CGVector = CGVector.zero
// "grab" is the usual term for the delta from the object
// to where the finger "grabbed" it...
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let t: UITouch = touches.first! as UITouch
let l = t.location(in: parent!)
grab = (l - position).vector
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let t: UITouch = touches.first! as UITouch
let loc = t.location(in: parent!)
position = loc - grab
}
// NOTE this code uses the absolutely obvious overrides for
// subtraction etc between vectors, which you will need in 100%
// of spritekit projects (Apple forgot about them)
}
Here is precisely the second solution, in iOS/SpriteKit
class FingerFollower: SKSpriteNode {
func setup() {
// NOTE, you MUST have a "setup" call in your sprite subclasses;
// apple forgot to include a "didAppear" for SKNodes
isUserInteractionEnabled = true
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// note that Apple do in fact include the "previous location"
// in sprite kit touches. (In systems where they don't do that,
// you just make a note of it each time.)
let prev = t.previousLocation(in: parent!)
let quickDelta = loc - prev
position = position + quickDelta
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?){
let touch = touches.anyObject() as UITouch!
let touchLocation = touch.locationInNode(self)
let previousLocation = touch.previousLocationInNode(self)
let distanceX = touchLocation.x - previousLocation.x
let distanceY = touchLocation.y - previousLocation.y
circle.position = CGPointMake(circle.position.x + distanceX, circle.position.y + distanceY)
}

How to speed up the movement of the object?

In the scene of a finger you can move the object. But if you just speed up the movement of the finger on the screen - the object remains in place. Is it possible to accelerate the speed of its movement? Duration is already set to 0
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
let node = self.nodeAtPoint(touchLocation)
if (node.name == "circle") {
let moveAction = SKAction.moveTo(touchLocation, duration: 0)
figureUser.runAction(moveAction)
}
}
Your problem is not the speed, your problem is you are moving so fast on the screen, that your touch is not recognizing a node underneath it anymore because the node is physically not underneath it. How you handle this problem is on the touch begin event, you check for a node, and assign this node to a variable. Then on the touch move event, update the new variable. Finally on touch end, clear the variable. Note, you would need to handle this code for things like multi touch
var movingNode : SKNode?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
let node = self.nodeAtPoint(touchLocation)
if (node.name == "circle") {
movingNode = node
let moveAction = SKAction.moveTo(touchLocation, duration: 0)
figureUser.runAction(moveAction)
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
let moveAction = SKAction.moveTo(touchLocation, duration: 0)
//at this point I am lost, how does node even relate here,
//is figureUser suppose to be node?
figureUser.runAction(moveAction)
}

Getting touch coordinates in Swift 1.2

I'm trying to get the coordinates of a touch on the screen, but every time it just gives me 0, 0. I could only find basic help with this function in the new version of Swift, and nothing on this problem. What am I doing wrong here?
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let location = touch.locationInView(self.view)
NSLog("location: %d, %d", location.x, location.y)
}
Thanks!
As per swift 1.2
override func touchesEnded(touches: Set<NSObject>!, withEvent event: UIEvent!) {
if let touch = touches.first as? UITouch {
let location = touch.locationInView(self.view)
println("Location: \(location)")
}
}
As per swift 1.1
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as? UITouch
let location = touch?.locationInView(self.view)
println("Location: \(location)")
}
Tested.

Resources