Reuse Director for new scene - ios

I am currently working on an Cocos2d-x, which uses native UI (UIKit Framework) besides the Game.
This is my actual workflow:
When I start the game every things works well. I can even restart the game by reinitializing the scene, but I can't pop the UIViewController with my CCEAGLView and push it again on the top.
When I do this I get a scene without any movement.
Here's my opens the scene:
if (cocos2d::Director::getInstance()->getRunningScene() && [[self.view.subviews firstObject] isKindOfClass:[CCEAGLView class]]) {
//Restart scene without popping ViewController
//Works well
auto beam = GrowingBeam::createLayer();
auto scene = beam->getScene();
beam->_self = (__bridge void*) self;
cocos2d::Director::getInstance()->replaceScene(scene);
}else {
if (cocos2d::Director::getInstance()->getRunningScene()) {
//Restart scene after View Controller has been popped
// DOESN'T WORK !!
auto beam = GrowingBeam::createLayer();
auto scene = beam->getScene();
beam->_self = (__bridge void*) self;
[self.view insertSubview:(__bridge CCEAGLView *) cocos2d::Director::getInstance()->getOpenGLView()->getEAGLView() atIndex:0];
cocos2d::Director::getInstance()->replaceScene(scene);
scene->resume();
cocos2d::Director::getInstance()->resume();
}else {
//Start Scene for the first time
//Works well
CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [UIScreen mainScreen].bounds
pixelFormat: kEAGLColorFormatRGBA8
depthFormat: GL_DEPTH24_STENCIL8_OES
preserveBackbuffer: NO
sharegroup: nil
multiSampling: NO
numberOfSamples: 0];
// IMPORTANT: Setting the GLView should be done after creating the RootViewController
auto gl2view = cocos2d::GLView::createWithEAGLView((__bridge void*)eaglView);
cocos2d::Director::getInstance()->setOpenGLView(gl2view);
[self.view insertSubview:eaglView atIndex:0];
auto beam = GrowingBeam::createLayer();
auto scene = beam->getScene();
beam->_self = (__bridge void*) self;
cocos2d::Director::getInstance()->runWithScene(scene);
cocos2d::Application::getInstance()->run();
}
}

Related

Detect which Scene is up in ViewController ios

Basically I have four scenes and on viewcontroller, I want to detect in which scene am I because, I have background music in viewcontroller, and I want to pause music in some Scenes. I found how to detect scenes in viewController, but it was in swift, and I know only basics of objective c.
Edited
SKView *skView =(SKView *)self.view;
TitleScene *sceneTitle = [TitleScene sceneWithSize:self.view.bounds.size];
BonusScene *sceneBonus = [BonusScene sceneWithSize:self.view.bounds.size];
GameScene *sceneGame = [GameScene sceneWithSize:self.view.bounds.size];
DifficultScene *sceneDifficult = [DifficultScene sceneWithSize:self.view.bounds.size];
if(skView){
if(sceneTitle){
NSLog(#"Iam in sceneTitle");
}
if(sceneGame){
NSLog(#"Iam in sceneGame");
}
if(sceneDifficult){
NSLog(#"Iam in sceneDifficult");
}
if(sceneBonus){
NSLog(#"Iam in sceneBonus");
}
else{
NSLog(#"else");
}
}
now, when start app, it runs through every if statement once,even when Iam only in TitleScene. What did wrong?
Give each scene a name, then in your viewcontroller class, just do
SWIFT:
if let skView = view as? SKView, let scene = skView.scene
{
switch(scene.name)
{
case "name1":
default:()
}
}
else
{
//Something is wrong, we do not have an SKView or a SKScene
}
Objective C:
SKView *view = (SKView*)self.view ;
if(view)
{
SKScene *scene = view.scene;
if(scene)
{
NSString *name = scene.name;
//compare names here
}
}

When to use didMoveToView or initWithSize with SpriteKit in xCode 6.4

Since xCode was updated to version 6.0, the default method for SpriteKit in "GameScene" (the default scene created) changes to:
-(void) didMoveToView:(SKView *) view {
/* Scene set up in here */
}
as opposed to the older method:
-(id) initWithSize:(CGSize) size {
if (self = [super initWithSize:size]{
/* Scene set up in here */
}
}
I understand that the new method (as with the changes in the view controller) is used to help manage the import from the .sks file that is new in xCode 6. However, I was curious if I didn't want to use the new "storyboard"-type format that is the .sks file, should I still use the new method? Or should I change the method back to the initWithSize method and just delete the .sks file?
Init methods should be used for initializing but keep in mind that inside init, view is always nil. So any code that needs the view has to be moved to didMoveToView method (it's called immediately after a scene is presented by a view).
About initWithSize in Xcode 6... By default, scene is loaded from .sks file. Because of this, initWithSize is never called actually. initWithCoder is called instead:
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
// do stuff
}
return self;
}
So initializing anything inside initWithSize won't have any effect. If you decide to delete .sks file and create a scene in "old" way, you can do something like this in view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = YES;
// Create and configure the scene.
GameScene *scene = [GameScene sceneWithSize:self.view.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
After that, you can use initWithSize for initialization.
Note that in viewDidLoad the final size of a view may not be known yet, and using viewWillLayoutSubviews instead could be a right choice. Read more here.
And proper implementation of viewWillLayoutSubviews for scene initialization purpose would be:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = YES;
//viewWillLayoutSubviews can be called multiple times (read about this in docs ) so we have to check if the scene is already created
if(!skView.scene){
// Create and configure the scene.
GameScene *scene = [GameScene sceneWithSize:self.view.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}
Swift code:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
let skView = self.view as SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.showsPhysics = 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 */
if(skView.scene == nil){
scene.scaleMode = .AspectFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}
}
}
I think that, for all intents and purposes, there isn't much of a difference between the two methods when it comes to actually setting up your scene and using it. (initWithSize is called when the scene is initialized, while didMoveToView is always called right when the scene is presented by the view). I guess if you really prefer to see the initialization then you could just use the init method without any trouble at all.
Regarding the .sks file:
take a look at your view controller implementation file. Right above the v.controller's methods you'll see:
#implementation SKScene (Unarchive)
+ (instancetype)unarchiveFromFile:(NSString *)file {
/* Retrieve scene file path from the application bundle */
NSString *nodePath = [[NSBundle mainBundle] pathForResource:fileofType:#"sks"];
/* Unarchive the file to an SKScene object */
NSData *data = [NSData dataWithContentsOfFile:nodePath
options:NSDataReadingMappedIfSafe
error:nil];
NSKeyedUnarchiver *arch = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
[arch setClass:self forClassName:#"SKScene"];
SKScene *scene = [arch decodeObjectForKey:NSKeyedArchiveRootObjectKey];
[arch finishDecoding];
return scene;
}
#end
This is the part that handles the sks. Using the file is totally optional, you aren't forced to do it and it really has no impact on you setting up your scene. However, if you do want to work with it, then you'll find that you'll need to work with this code snippet.
Both options are correct. Said that, the init method is not actually the same than the didMoveToView: method, as this last one will be called once the SKScene is presented in a SKView.

Root ViewController not reacting to message from SpriteKit view

I'm using the root viewcontroller to display a couple of labels to keep track of scores etc, which is working fine. However, the root viewcontroller is not responding to messages from the scene right away when the scene is loaded.
The situation is this: UIViewController presents SKView where I initialize MyScene on top of that (normal SpriteKit stuff). Immediately in MyScene I load the level from a json-file, which contains the number of possible points on that specific level. When the level is done loaded, I try to send the number of possible points back to my root viewcontroller, so it can display it in a label.
I've tried setting a property myScene.mainViewController = self from the viewWillLayoutSubviews on ViewController, and then just [self.mainViewController setInitialScore:_bgLayer.maxScore];, but the method setInitialScore never fires. I've also tried using the NSNotificationCenter, but this does not work either.
However, I call a method in self.mainViewController whenever the player collects a point (which happens a little bit later), and that works fine. So the problems seems to be that the ViewController is not done loading or something, so it doesn't respond right away.
How could I solve this?
EDIT 1:
Here's some code by request:
ViewController.m
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
SKView *skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
MyScene* scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
scene.mainViewController = self;
// Present the scene.
[skView presentScene:scene];
self.numLives = bStartNumLives;
self.score = 0;
[self resetLabels];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(setInitialRings:) name:#"setInitialRings" object:nil];
}
}
- (void)setInitialRings:(NSNotification *)notification {
NSLog(#"Initial rings set");
NSDictionary *userInfo = [notification userInfo];
NSInteger rings = [(NSNumber *)[userInfo objectForKey:#"rings"] integerValue];
self.numRings = rings;
[self resetLabels];
}
MyScene.m
- (void)createWorld {
_bgLayer = [self createScenery];
NSLog(#"world created setting initial rings");
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:_bgLayer.numRings] forKey:#"rings"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"setInitialRings" object:userInfo];
_worldNode = [SKNode node];
[_worldNode addChild:_bgLayer];
[self addChild:_worldNode];
self.anchorPoint = CGPointMake(0.5, 0.5);
_worldNode.position = CGPointMake(-_bgLayer.layerSize.width / 2, -_bgLayer.layerSize.height / 2);
}
As you can see from this snippet of code, I'm trying to use the NSNotificationCenter. I've also tried calling [self.mainViewController setInitialRings:_bgLayer.numRings]; manually (but changing the setInitialRings-method to take an integer instead of a notification).

SpriteKit Bool Errors

I am having an error with my code, I am having an issue with my code, So I have a settings bundle that defines whether SpriteKit shows the Nodes and FPS in the scene and this code works, it just when I run it in the simulator it gets an problem at the last line and says it has a problem with the view controller running theScene and doesnt load my scene but when I am in the simulator I can click on the app and it runs all fine, I have no idea what the problem is?
And also, is there a way to reset whether the skView shows the FPS count, when I go back into the app from the homescreen (while the app is running)?
- (void)startScene
{
SKView *skView = (SKView *)self.view;
if(!skView.scene)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL statsForNerds = [defaults boolForKey:#"myKeyName"];
if (statsForNerds)
{
skView.showsFPS = YES;
skView.showsNodeCount = YES;
}
else
{
skView.showsFPS = NO;
skView.showsNodeCount = NO;
}
MyScene *theScene = [MyScene sceneWithSize:skView.bounds.size];
theScene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:theScene];
}
} //Here's the problem line (According to Xcode)
That is not an error, Xcode hit a breakpoint that you set.
A breakpoint stops execution of the running program before the highlighted line is executed, allowing you to see the values of variables and step through code to debug. See: https://developer.apple.com/library/ios/recipes/xcode_help-source_editor/Creating,Disabling,andDeletingBreakpoints/Creating,Disabling,andDeletingBreakpoints.html

My modal keeps eating memory

I am experiencing a rather serious issue with my iPhone app using ARC.
I have a viewcontroller (lets call this A). This viewcontroller opens a navigationcontroller as a modal which runs through 3 different viewcontrollers (lets call these 1, 2 and 3). After viewing number 3 the navigationcontroller closes and we're back to A again.
So the flow is: A opens navigationcontroller and goes through 1->2->3 and then it closes again.
Every time I go through this flow i lose memory. I've searched through all my files looking for any retain og strong properties, un-invalidated timers or similar in order to solve this problem.
I have one idea, which might be the problem. At viewcontroller 1 i present a animation using coreanimation and a sprite. I'm using a implementation made by someone else. It seems like if i disable the animations the memory used seems quite constant (and thereby no memory loss). I have modified the implementation a bit to use ARC. This is the implementation I use for my sprite animations:
MCSpriteLayer.h
//
// MCSpriteLayer.h
//
// Created by Miguel Angel Friginal on 8/20/10.
// Copyright 2010 Mystery Coconut Games. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#interface MCSpriteLayer : CALayer {
unsigned int sampleIndex;
}
// SampleIndex needs to be > 0
#property (nonatomic) unsigned int sampleIndex;
// For use with sample rects set by the delegate
+ (id)layerWithImage:(CGImageRef)img;
- (id)initWithImage:(CGImageRef)img;
// If all samples are the same size
+ (id)layerWithImage:(CGImageRef)img sampleSize:(CGSize)size :(int)useRetina;
- (id)initWithImage:(CGImageRef)img sampleSize:(CGSize)size;
// Use this method instead of sprite.sampleIndex to obtain the index currently displayed on screen
- (unsigned int)currentSampleIndex;
#end
MCSpriteLayer.m
//
// MCSpriteLayer.m
//
// Created by Miguel Angel Friginal on 8/20/10.
// Copyright 2010 Mystery Coconut Games. All rights reserved.
//
#import "MCSpriteLayer.h"
#implementation MCSpriteLayer
#synthesize sampleIndex;
#pragma mark -
#pragma mark Initialization, variable sample size
- (id)initWithImage:(CGImageRef)img;
{
self = [super init];
if (self != nil)
{
self.contents = (__bridge id)img;
sampleIndex = 1;
}
return self;
}
+ (id)layerWithImage:(CGImageRef)img;
{
MCSpriteLayer *layer = [(MCSpriteLayer*)[self alloc] initWithImage:img];
return layer;
}
#pragma mark -
#pragma mark Initialization, fixed sample size
- (id)initWithImage:(CGImageRef)img sampleSize:(CGSize)size;
{
self = [self initWithImage:img];
if (self != nil)
{
CGSize sampleSizeNormalized = CGSizeMake(size.width/CGImageGetWidth(img), size.height/CGImageGetHeight(img));
self.bounds = CGRectMake( 0, 0, size.width, size.height );
self.contentsRect = CGRectMake( 0, 0, sampleSizeNormalized.width, sampleSizeNormalized.height );
}
return self;
}
+ (id)layerWithImage:(CGImageRef)img sampleSize:(CGSize)size :(int)useRetina;
{
CGSize newSampleSize;
if(useRetina == 1) {
// Supporting retina displays
if ([[UIScreen mainScreen] respondsToSelector:#selector(displayLinkWithTarget:selector:)] &&
([UIScreen mainScreen].scale == 2.0)) {
newSampleSize = CGSizeMake(size.width*2, size.height*2);
} else {
newSampleSize = size;
}
} else
newSampleSize = size;
MCSpriteLayer *layer = [[self alloc] initWithImage:img sampleSize:newSampleSize];
return layer;
}
#pragma mark -
#pragma mark Frame by frame animation
+ (BOOL)needsDisplayForKey:(NSString *)key;
{
return [key isEqualToString:#"sampleIndex"];
}
// contentsRect or bounds changes are not animated
+ (id < CAAction >)defaultActionForKey:(NSString *)aKey;
{
if ([aKey isEqualToString:#"contentsRect"] || [aKey isEqualToString:#"bounds"])
return (id < CAAction >)[NSNull null];
return [super defaultActionForKey:aKey];
}
- (unsigned int)currentSampleIndex;
{
return ((MCSpriteLayer*)[self presentationLayer]).sampleIndex;
}
// Implement displayLayer: on the delegate to override how sample rectangles are calculated; remember to use currentSampleIndex, ignore sampleIndex == 0, and set the layer's bounds
- (void)display;
{
if ([self.delegate respondsToSelector:#selector(displayLayer:)])
{
[self.delegate displayLayer:self];
return;
}
unsigned int currentSampleIndex = [self currentSampleIndex];
if (!currentSampleIndex)
return;
CGSize sampleSize = self.contentsRect.size;
self.contentsRect = CGRectMake(
((currentSampleIndex - 1) % (int)(1/sampleSize.width)) * sampleSize.width,
((currentSampleIndex - 1) / (int)(1/sampleSize.width)) * sampleSize.height,
sampleSize.width, sampleSize.height
);
}
#end
Is this implementation somehow not realeasing correctly or retaining anything? Thanks in advance.
Update
- I am using Instruments to measure the memory. I am using the Memory Monitor where I keep an eye on the Physical Memory Free
- The image is created like this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"round_start.png" ofType:nil];
CGSize fixedSize = CGSizeMake(320, 480);
mascot = [MCSpriteLayer layerWithImage:[UIImage imageWithContentsOfFile:path].CGImage sampleSize:fixedSize :0];
mascot.frame = CGRectMake(ANIMATION_X, ANIMATION_Y, ANIMATION_WIDTH, ANIMATION_HEIGHT);
[self.view.layer addSublayer:mascot2];
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"sampleIndex"];
anim.delegate = self;
anim.fromValue = [NSNumber numberWithInt:1];
anim.toValue = [NSNumber numberWithInt:52];
anim.duration = ANIMATION_DURATION;
anim.repeatCount = 1;
[mascot addAnimation:anim forKey:nil];
- I've been experiencing with closing the modal with
[self dismissModalViewControllerAnimated:YES];
and
[self.navigationController dismissModalViewControllerAnimated:YES];
Dismissing the navigation controller does not release it, assuming you have a strong reference to it (that would need to get nil'd). In the view controllers used by it, add a log message in the dealloc method, so you know they are getting dealloced (you can do this for any subclassed item). If needed you can create a simple UINavigation subclass for the sole purpose of adding a message in dealloc). You will find that one or more of these items are not getting dealloced, and then you need to figure out if they are retained by a property/ivar or because they still have a superview.

Resources