Good game design with multiple levels advices

Forums Programming cocos2d support (graphics engine) Good game design with multiple levels advices

This topic contains 18 replies, has 7 voices, and was last updated by  marciokoko 2 years, 9 months ago.

Viewing 19 posts - 1 through 19 (of 19 total)
Author Posts
Author Posts
May 10, 2011 at 8:52 pm #231202

mrcointreau
@mrcointreau

Hi everybody, it’s been only a couple weeks that I’m entering the cocos2d world, so: I’m a nooooooooob!

I’m getting crazy about the game design pattern, I’ve never programmed a game before and i would need some advices.

I’m trying to make a Super Mario like platform, so I came out with the following solution:

- a GameScene : CCScene , a singleton class for keeping my current level layer, my input layer and my default game player

- a Level : CCLayer , a class which contains the generic instance variable of a level, such as an array of platforms, enemies, ground level, gravity etc.

- some class such as Level1 – Level2 which inherits from Level and are used as specific layer, so that i can load them into my GameScene singleton class

My question is: could this be a good game pattern design or not?

Thanks everybody.

May 10, 2011 at 9:54 pm #329048

skyhawk
Participant
@skyhawk

doubt you need to subclass “a level”. Just have “a level” handle everything.

Also be sure to have a layer that represents your hud, that way it is disassociated from the translations / scaling of “a level”

May 11, 2011 at 10:56 am #329049

mrcointreau
@mrcointreau

Thanks for your reply skyhawk, I will follow your advice about the HUD.

I thought about having a single Level class like this:

@interface Level : CCLayer {
CCArray *platforms;
CCArray *enemies;
float ground;
}

@property (nonatomic, retain) CCArray *platforms;
@property (nonatomic, retain) CCArray *enemies;
@property float ground;

- (NSSet *)collisionDetectedWhileMoving:(CCSprite *)sprite atPosition:(CGPoint)position;

@end

and then subclassing and implement it in single level classes, such as Level1, Level2 etc, which will contain the position of the enemies and of the platforms.

How would you implement this part?

Thanks again :)

P.S.: I’m not using any physics engine, that’s why I need a collisionDetected method, it returns an NSSet containing B if the object has hit the bottom of something, L if the left side of something, etc.

May 11, 2011 at 12:50 pm #329050

vcnzo
@vcnzo

I think you shouldn’t have multiple Level classes, but only 1.

Position of enemies/platforms should be stored in a plist/tmx/what so ever.

May 11, 2011 at 1:37 pm #329051

mrcointreau
@mrcointreau

That’s a pretty cool idea, thanks vcnzo!

May 11, 2011 at 8:04 pm #329052

skyhawk
Participant
@skyhawk

pretty much what @vcnzo said. That way when you go to implement 100 levels, you don’t have to recompile them all in. You just drop new levels in, and just hit “run”.

May 11, 2011 at 10:22 pm #329053

mrcointreau
@mrcointreau

I implemented it with the plist and it really was a success!

But now I’m wondering about the GameScene design, I was thinking about having a GameScene class (singleton) with Player, InputLayer, HUD and Level as instance classes, so that I can handle everything from the GameScene class itself by simple using an update method for managing the communication between its instance classes.

Is it ok or should I use a specific pattern for this?

I’m really sorry for asking you guys all of these questions but this is my first absolute game experience, I read a couple books but, in the end, the experience is the only good teacher.

Thanks.

May 12, 2011 at 1:23 am #329054

Dragyn
Moderator
@dragyn

I’m all in favor of using a Singleton, but I’d advise making your singleton class something outside of your “GameScene”. I usually create a “GameHandler” singleton that I use to hold key pieces of information, like any global game settings (like audio prefs), my primary load/save IO, and core pieces of gameplay data that I want to persist (plaayer name, score, current level number, etc). This GameHandler is always available, and it is really easy to do make my main game scene (and layers) use this info during their init. This lets me go to the next level by setting a couple settings in the GameHandler and then calling replaceScene on my main scene.

If I’m doing something funky, I sometimes will store a reference to a layer in it, too, but I prefer to keep those inside the primary scene instead.

But in the end, whatever way works for you is the right way.

May 12, 2011 at 8:48 am #329055

mrcointreau
@mrcointreau

Thanks for your reply Dragyn :)

This could be a good implementation, which class should be the GameHandler in this case? CCNode?

Because to change my currentLevel scene I use the CCLayer Level class method:

+ (id)sceneWithLevel:(int)levelNumber

My last question is: where should the code about the movement of the player and the collisions with the various platforms should be put?

May 13, 2011 at 2:21 pm #329056

Dragyn
Moderator
@dragyn

I usually use a subclass of NSObject or CCNode for my GameHandler.

You can think of that Singleton as being completely outside the scenes and layers you see on screen.

So all of your actual game play code would be put into either the CCLayer(s) that you have on-screen, or in the objects themselves. In my opinion, player movement should be handled primarily by the player object class itself, but collisions should be handled/sorted out in the layer.

May 13, 2011 at 3:19 pm #329057

Birkemose
Keymaster
@birkemose

I did it this way.

For each different screen I have ( main menu, setup, game ), I created a class based on CCLayer.

I then added controls and stuff needed, to each of the layers, and any methods needed to support them.

As for game mechanics and objects, I add everything to a singleton WORLD class. So I can ex refer to WORLD.player.score from anywhere in the code. I am a strong believer in allocating these things once ( I know “dead space” probably cant ), but in our cases, the class allocations take an insignificant amount of memory compared to ex textures, and will in most cases not bring you in trouble.

So all objects ever used in the WORLD, is allocated once, and because of that, I never have problems with leaks, or defragged memory.

As for stuff like collision, target detection, etc, I place it in the class which will use it. When I create the world, I add every object the player can ex collide with, to a list, and feed this list to the player class. I can then on every tick ( or once every 5.th tick to save cpu time ), check the player against this list. This means that I dont have to worry about not colliding with gun turrets on the ground, or ally object. Just the ones I have on the list. OOP then makes sure, that it doesnt matter if the object is round, square, or anything else.

Same with objects player can shoot. I Add them to a shoot list, and feed it to the player class.

May 14, 2011 at 5:45 am #329058

diogorh
Participant
@diogorh

@Dragyn

As levels are not well covered over the internet (at least i couldn’t find much tutorials), could you post some code on how you implemented your GameHandler? Or someone just give me a hand with my implementation.

So far I have a GameScene where i have:

- (id) initWithLevel:(int)level;

In my SelectLevelScene is my menu with each menu item returning a tag number.

...
[Button1 setTag:1];
[Button2 setTag:2];

-(void)selectStage:(CCMenuItemImage *)btn {

int level = btn.tag;
GameScene * gs = [[GameScene alloc] initWithLevel:level];
[[CCDirector sharedDirector]replaceScene:gs];
}

My question is how i implement the levels and how i call then on the GameScene?

Thanks in advance for any answer!

July 14, 2011 at 5:16 pm #329059

marciokoko
Participant
@marciokoko

Hi, im currently in the same position as you, noob game coder, although I have been coding apps for a couple of years. Ive gone thru the raywenderlich tutorials. So far I haven’t been able to get my question answered.

I have a game designed with different players, enemies & logics.

Stage1:

Player = head

targets = melons

action = eat melons

Stage2:

Player = plane

targets = ufo’s

action = fire projectiles

Stage3:

Player = horse

targets = people

action = must jump them

Stage4:

Player = person

targets = walk into a room and shoot at you

action = dodge projectiles

So my question is, HelloWorldLayer returns a CCScene for me and adds its own layer onto it. Im thinking of creating a Layer Class for each stage. At the end of stage1, i call to remove the HelloWorldLayer layer from my CCScene and add the new PlaneLayer layer to that CCScene. Im trying to understand where to call that code from, because if Im in HWL Class, i cant call to remove the very layer Im inside of, can I?

July 14, 2011 at 11:38 pm #329060

Dragyn
Moderator
@dragyn

@diogorh: Sorry I missed your post originally. Been deep in the coding trenches for my latest project. My answer below I think addesses your questions, if you are still interested.

@marciokoko: My “GameHandler” is really just a Singleton, with a few variables that are used throughout the project. From your description, I’m assuming each level is pretty different. What I would do is implement a Singleton with a “currentLevel” NSInteger. In your CCScene, (the “scene” method itself), I would reference that Singleton, see what “currentLevel” is, and add the layer for that level. So each time you run the scene, it will load the level that the player is supposed to be on. Then, when you want to advance from level 1 to level 2, change the value of currentLevel, and then call a replaceScene to replace the current scene with itself. The new scene will look back at that currentLevel variable, and set itself up with the correct layer.

You could set these all up as separate scenes, but the benefit of this is from other places in your game, like a level select menu, or a “continue game” option, you only have to set the variable, call the same scene, and the scene itself figures out what it should be loading.

This also allows you to use the cocos2D transitions between scenes without having to invent something new to transition one layer out and the new one in (unless you really want to).

This approach is probably because I am too lazy to want to write new class methods like the initWithLevel approach that @diogorh mentioned. I prefer to just call a scene directly, and let it figure out what it’s supposed to be doing.

July 15, 2011 at 8:26 pm #329061

marciokoko
Participant
@marciokoko

I have 2 approaches and I would love someone to validate them and perhaps point out pros & cons:

APPROACH A:

// My Layer
- (id)initWithScene:(MyScene *)scene {
if ((self = [super init])) {
// Store scene in an instance variable
_scene = scene;
}
return self;
}

// MyScene
- (id)init {
if ((self = [super init])) {
_layer1 = [[[MyLayer alloc] initWithScene:self] autorelease];
[self addChild:_layer1];
_currentLayer = layer1;
}
return self;
}

// MyScene
- (void)switchToLayer2 {
[_currentLayer removeFromParentAndCleanup:YES];
_layer2 = [[[MyLayer2 alloc] initWithScene:self] autorelease];
[self addChild:_layer2];

_curentLayer = layer2;
}

// In MyLayer, you can call the method on MyScene like so:
[_scene switchToLayer2];

APPROACH B:

//GameScene.h
@interface GameScene : CCScene {
HelloWorldLayer *_layer1;
PlaneLevel *_layer2;
}

-(void)addLayer1;
-(void)addLayer1;
@end

//GameScene.m
#define kLayer1 1
#define kLayer2 2

@implementation GameScene

-(id)init{
self = [super init];
if (self != nil) {
[self addLayer1];

}
return self;
}

-(void)addLayer1{
_layer1 = [HelloWorldLayer node];
[self addChild:_layer1 z:1 tag:kLayer1];
}

-(void)addLayer2{
_layer2 = [PlaneLevel node];
[self addChild:_layer1 z:1 tag:kLayer2];
}

@end

// And call this in the appDelegate
CCDirector sharedDirector] runWithScene:[GameScene node;

// And in HelloWorldLayer, when the level is complete, call something like:
-(void) endGamePlayeLayer1 {
[self removeFromParentAndCleanup:YES];
GameScene *scene = [GameScene node];
[scene addLayer2];
}

APPROACH A

This seems a little more convoluted, although I like the _currentLayer idea. What confuses me is that MyLayer initWithScene. I dont use the initWithScene in my HelloWorldLayer. Actually it came, template, with a +(CCScene*)scene method which i commented out because I didnt like mixing the Layer Class with the Scene Class.

APPROACH B

This one makes sense to me, Im just calling an instance method of GameScene which is like the boss of all the scenes and layers, telling it to remove one and add the other.

July 15, 2011 at 10:48 pm #329062

Dragyn
Moderator
@dragyn

I’m with you that your Approach A does feel a little convoluted and a little harder for actually tracing out what is actually happening. One drawback to this approach (other than reading it!) is that I think it might be a little more difficult if you decide to use different layers for your scene at the same time. For instance, if you want your HUD to be a separate layer, I’m not exactly sure how you’d integrate that into Approach A.

Approach B is a more modular approach that benefits from keeping your main scene instantiated throughout and you are adding and removing the children. This means you can keep persisted data in your scene without needing external classes to hold it (if you prefer), since you aren’t tearing down the CCScene when you move to a new level/layer. Also, as I mentioned earlier, it would be dead simple to add your HUD layer (if you have one) to the Scene and not have to worry about it as you move from layer to layer – you just leave it alone and everything is happy.

One concern I see in the code for Approach B is that you might want to move the level complete logic to the Scene instead of in the layer. This is because the teardown and built is being done at the parent scene level anyway, so it makes more sense to float it to that level. Also, if you decide you want to do something different about how you get from level to level (cutscenes, etc), then you only have to change it in the CCScene’s method instead of editing every layer to change the behavior. I’m not positive, but you might run into some crashes/odd behavior if you are essentially telling it to go away and get rid of itself, but then you’re calling further code that is being called from that exact object. Depending on how fast the autorelease pool drains, it might throw a SIGABRT if you are still interacting with a dead object. Again, not sure on this last point, but something to keep in mind as you design this.

Good luck!

July 16, 2011 at 4:03 pm #329063

marciokoko
Participant
@marciokoko

Ok so now the logic goes:

1. AppDelegate calls GameScene which inits as above.

2. GameScene inits with layer1

3. layer1 does its thing and upon a win, calls CCDirector sharedDirector] replaceScene:[NewLevelScene node;

4. This is the transition scene b/w layers. It has a method that will call the appdelegate's nextLevel method which does this:

- (void)nextLevel {
//Add to currLevel
_curLevelIndex ++;
//Call GameScene scene
[[CCDirector sharedDirector] replaceScene:_mainScene];
}

5. So I would have to have a switch statement in the GameScene to let it know which layer to add, right? Which in this case would be layer2. Or somehow use the _curLevelIndex variable to call the specific method?

July 16, 2011 at 4:38 pm #329064

Dragyn
Moderator
@dragyn

That code looks like the approach I’d take. Have you tried it out yet?

July 16, 2011 at 11:30 pm #329065

marciokoko
Participant
@marciokoko

Yes it works! Wow Im so surprised. This is fine for a few new layer-levels. But if I were to have many layers I would still need to call a generic method that uses my _curLevelIndex, which is currently unused, and make it call generic method to remove the old layer 0, and replace it with the new layer 1, then remove 1 and replace with 2, etc…

Viewing 19 posts - 1 through 19 (of 19 total)

You must be logged in to reply to this topic.