TouchesMoved() lagging very inconsistently when there is a SkSpriteNode with a physics body

Question

I'm using Swift 3.0, SpriteKit, and Xcode 8.2.1, testing on an iPhone 6s running iOS 10.2.

The problem is simple... on the surface. Basically my TouchesMoved() updates at a very inconsistent rate and is destroying a fundamental part of the UI in my game. Sometimes it works perfectly, a minute later it's updating at half of the rate that it should.

I've isolated the problem. Simply having an SKSpriteNode in the scene that has a physics body causes the problem... Here's my GameScene code:

import SpriteKit
import Darwin
import Foundation

var spaceShip = SKTexture(imageNamed: "Spaceship")

class GameScene: SKScene{

    var square = SKSpriteNode(color: UIColor.black, size: CGSize(width: 100,height: 100))

    override func didMove(to view: SKView) {
        backgroundColor = SKColor.white
        self.addChild(square)
         //This is what causes the problem:
        var circleNode = SKSpriteNode(texture: spaceShip, color: UIColor.clear, size: CGSize(width: 100, height: 100))
        circleNode.physicsBody = SKPhysicsBody(circleOfRadius: circleNode.size.width/2)
        self.addChild(circleNode)
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches{
            var positionInScreen = touch.location(in: self)
            square.position = positionInScreen

        }
    }
    }

The problem doesn't always happen so sometimes you have to restart the app like 5 times, but eventually you will see that dragging the square around is very laggy if you do it slowly. Also I understand it's subtle at times, but when scaled up this is a huge problem.

My main question: Why does me having an SKSpriteNode with a physics body cause TouchesMoved() to lag and nothing else to lag, and how can I prevent this?

Please for the love of code and my sanity save me from this abyss!!!


Show source
| swift   | ios   | sprite-kit   | touchesmoved   2017-01-07 23:01 2 Answers

Answers to TouchesMoved() lagging very inconsistently when there is a SkSpriteNode with a physics body ( 2 )

  1. 2017-01-09 21:01

    It looks like this is caused by the OS being too busy to respond to touch events. I found two ways to reproduce this:

    • Enable Airplane Mode on the device, then disable it. For the ~5-10 seconds after disabling Airplane Mode, the touch events lag.

    • Open another project in Xcode and select "Wait for the application to launch" in the scheme editor, then press Build and Run to install the app to the device without running it. While the app is installing, the touch events lag.


    It doesn't seem like there is a fix for this, but here's a workaround. Using the position and time at the previous update, predict the position and time at the next update and animate the sprite to that position. It's not perfect, but it works pretty well. Note that it breaks if the user has multiple fingers on the screen.

    class GameScene: SKScene{
        var lastTouchTime = Date.timeIntervalSinceReferenceDate
        var lastTouchPosition = CGPoint.zero
    
        var square = SKSpriteNode(color: UIColor.black, size: CGSize(width: 100,height: 100))
    
        override func didMove(to view: SKView) {
            backgroundColor = SKColor.white
            self.addChild(square)
    
        }
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            lastTouchTime = Date().timeIntervalSinceReferenceDate
            lastTouchPosition = touches.first?.location(in: self) ?? .zero
        }
    
    
    
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            let currentTime = Date().timeIntervalSinceReferenceDate
            let timeDelta = currentTime - lastTouchTime
    
    
            for touch in touches{
                square.removeAction(forKey: "TouchPrediction")
    
                let oldPosition = lastTouchPosition
                let positionInScreen = touch.location(in: self)
                lastTouchPosition = positionInScreen
    
                square.position = positionInScreen
    
    
                //Calculate the difference between the sprite's last position and its current position,
                //and use it to predict the sprite's position next frame.
                let positionDelta = CGPoint(x: positionInScreen.x - oldPosition.x, y: positionInScreen.y - oldPosition.y)
                let predictedPosition = CGPoint(x: positionInScreen.x + positionDelta.x, y: positionInScreen.y + positionDelta.y)
    
                //Multiply the timeDelta by 1.5.  This helps to smooth out the lag, 
                //but making this number too high cause the animation to be ineffective.
                square.run(SKAction.move(to: predictedPosition, duration: timeDelta * 1.5), withKey: "TouchPrediction")
            }
    
    
            lastTouchTime = Date().timeIntervalSinceReferenceDate
        }
    }
    
  2. 2017-01-10 15:01

    I had similar issues when dragging around an image using the touchesMoved method. I was previously just updating the node's position based on where the touch was, which was making the movement look laggy. I made it better like this:

    //in touchesMoved
    let touchedPoint = touches.first!
    let pointToMove = touchedPoint.location(in: self)
    let moveAction = SKAction.move(to: pointToMove, duration: 0.01)// play with the duration to get a smooth movement
    
    node.run(moveAction)
    

    Hope this helps.

Leave a reply to - TouchesMoved() lagging very inconsistently when there is a SkSpriteNode with a physics body

◀ Go back