Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Objects in other Layers (cocos2d)

I was playing around with a Joystick moving a Sprite around in one layer. Now, according to best practice, the Joystick and the Sprite have to be in different Layers of the same scene. I have managed to separate these, yet I am now completely stuck having absolutely no clue whatsoever how to pass joystick commands from one layer to another? What is the recommended way to do this?

Scene

  • Game Play Layer
    • Sprite
  • Game Control Layer
    • Joystick

When the joystick is manipulated I need to pass this info to the GamePlayLayer to control the sprite.

like image 393
clops Avatar asked Mar 01 '12 17:03

clops


1 Answers

Well, I got a great Cocos2d book, written by Rod Strougo and Ray Wenderlich, by the name "Learning Cocos2d". In their book, they implement a game, which has a joystick implemented and all, using your initial setup. The GamePlayLayer contains both the joyStick and the hero sprite. (See book page 40).

I don't believe they would use bad practices in their book, given they are very talented!

...

With that being said, I have possible solutions, if you wish to implement them on separate layers:

GameScene
|
|__>GameLayer
|
|__>ControlLayer

That's your basic setup. But, intuitively, what is the purpose of the control layer? Control the game layer's content! So, I would suggest you hold a (weak) reference to the GameLayer within the ControlLayer. That way, using a simple:

@property (nonatomic, assign) CCSprite* hero;

you now have access to the hero from the ControlLayer!

Extra (if needed):

//GameScene init:
- (id)init {
    ....
    gameLayer = [GameLayer node];
    controlLayer = [ControlLayer node];
    [controlLayer setGameLayerRef:gameLayer];
    ...
}

// Control layer:
@property (nonatomic, assign) GameLayer* gameLayerRef;

Even though I just suggested that way, I don't use it in my games :)

What I normally do is:

Make the GameScene class a "Semi-Singleton". (I learned this method from "Learn iPhone and iPad Game Dev" By Itterheim (aka gaming horror, Kobold2d publisher ... etc).

Then, inside the control layer, I would call the GameScene object:

[[GameScene sharedScene] doSomethingToTheGameLayer];

Yeah, the gameScene has simplistic methods that just relies what the control need to update in the game layer.


Edit:

Implementing the Semi-singleton pattern, as described by Itterheim in his book.

But, what is semi-singleton?

It has the singleton pattern's property: you can access the object instance from anywhere using a static call.

[GameScene sharedScene];

However, singleton objects are usually retained, after being created for the first time, till the end of the application's life. In the Semi-singleton pattern, this is not the case.

Once you create the instance, you cannot create another instance before destroying the old one, BUT once you are done with the instance, you destroy it (dealloc). Creating another one when necessary.

Recap: Semi-Singleton: Create many object from it, but only one at any given time. Only recreate after destroying the old.

Implementation:

Of course, as you do with any singleton class, you first declare a static variable of the same type of the class:

//In GameScene.m
static GameScene* instance = nil;

@implementation

//Here we have the static method to access the instance:
+(GameScene*)sharedScene {
    //you must have created one before trying to access it "Globally".
    ///Typically, created where you transition the GameScene using CCDirector's replaceScene.
    NSAssert(instance != nil, @"GameScene not instantiated!!");
    return instance;
}

-(id)init {
    if((self = [super init])) {
        //Make sure you don't have another instance of the class created
        //NOTE: Possible race condition that I wouldn't worry about, since it should never happen.
        NSAssert(instance == nil, @"Another instance is already created!!");
        instance = self;

        ...
    }
    return self;
}

//Don't forget!! Dealloc!!
- (void)dealloc {
    //the only instance we had is dealloc'd! We are back to zero. Can create a new one later! :D
    instance = nil;
    [super dealloc];
}

Edit2:

So, the timeline:

CCScene* scene = [GameScene node];
[[CCDirector sharedDirector] replaceScene:scene];
...
[[GameScene sharedScene] doSomething];
...
[[CCDirector sharedDirector] replaceScene:otherScene];
//After the new scene replaces GameScene, dealloc will be called, implicitly. Making instance = nil;
instance = nil;
[super dealloc];
...
//Loop again
CCScene* scene = [GameScene node];
[[CCDirector sharedDirector] replaceScene:scene];
...
like image 190
Mazyod Avatar answered Oct 11 '22 12:10

Mazyod