Swift: Deallocate Gamescene After Transition to New Scene

Swift: Deallocate GameScene after transition to new scene?

In my project, I used the following mechanism and it worked well. All my scenes SKScene objects were optional variables. When i need the new scene to show, i create it and present at SKView. When i need to display the new scene, I set the previous object scene object to nil, this immediately reducing the reference count by 1, and becouse of at this moment no one object is not use my scene, the reference count becomes zero and scene was deleted.

The SKScene object is a ordinary class object and ARC works with them like with all reference type objects. You only need to monitor the number of references to the scene. All are finished with the various resources I was start in deinit of SKScene object

The simple example:

At UIViewController we have optional objects of GameScene:

class GameViewController: UIViewController {
var scene1: GameScene?
var scene2: GameScene?
var skView: SKView?

// Action to present next Scene
@IBAction func nextSceneAction(sender: AnyObject) {
print("Next scene")
// Create new GameScene object
scene2 = GameScene(fileNamed:"GameScene")
// Present scene2 object that replace the scene1 object
skView!.presentScene(scene2)
scene = nil
}

override func viewDidLoad() {
super.viewDidLoad()

// Create GameScene object
scene = GameScene(fileNamed:"GameScene")
skView = (self.view as! SKView)
skView!.showsFPS = true
skView!.showsNodeCount = true

skView!.ignoresSiblingOrder = true

scene!.scaleMode = .AspectFill

// Present current scene
skView!.presentScene(scene)
}
}

At GameScene in deinit print some text to show that it object will be deleted:

class GameScene: SKScene {
...

deinit {
print("Deinit scene")
}
}

Debug output after push the nextSceneAction button:

Next scene

Deinit scene

Swift, SpriteKit: Deallocate a Gamescene et reallocate a new one

In general a SKScene automatically deallocates once you transition to a new scene, unless you have a memory leak, so usually you don't have to do anything in particular to free memory.

To see if your scene has deallocated you can add the deinit method

  func deinit {
print("scene did deallocate")
}

If this method gets called when you change scenes you know everything correctly deallocated, if it doesn't get called you have a memory leak.

Also you code is faulty, you are creating a new GameViewController instance everytime you change scene instead of accessing the current instance.

 let controller2 = GameViewController() // creates new instance of GameViewController
controller2.nextSceneAction1()

You normally only load your 1st scene from your GameViewController and than all other scene changes can be done directly in the SKScene you are in. Don't bother using the GameViewController for this.

In your current SKScene you can change to a new scene like this (e.g from GameScene to MenuScene)

 class GameScene: SKScene {

...

func loadMenuScene() {

let menuScene = MenuScene(size: self.size) // use size of current scene
let transition = Some SKTransition
view?.presentScene(menuScene, withTransition: transition)
}
}

Hope this helps

Save state of GameScene through transitions

Retaining your scene is very simple, all you need to do is retain the scene with a strong reference

In your ViewController, it is as simple as storing a variable

class ViewController : UIViewController
{
var gameScene = GameScene(fileNamed:"GameScene")
}

Now for as long as your view controller is alive, your scene will be alive.

To access it, you just need to find a way to tell the MenuScene where your view controller is, then present the scene.

class ViewController : UIViewController
{
var gameScene = GameScene(fileNamed:"GameScene")
lazy var skView : SKView = self.view as! SKView
func gotoMenu()
{
let menu = MenuScene(fileNamed"MenuScene")
menu.viewController = self
skView.presentScene(menu)
}
}

class MenuScene : SKScene
{
var viewController : ViewController!
func returnToGame()
{
view.presentScene(viewcontroller.gameScene)
}
}

But, what if you don't want to use custom SKScene classes all the time, use a view controller, or would rather rely on components, why isn't there a convenient way to go back to a scene.

Well my friend, there is, and it is where userData comes into play

class GameScene : SKScene
{
func gotoMenu()
{
let menu = MenuScene(fileNamed:"MenuScene")
menu.userData = menu.userData ?? ["":Any]()
menu.userData["backToScene"] = self
view.presentScene(menu)
}
}

class MenuScene : SKScene
{
func returnToGame()
{
guard let userData = userData, let scene = userData["backToScene"] as? SKScene
view.presentScene(scene)
}
}

Since we are retaining it in the user data, we can now present the old scene anywhere we have access to the menu scene.

userData is also great in transferring inventory, of course I would create a class to manage the inventory, and just pass the reference via userData

Now, to create a menu that overlays the current scene, that is as simple as applying a new node onto your scene.

You can even use a separate SKS file to layout your menu, and overlay it:

class GameScene : SKScene
{
let menu = MenuScene(fileNamed:"MenuScene")

func overlayMenu()
{
scene.addChild(menu) //You probably want to add an SKCameraNode, and add it to the camera instead
}
override func update(currentTime: CFTimeInterval)
{
if menu.parent != nil
{
menu.update(currentTime:currentTime) //do this only when you need to have a constant update call, be sure to include additional functionality like `didFinishUpdate` in the approprate functions when needed
}
}

}

Of course now would be a good time to develop what is called a worldNode, also may be referred to as gameNode

Essentially what this node is, is the node that holds all your game elements.
This allows you to add overlay nodes that can pause your game.

Your scene hierarchy would like like this:

SKScene

--worldNode

----all nodes that belong in the game

--menuNode

----all nodes that belong on the menu

Now at any time, menu can set the worldNode's isPaused state to true, allowing the game to pause and still giving you the ability to interact with the menuNode

Change Scenes after Interstitial (SpriteKit)

So I was able to do was include a wait function, that way the ad is shown on the screen before the scene even transitions.

 func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

And then placed scene changer within the delay function like so

 delay(2.0) {
self.changeScene()
}

The 2.0 is the time in seconds so this is saying that the code will wait 2 seconds and then run the change scene function. When the ad is exited the scene is now changed.

My GameScene is not positioned correctly after switching scene

Take a look at scene.anchorPoint, maybe you accidentally changed it. Default is CGPoint(x: 0.5, y: 0.5).

Appearing scene after transition

I found place where my code works bad.
when I enter GameScene I save it to my manager and when I leave this scene I save it as well to my manager. I just removed first save and now code works like a charm.

Hope this can save someone several hours.

Thank you to everyone!

Deallocate SKScene after transition to another SKScene in SpriteKit

A similar problem was faced by the person who asked this question.

When asked whether he was able to solve it, they said:

Yes, I did, there wasn't anything I could do about it from the scene
or Sprite Kit for that matter, I simply needed to remove the scene and
the view containing it completely from the parent view, cut all its
bonds to the other parts of the system, in order for the memory to be
deallocated as well.

You should use separate views for each of your scene and transition between these views. You can follow these steps to make it look natural:

1 - At the point you want to transition from one scene to the other, take a snapshot of the scene using the following code:

UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, scale);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Then, add this image as a subview of the SKView of the current scene, and remove the scene.

2 - Initialise the new scene on another view, and transition between the two views using UIView animation.

3 - Remove the first view from it's superview.



Related Topics



Leave a reply



Submit