当存在具有物理主体的SkSpriteNode时,TouchesMoved()滞后非常不一致

Mar*_*llD 7 touchesmoved ios sprite-kit swift

我正在使用Swift 3.0,SpriteKit和Xcode 8.2.1,在运行iOS 10.2的iPhone 6上进行测试.

问题很简单......表面上看.基本上我的TouchesMoved()以非常不稳定的速率更新,并且正在破坏游戏中UI的基本部分.有时候它完美地工作,一分钟后它以一半的速率进行更新.

我已经解决了这个问题.只是在具有物理体的场景中有一个SKSpriteNode会导致问题......这是我的GameScene代码:

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

        }
    }
    }
Run Code Online (Sandbox Code Playgroud)

这个问题并不总是会发生,所以有时你必须重启应用程序5次,但最终你会发现,如果你慢慢地拖动方块,那么它就会非常滞后.我也理解它有时很微妙,但是当扩大规模时,这是一个很大的问题.

我的主要问题: 为什么我有一个带有物理体的SKSpriteNode导致TouchesMoved()滞后而没有其他任何延迟,我该如何防止这种情况?

请为了爱的代码和我的理智,救我脱离这个深渊!

Nob*_*ica 4

看起来这是由于操作系统太忙而无法响应触摸事件造成的。我找到了两种重现此问题的方法:

  • 在设备上启用飞行模式,然后将其禁用。禁用飞行模式后约 5-10 秒内,触摸事件会出现滞后。

  • 在 Xcode 中打开另一个项目,并在方案编辑器中选择“等待应用程序启动”,然后按“构建并运行”将应用程序安装到设备上而不运行它。安装应用程序时,触摸事件会滞后。


似乎没有解决这个问题的方法,但这里有一个解决方法。使用上一次更新的位置和时间,预测下一次更新的位置和时间,并将精灵动画到该位置。它并不完美,但效果很好。请注意,如果用户在屏幕上有多个手指,它就会中断。

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
    }
}
Run Code Online (Sandbox Code Playgroud)