SpriteKit memory issues - memory

I'm new to game development with SpriteKit and everything went ok so far, but cant really the memory management part.
I created a first UIViewController to act as a level selector, a second UIViewController in which a present the SKScene of the game. The problem appears when i go back from the SKScene to the level selector and none of the memory is released.
From the game SKScene, when the user pushes the back button to go to the level selector i post a notification which tells the second UIViewController to perform the segue.
//SKScene
[self removeAllActions];
[self removeAllChildren];
[self removeFromParent];
[[NSNotificationCenter defaultCenter]postNotificationName:#"toLevelSelector" object:nil userInfo:nil];
//Second UIViewController
- (void)toLevel:(NSNotification *)notif
{
[self performSegueWithIdentifier:#"toLevelSelector" sender:self];
}
Can you help me get a better understanding on when and where should be the skview or the skscene released from memory ?

I think the memory is not released because you did not dismiss view controller that holds SpriteKit. When I first tried to make game that has menus in UIViewControllers and then button starts SpriteKit game, I experienced that closing game does not end timers, sounds, music, etc...
Let's say you have UIViewController called MainMenuViewController and that it has a button that calls GameViewController in which you run SpriteKit Game. So in order to close your game completely, try to implement this method in your exit game button
[(GameViewcontroller *)self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];

Related

How to deallocate unused SKScene

I have a SKScene object setup in GameViewController. I implemented dealloc into the SKScene subclass as follow:
- (void)dealloc {
NSLog(#"Dealloc: %#", self);
}
Now I want to present another view controller on top of GameViewController. I used storyboard segue to do this. But after the new view controller is presented, I never received the SKScene dealloc. It got stuck in the memory. My app starts to freeze after a few minute due to low memory. How can I dealloc the scene after presenting the new view controller.
In order to deallocate you SKScene instance -> you Should Eliminate every pointer that retains it -> then ARC will automagically release it.
Simply call the [skView presentScene:nil]; method which also will remove the SCScene and deallocate it by setting SKView.scene property to nil.
SKView *skView = (SKView *)self.view;
GameScene *scene = (GameScene *)skView.scene;
for(SKNode * child in scene.children)
{
[child removeAllActions];
}
[scene removeAllChildren];
[scene removeAllActions];
[skView presentScene:nil];
Note Normally you don't have to remove everything from the SKScene if your memory is managed right just call [skView presentScene:nil]; method and ARC will take care of it.
Apparently you had something inside the SKScene that retained it.
So by removing everything from it, we eliminated the retain cycle.
This solution will not always work, it will work only if one of the SKActions or SKNodes are retaining the SKScene which were the issue in your case, However your SKScene could be retained from somewhere else and this answer wouldn't really help you

dismissing GLKViewController does not free memory

I got a question to ask. Currently, dismissing GLKViewController is not deallocating the memory at all. Rather, it takes more memory whenever I move to the GLKViewController and dismiss it. My game starts at 100MB. Whenever I play a game, it adds up about 10MB. So, if I play it 10 times, it will be about 200MB in the end. It will crash eventually.
This is my only reference to the GLKViewController. I do not call it anywhere in my uiviewcontroller anywhere other than that. I use modal segue to move to glkviewcontroller.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
Game *renderer = [segue destinationViewController];
[renderer setDelegate:self];
renderer.bomb = bomb;
}
And I dismiss it using [self dismissViewControllerAnimated:YES completion:nil]
Do you know how to fix this problem? It is wasting so much time right now. Thanks.

How to kill an SKScene

I've got a game with a MasterViewController. It has a "Play" button that segues to the GameViewController. The GameViewController and GameScene are plain vanilla what a game build provides except I added an NSLog to the GameScene's update method and, on the storyboard, created a "Quit" button that segues back from the GameViewController to the MasterViewController.
Everything works as expected. I fire up the app and touch the Play button and it transitions to the GameViewController to the GameScene. Fine, I see the standard "Hello World" message and can touch to create spinning spaceships. I start getting the NSLog output from the update method. Great.
However, when I click the Quit button and it segues back to the MasterViewController, I am still getting the NSLog output from the GameScene update method so the GameScene is still active. I want the GameScene gone. Added a dealloc to the GameScene and it is never called, probably because of ARC.
In the GameViewController I added a weak gameScene property and:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(#"viewWillDisappear");
[_gameScene removeAllChildren];
[_gameScene removeAllActions];
[_gameScene removeFromParent];
_gameScene = nil;
}
Still getting the NSLog output from the GameScene update method. Sigh... How do I kill the GameScene dead, dead, dead?
I did the Play/Quit/Play/Quit transition several times. The output from the update method is:
2014-11-20 12:48:41.551 Demo[7386:2004098] update: 0x7b091090
2014-11-20 12:48:42.095 Demo[7386:2004098] update: 0x7ed21020
2014-11-20 12:48:42.656 Demo[7386:2004098] update: 0x7eb1c4b0
2014-11-20 12:48:43.217 Demo[7386:2004098] update: 0x7b091090
2014-11-20 12:48:43.762 Demo[7386:2004098] update: 0x7ed21020
2014-11-20 12:48:44.322 Demo[7386:2004098] update: 0x7eb1c4b0
So all of my GameScenes are still active in the background.
You must make sure no other objects are pointing strongly to the object you want to remove from memory. See Apple Developer.
I found a workaround - use a nav controller, which I alway hide to make room for my game. To transition to the next view controller in the hierarchy, use a "Show" segue. To pop back, add your own Back button and connect the action:
- (IBAction)backButtonClicked:(UIButton *)sender {
[[self navigationController] popViewControllerAnimated:YES];
}
I also have one place in my GameViewController where I use a "Pause" button to pop all the way back to the root view controller.
- (IBAction)pauseButtonClicked:(UIButton *)sender {
NSArray * viewControllers = self.navigationController.viewControllers;
NSLog(#"nav view controllers: %#", viewControllers);
UIViewController * targetViewController = viewControllers[0];
NSLog(#"target controller: %#", targetViewController);
[self.navigationController popToViewController:targetViewController animated:YES];
}
All this correctly deallocates the GameViewController and GameScene when they disappear.

Turn-based multiplayer game (Game Center, iOS) - Dismiss GKMatchmakerViewController and presenting own user interface to play a turn

I am implementing a turn-based multiplayer game with game center. The first view controller (ViewController) shows the menu and it is already possible to display the GKTurnBasedMatchmakerViewController on button tap. After inviting a friend for the game, the GKTurnBasedMatchmakerViewController disappears and game center 'starts' the game. The problem is that the game does not really start because it belongs to another view controller (SecondViewController). I am not able to use segues because I do not know how to assign a segue identifier to the GKTurnBasedMatchmakerViewController (because it is not in Storyboard). I know that there has to be a possibility to realize it because many apps use this feature. The Apple Developer Documentation solves this problem using a custom segue, but how is this possible? Any ideas?
Apple Developer Documentation:
'Typically, your game dismisses the matchmaker view controller and immediately starts its own user interface to allow the player to play a turn.' >> That's what I want to do!
-(void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match
{
[self dismissViewControllerAnimated:YES completion:nil];
[self performSegueWithIdentifier:#"GamePlayScene" sender:match];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"GamePlayScene"])
{
MyGamePlayViewController* gameVC = (MyGamePlayViewController*) segue.destinationViewController;
gameVC.delegate = self;
gameVC.match = (GKTurnBasedMatch*) sender;
}
}
Thanks in advance!
You have to create two "View Controller"(Scene) in "Main.storyboard". One for all the "game center stuff"(Default:GameViewController), another for "game logic"(MyGamePlayViewController). After that hold "ctrl" key and drag the first scene to the other and select "present modally".
See bild.
Take care of the arrows, there you have to set your "identifier".
Hope this will help. Good luck.

Stop Animation with CCDirector AFTER adding UIViewController to cocos2d scene

I'm trying to add a UIKit ViewController on top of a cocos2d scene. Everything works great, but I need to run [[CCDirector sharedDirector] stopAnimation] AFTER the UIKit view is loaded completely.
I push a button and have a 1 second transition to the new scene being loaded. I have tried adding stopAnimation to every callback in both the UIViewController(ViewDidLoad, ViewDidLayoutSubviews, etc) and the Cocos2d scene. What happens is the scene is loaded but the animation is stopped before the UIViewController is completely loaded, which prevents the view from loading at all.
I could set a timer for the transition duration and then call stopAnimation but I would rather not do it that way. Is there any sort of delegate method I'm missing here?
You need to call that method from your view controller's viewDidLoad method:
- (void) viewDidLoad
{
[super viewDidLoad];
[[CCDirector sharedDirector]stopAnimation];
}

Resources