Reputation: 1077
I'm experiencing this lag or stutter in my game, and I'm thinking it could be due to the way I have the camera follow my player.
Here is the code:
func update(_ hp: CGPoint) {
let camMove = SKAction.move(to: CGPoint(x: hp.x, y: hp.y), duration: 0.2)
self.run(camMove)
}
The above function is in an extension for SKCameraNode and I call it basically from the scenes update function.
It functions pretty well, although someone mentioned the stutter could be due to the overlapping actions since it is called every frame. I use this method because the duration allows for the camera to follow more naturally, than rigid.
I'm looking for alternatives to this to eliminate this SKAction overlap.
I have tried adding a removeallActions() call before running the new action, but it must happen to fast because the camera doesn't move at all. Also, I tried adding a key to the action and remove by key prior to the next one starting, but same as removeallActions(), camera doesn't move.
I have also tried the following:
func moveCam(_ point: CGPoint) {
let camMove = SKAction.move(to: CGPoint(x: point.x, y: point.y), duration: 0.2)
self.run(camMove) {
self.moveCam(player.position)
}
}
This stops the overlapping, but there is a slight delay in-between one action ending and another ending which causes an ugly stutter constantly.
I have an idea to attempt to use SKAgents with a follow behavior, but I'm not entirely sure how this would be done or if it would be good. Any suggestions or solutions would be greatly appreciated.
Upvotes: 1
Views: 261
Reputation: 16827
You want to do this when the sprite position changes, so I would recommend doing KVO to monitor this, and update your action accordingly:
override func didMove(to view:SKView) {
super.didMove(to:view)
sprite.addObserver(self, forKeyPath: #keyPath(SKNode.position),
options: [.old, .new, .initial],
context: nil)
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?, change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
if keyPath == #keyPath(SKNode.position) {
guard let camera = self.camera, let sprite = object as? SKNode else { fatalError() }
let wait = SKAction.wait(forDuration: 0.2)
let loop = SKAction.customAction(withDuration: TimeInterval(CGFloat.infinity)) {_,_ in
let move = SKAction.move(to: CGPoint(x: self.sprite.position.x, y: self.sprite.position.y), duration: 0.2)
camera.run(SKAction.sequence([move,SKAction.run({
camera.removeAction(forKey:"cameraWaitKey")})]), withKey:"cameraMoveKey")
}
if camera.action(forKey:"cameraWaitKey") == nil {
camera.run(SKAction.sequence([wait,loop]), withKey:"cameraWaitKey")
}
}
}
deinit { sprite.removeObserver(self, forKeyPath: #keyPath(SKNode.position)) }
What this will do is apply a small wait, then start moving the camera every time the position changes. Once the camera reaches the player, it will reset, and when you move again, the delay will happen.
You can add timingMode to your actions to create easeIn and easeOut effects as the camera moves.
Upvotes: 1