# Copies of GameScene created when called additional times

UPDATE: It no longer crashes after removing [self removeAllChildrenWithCleanup:YES]; from onExit. The artifacts still remain though.

UPDATE 2: Found my problems. See my answer below.

I have a game with a level select managed by a SceneManager, which basically just uses ReplaceScene. The first time I load a level everything works fine. On subsequent calls, for example: completing the level and continuing to the next, things blow up. The level loads fine, but when I try to pan the map or try to move the player the game crashes. Debugging through I found that there are multiple occurrences of self and related children like player and mapLayer.

As a test, I put this code in my ccTouchesBegan:

NSLog(@"test %i", [self retainCount]);


The first time a level is loaded, it gives:

test 2


The second time I load a level it gives:

test 2
test 1


as in it spits out both values by looping through twice, not just appending an output to the last. It continues with this pattern for each subsequent load. So the third time will give 2 1 1.

Particular code that causes the game to crash involve calling _tileMap.tileSize because there is a second GameScene with a tileMap that was supposedly destroyed, so it has tileSize and mapSize of 0.

I noticed dealloc doesn't really ever get called, so I tried to manage some things with -(void) onExit

-(void) onExit
{
[self unscheduleAllSelectors];
[_player stopAllActions]; //stop any animations just in case. normally handled in ccTouchesEnded
[self removeAllChildrenWithCleanup:YES];
}


I never replace the GameScene while I'm in a GameScene; if the level is completed it goes to a GameOver scene, or I use a back button that goes to the LevelSelect scene.

This is [the relevant parts of] my init, in case something like the adding of children matters:

-(id) init
{
_mapLayer = [CCLayer node];

int selectedChapter = gameData.selectedChapter;
int selectedLevel = gameData.selectedLevel;

//loop until we get selected level, then do stuff
for (Level *level in chapterLevels.levels) {
if (level.number == selectedLevel) {
_tileMap = [CCTMXTiledMap tiledMapWithTMXFile:level.file];
}
}

_background = [_tileMap layerNamed:@"Background"];
_foreground = [_tileMap layerNamed:@"Foreground"];
_meta = [_tileMap layerNamed:@"Meta"];
_meta.visible = NO;

//initialize Spawn Point object and place player there
CCTMXObjectGroup *objects = [_tileMap objectGroupNamed:@"Objects"];
NSMutableDictionary *spawnPoint = [objects objectNamed:@"SpawnPoint"];
int x = [[spawnPoint valueForKey:@"x"] intValue] / retinaScaling;
int y = [[spawnPoint valueForKey:@"y"] intValue] / retinaScaling;

//setup animations
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"MouseRightAnim_24x21.png"];
NSMutableArray *rightAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 3; ++i) {
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:@"MouseRight%d_24x21.png", i]]];
}
CCAnimation *rightAnim = [CCAnimation animationWithSpriteFrames:rightAnimFrames delay:0.1f];
self.player = [CCSprite spriteWithSpriteFrameName:@"MouseRight2_24x21.png"];
_player.position = ccp(x, y);
self.rightAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:rightAnim]];
rightAnim.restoreOriginalFrame = NO;

//get map size in pixels
mapHeight = _tileMap.contentSize.height;
mapWidth = _tileMap.contentSize.width;
//setup defaults
//this value works well for the calculation later, trial and error really
distance = 150;
lastGoodDistance = 150;
mapScale = 1;

[self setViewpointCenter:_player.position];

self.isTouchEnabled = YES;
}
return self;
}


And here's the SceneManager code for replacing scenes:

+(void) goGameScene
{
CCLayer *gameLayer = [GameScene node];
[SceneManager go:gameLayer:[GameHUD node]];
}

//this is what every call looks like besides the GameScene one above
+(void) goLevelSelect
{
[SceneManager go:[LevelSelect node]:nil];
}

+(void) go:(CCLayer *)layer: (CCLayer *)hudLayer
{
CCDirector *director = [CCDirector sharedDirector];
CCScene *newScene = [SceneManager wrap:layer:hudLayer];
if ([director runningScene]) {
[director replaceScene:newScene];
}
else {
[director runWithScene:newScene];
}
}

+(CCScene *) wrap:(CCLayer *)layer: (CCLayer *)hudLayer
{
CCScene *newScene = [CCScene node];
if (hudLayer != nil) {
}
return newScene;
}


Any ideas why I'm getting these fatal artifacts? I'm hoping this isn't considered too localized since it basically combines 3 tutorials that anyone could end up following. (Ray Wenderlich Animations, Tim Roadley Scene Manager, Pan and Zoom with Tiled Maps.

• Do you happen to use blocks in any of the codes further in your class? If you do, please provide those as well. There are cases where blocks causes a retain loop which essentially prevents the object from being deallocated so that might be the problem. Plus, I don't see the code for the replace scene, that might be useful to see as well. – Jamornh Oct 26 '12 at 16:51
• @Jamornh Nope, no blocks anywhere. And I'll add the replace scene code to the question. – Orin MacGregor Oct 26 '12 at 16:52
• Ok, so I fixed the crashing by removing [self removeAllChildrenWithCleanup:YES]; from onExit because it was causing an over-release problem with _tileMap. However, the problem of the artifacts still remains, just without fatal (for now) consequences. For now the problem has been demoted to a memory leak problem. – Orin MacGregor Oct 26 '12 at 17:56

Found my problems.

I had to add [super onExit] to onExit. Missing this was causing dealloc to not fire.

I also had to remove the following lines from dealloc:

self.tileMap = nil;
self.background = nil;
self.meta = nil;
self.foreground = nil;
self.fogOfWar = nil;
self.mapLayer = nil;


They are all the ones related to the tilemap, and they were causing over-releases once I was able to hit dealloc. It's weird though; they're declared as retain in the header file.