0
votes

EDIT: I am using ARC

In my code (based on the ShootEmUp example in this book, which I highly reccomend, source code in chapter 8 available here) I often use the trick of accessing the GameScene via:

+(GameScene*) sharedGameScene;

which returns a reference to the static instance of GameScene and is used by all children of GameScene (e.g. ShipEntity, InputLayer.. etc..) to dialog with GameScene (EDIT: aka singleton instance of GameScene).

To create multiple levels I thought of implementing a method calledsceneWithId:(int) method where I load different level data each time.

EDIT: Code snippet of init method:

-(id) initWithId:(int)sceneId
{
    CCLOG(@"scene With id");
    if ((self = [super init]))
    {
     //All common initialization happens here..
     switch (sceneId) {
        case 1:
            [self loadFirstLevelData];
            break;
        case 2:
            [self loadSecondLevelData];
            break;
        default:
            [self loadSecondLevelData];
            break;
    } 
    //Other common stuff..
    [self setUpCounters];
    [self setUpWeaponsMenu];
    [self scheduleUpdate];
    [self schedule:@selector(timerUpdate:) interval:1];
    InputLayerButtons* inputLayer = [InputLayerButtons node];
    [self addChild:inputLayer z:1 tag:GameSceneLayerTagInput];
}

EDIT: Is that init method ok? I have found this post which uses dispatch_once. Should I do the same?

Or should I pheraps create a GameScene class and then sublcass it?

E.g. FirstGameScene : GameScene 

EDIT: I have followed the advice of @LearnCocos2D and used the cleanup method, and I used it to stop a singleton object music layer to play (the MusicLayer object is initialized in AppDelegate and I meant to use it to "manage" the music across all scenes - the problem was that without stopping it in dealloc it would have kept playing the music that was loaded at init time).

-(void) loadFirstLevelData{
//HERE WILL LOAD ALL SPECIFIC ELEMENTS: ENEMIES, BONUSES etc..

//AS WELL AS THE MUSIC FOR THE LEVEL 
[[MusicLayer sharedMusicLayer] _loadMusic:@"1.mp3"];
[[MusicLayer sharedMusicLayer] playBackgroundMusicFile: @"1.mp3"];
}

-(void) cleanup
{
    //Should I remove all child loaded in LoadLevelData?? 
    CCLOG(@"cleanup GameScene");
    [[MusicLayer sharedMusicLayer] stopAllMusic];
    //MusicLayer is not a child of GameScene but of AppDelegate - the idea is to keep loading and unloading music files - sometimes I need to keep playing the file between scenes and hence I used the singleton pattern for this as well..
    [super cleanup];     
}

But I still have some doubts:

  1. Is it ok to have several loadLevelData methods in GameScene class? Each method can be 200 lines long! I tried to sublcass GameScene but is a bit messy. I explain better. I imported "GameScene.h" in the header file of the subclass and by doing so I expected that if I had ovverriden only certain methods (e.g. init) I would have been able to see the various classes imported in GameScene (e.g. InputLayerButtons). It is not the case. So I probably don't understand how imports work in Objective-C

  2. Is it ok to remove specifc children in the cleanup method? I thought that I would remove all child that are added in the LoadLevelXXXData method to reduce the memory usage.

I have set a bounty for this question but I will probably need to test the answer and re-edit as I don't have a clear enough understanding of the subject to be super precise in the question. Hope is ok.

PS: Would be great if someone would feel like sharing a UML style diagram of a Cocos2D Shooter Game where with various levels and GameScene using singleton pattern :).

2
Your question is hard to answer, (most probably due to my lack of experience or..), since it depends on many factors related to the nature of your game. I'll just say that I read the book, and use the pattern all the time by subclassing GameScene, as you suggested. I use this approach not to provide completely new GameScenes, but to tweak the game scene to suite single player mode, multiplayer mode, and online mode.Mazyod
@Mazyod ah, that's great to know. And how did you deal with the load level data? I mean, if you want to add different enemies for each level (in the book, they are added to EnemyCaches) or for example add different ParallaxBackground sublcasses what have you done? :) thanks a lot!mm24
It's a board game, so no levels exist, really :) .. However, I can suggest you make the levels as CCLayers instead, and the GameScene is just initializes the GameLayer using parameters from a plist (for example: { enemies : { Jets : 3, Tanks : 0 , ... } } or something similar .. ?Mazyod
I think you refer this to my previous answer. A singleton sharedGameScene is fine if and only if you make sure the scene does get released at the end of the scene. The sharedGameScene is a static accessor to the instance of the scene while it is live. It should only be used by children of that scene, not by other scenes though.LearnCocos2D
@LearnCocos2D I am using ARC and hence I don't realease automatically. As described above, when I push the MainScene back, the GameScene dealloc doesn't get called because of the singleton instance being still live I guess. My solution would be to have a cleanup method to call before pushing the MainScene. Any other?mm24

2 Answers

1
votes

I'll focus on the questions on the bottom:

  1. Is it ok to have several loadLevelData methods in GameScene class? Each method can be 200 lines long! I tried to sublcass GameScene but is a bit messy. I explain better. I imported "GameScene.h" in the header file of the subclass and by doing so I expected that if I had ovverriden only certain methods (e.g. init) I would have been able to see the various classes imported in GameScene (e.g. InputLayerButtons). It is not the case. So I probably don't understand how imports work in Objective-C

There's nothing wrong with having long methods. However I suspect your loading methods perform very similar routines, so you should check if you can generalize these into subroutines. A good indicator is if several lines of code are absolutely identical except for the parameters or variable names. The best practice is to write identical code only once, and execute it many times with varying parameters.

The #import statement is used to allow the compiler to "see" other classes. Without importing other header files, you couldn't use that class' methods and properties without the compiler complaining.

2 . Is it ok to remove specifc children in the cleanup method? I thought that I would remove all child that are added in the LoadLevelXXXData method to reduce the memory usage.

It makes no sense to remove children during cleanup. Cocos2D removes all children during cleanup automatically.

If that does not seem to be the case for you, you have a retain cycle somewhere that prevents child nodes from deallocating.

0
votes

sorry for answering this but I have been experimenting and decided to:

  • not use the singleton pattern for GameScene
  • use, insteada a singleton object to keep all shared data

My implementation draft for the GameScene (now called ShooterScene) is the following (I followed some advices in a cocos2d-iphone forum post as well as this other one ):

#import "ShooterScene.h"
#import "LevelData.h"
#import "HudLayer.h"


@interface ShooterScene (PrivateMethods)
-(void) loadGameArtFile;
@end

@implementation ShooterScene

+ (id) sceneWithId:(int)sceneId
{
    CCScene *scene = [CCScene node];

    ShooterScene * shooterLayer = [ShooterScene node];
    [scene addChild:shooterLayer];
    [shooterLayer loadGameArtFile]; 

    LevelData * levelData = [LevelData node];
    [shooterLayer addChild:levelData];

    switch (sceneId) {
        case 1:
            [levelData loadLevelDataOne];
            break;
        case 2:
            [levelData loadLevelDataOne];
            break;            
        default:
            break;
    }

    HudLayer * hud = [HudLayer node];
    [hud setUpPauseMenu];
    [shooterLayer addChild:hud];

    return scene;    
}

I am using the update method of ShooterScene to manage the all the game events (E.g. spawning, checking collisions, moving the background layer). I haven't put the full implementation here as is still work in progress, but is just to have an idea of the type of answer I am finding useful.