-1

I have been working on my first SpriteKit game that perfectly works on iPhone 6 but not on iPhone 5 (iOS 8.2 on both, written in Swift). I explain my problem: there is a simple ball (a SkShapeNode) that bounces when tapping on the screen. The move is very smooth on iPhone 6 but it has some lagging on iPhone 5 but NOT the first time. The FPS is 60 all the time. I have simplified my project a maximum and I have tried everything, I really need your help so much, I am a little bit desperate. I am ready to try anything, any help would be very appreciated. Thank you so much in advance!

Here is the video (smooth the first time then lagging):

https://www.youtube.com/watch?v=xNxp2uGIJew

Here is the repository if you want to test it:

https://github.com/jonmars8/SpriteKit-Lag-iPhone5

Here is the code in the GameScene: (empty but it was the menu before simplification)

import SpriteKit

class GameScene: SKScene {

var viewController:GameViewController?

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    viewController?.presentPlayScene()
}
}

Here is the code in the PlayScene:

import SpriteKit

let PlayerCategory: UInt32 = 1 << 2
let ObstacleCategory: UInt32 = 1 << 3

class PlayScene: SKScene, SKPhysicsContactDelegate {
let world = SKNode()
var player : SKShapeNode!
var firsttap = true
var viewController : GameViewController?

override func didMoveToView(view: SKView) {
    physicsWorld.gravity = CGVectorMake(0.0, -9.8)
    physicsWorld.contactDelegate = self

    self.createPlayer()

    let obstacle = SKSpriteNode(color: SKColor.whiteColor(), size: CGSizeMake(100.0, 38.0))
    obstacle.position = CGPointMake(frame.size.width / 2.0, CGRectGetMidY(frame) * 0.75 + 400)
    let body = SKPhysicsBody(rectangleOfSize: obstacle.size)
    body.categoryBitMask = ObstacleCategory
    body.contactTestBitMask = PlayerCategory
    body.collisionBitMask = PlayerCategory
    body.linearDamping = 0.0
    body.angularDamping = 0.0
    body.dynamic = false
    obstacle.physicsBody = body

    self.addChild(world)
    self.world.addChild(obstacle)
    self.world.addChild(player)
}

func createPlayer() {
    self.player = SKShapeNode(circleOfRadius: 13)
    player.fillColor = SKColor.greenColor()
    player.strokeColor = SKColor.blackColor()
    player.lineWidth = 1
    let body = SKPhysicsBody(circleOfRadius:13)
    body.dynamic = false
    body.linearDamping = 0.0
    body.angularDamping = 0.0
    body.allowsRotation = false
    body.affectedByGravity = true
    body.categoryBitMask = PlayerCategory
    body.contactTestBitMask = ObstacleCategory
    body.collisionBitMask = ObstacleCategory
    player.physicsBody = body
    player.position = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame) * 0.75)
}

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    for touch: AnyObject in touches {
        if firsttap {
            player.physicsBody?.dynamic = true
            firsttap = false
        }
        let touchLoc = touch.locationInNode(self)
        var right = true
        if touchLoc.x > CGRectGetMidX(frame) {
            right = false
        }
        player.physicsBody?.velocity = right ?
            CGVectorMake(CGFloat(-65), CGFloat(650)) :
            CGVectorMake(CGFloat(65), CGFloat(650))
    }
}

override func update(currentTime: NSTimeInterval) {
    var pos = -player.position.y + CGRectGetMidY(frame)
    if pos < world.position.y {
        world.position = CGPointMake(world.position.x, pos)
    }
}

func didBeginContact(contact: SKPhysicsContact) {
    viewController?.presentGameScene()
}
}

Here is the code in the gameViewController:

extension SKNode {
    class func unarchiveFromFile(file : NSString) -> SKNode? {
    if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
        var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)
        var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData!)

        archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")

        if file == "GameScene"{
            let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene
            archiver.finishDecoding()
            return scene
        }
        else {
            let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as PlayScene
            archiver.finishDecoding()
            return scene
        }
        } else {
            return nil
        }
    }
}

class GameViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    self.presentGameScene()
}

func presentPlayScene() {
    if let scene = PlayScene.unarchiveFromFile("PlayScene") as? PlayScene {

        // Configure the view.
        let skView = self.view as SKView
        skView.showsFPS = true
        skView.showsNodeCount = true

        /* Sprite Kit applies additional optimizations to improve rendering performance */
        skView.ignoresSiblingOrder = true

        /* Set the scale mode to scale to fit the window */
        scene.scaleMode = .AspectFill

        //reference to self
        scene.viewController = self
        skView.presentScene(scene, transition: SKTransition.crossFadeWithDuration(0.5))
    }
}

func presentGameScene() {
    if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {

        // Configure the view.
        let skView = self.view as SKView
        skView.showsFPS = true
        skView.showsNodeCount = true
        skView.showsQuadCount = true
        skView.showsDrawCount = true

        /* Sprite Kit applies additional optimizations to improve rendering performance */
        skView.ignoresSiblingOrder = true

        /* Set the scale mode to scale to fit the window */
        scene.scaleMode = .AspectFill

        //reference to self
        scene.viewController = self

        skView.presentScene(scene, transition: SKTransition.crossFadeWithDuration(0.5))
    }
}

override func shouldAutorotate() -> Bool {
    return true
}

override func supportedInterfaceOrientations() -> Int {
    if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
        return Int(UIInterfaceOrientationMask.Portrait.rawValue)
    } else {
        return Int(UIInterfaceOrientationMask.All.rawValue)
    }
}

override func prefersStatusBarHidden() -> Bool {
    return true
}
}

Note: on iPhone 6 there is a very small lagging when quickly tapping on the screen at the very first beginning of the game.

I have replaced the SKShapeNode by SKSpriteNode and it hasn't changed anything.

Please help me, thank you so much in advance!

Jonathan
  • 11
  • 4
  • I don't know how your real code looks and if this will help but have you tried to move your code from update method to didSimulatePhysics method ? One question, why do you keep viewController?.presentGameScene() in didBeginContact ? Maybe you meant to put that in touchesBegan ? And note that using SKShapeNode, in most cases is not performant solution, and there are differences between using SKSpriteNode. You can see that if you enable showsDrawCount, showsQuadCount properties in your viewController and see what happens when you drawing a ball with shape node and using an image. – Whirlwind Apr 08 '15 at 22:30
  • When I tried your code, I created a game view controller and present this scene in a SKView. It only gives me a GREEN full screen. When I click, the screen goes black. What do I need to set up your code? – Bigman Apr 08 '15 at 22:37
  • @Bigman I just tried he's code without problems. You have to change scene class name to GameScene. Also remove the code from didBeginContact. – Whirlwind Apr 08 '15 at 22:43
  • make sure to try your game on release mode too. see if it makes a difference – hamobi Apr 08 '15 at 23:45
  • @Whirlwind, yea figured out. I did not give a size to the SKScene. – Bigman Apr 08 '15 at 23:51
  • First of all, thank you guys! @Whirlwind: I have tried with didSimulatePhysics() but I prefer in update() (the move of the ball is better this way). In my game, you lose if you touch an obstacle, that's why it is in didBeginContact(). I will enable showsDrawCount and showqQuadCount. Why do you want me to change the name to GameScene, does it have an effect? Like I said, there is no lagging the first but when presenting the scene the following times. – Jonathan Apr 08 '15 at 23:53
  • @Bigman: I will edit the code to show you how I used it – Jonathan Apr 08 '15 at 23:54
  • @hamobi: I have tried but without any differences :( – Jonathan Apr 08 '15 at 23:54
  • @Jonathan I didn't suggest you to change your scene class name. In the second comment, I was talking to Bigman. – Whirlwind Apr 09 '15 at 08:20
  • yes I have understood in a second time :) – Jonathan Apr 09 '15 at 08:49

2 Answers2

0

I posted my view controller code over here. I do not observe performance difference between iPhone 5 and iPhone 6. Can you try my code and let me know if the lag is still there?

import UIKit
import SpriteKit

class GameViewController: UIViewController
{

    var playScene: PlayScene?

    override func viewDidLoad() {

    }

    override func viewDidAppear(animated: Bool) {
        let view = self.view as SKView
        playScene = PlayScene(size: view.bounds.size)
        playScene!.viewController = self
        view.showsFPS = true
        view.showsNodeCount = true
        view.presentScene(playScene!)
    }

    func presentGameScene()
    {

        let view = self.view as SKView
        playScene = PlayScene(size: view.bounds.size)
        playScene!.viewController = self
        view.presentScene(playScene!)
    }
}
Bigman
  • 1,363
  • 11
  • 17
  • no it doesn't change anything. Everything is smooth the first time then there is a lag the following times. I'm gonna put my code on github if it can help and make a video to show you. I really don't understand the issue :( – Jonathan Apr 09 '15 at 05:46
  • Did you try it on a device or just the simulator? – Bigman Apr 09 '15 at 06:23
  • I only try on the device. I have added all the code and the link to the video to show the lagging – Jonathan Apr 09 '15 at 06:50
  • If you can put the code on github it would be helpful. I understand this little lag on the video, but when I tried my code on my iPhone 5s, I have hard time observing the lag – Bigman Apr 09 '15 at 07:36
  • Thank you so much. I will do it! – Jonathan Apr 09 '15 at 07:49
  • I have added the repository on github and added the link in the description – Jonathan Apr 09 '15 at 09:52
  • did you test it? Any idea? Thanks – Jonathan Apr 10 '15 at 06:26
  • Sorry for being missing for a while. I commented out the update method in the PlayScene.swift. The player does not seem lagging any more. Can you confirm? Also your youtube link is not available now. Can you re-upload? – Bigman May 27 '15 at 21:15
0

Maybe you can try to apply impulse instead of changing velocity directly:

 if(right){
    player.physicsBody?.velocity = CGVectorMake(0,0)
    player.physicsBody?.applyImpulse(CGVectorMake(-3, 15))
    }else{
    player.physicsBody?.velocity = CGVectorMake(0,0)
    player.physicsBody?.applyImpulse(CGVectorMake(3, 15))
    }

Also I can't produce that lag with your code(but only tested on iPhone 6)...Try to experiment with update vs didSimulatePhysics though. And try to isolate the part which causing the lag. Delete the code from update method to see what causing the lag (ball bouncing or moving the world node or something else).

Whirlwind
  • 14,286
  • 11
  • 68
  • 157