cocos2d scene question - where does init() get called within an app? - cocos2d-iphone

I am totally new to cocos2d and Objective C. I just started studying the HelloWorld example that came with cocos2d package, and just couldn't figure out where in the application the -init() function within HelloWorldScene.m is getting called.
Here is the tutorial that I was following:
http://www.bit-101.com/blog/?p=2123
Thanks in advance!

jtalarico is correct. I'd like to expand on his answer a bit.
In general, some form of [init] is called by convention whenever an object gets instantiated. For many objects, [init] is all that is needed, but some objects have more complex forms, such as [initWithSomething].
In Cocos2d, the init function is generally called by the [node] method, which is often used to construct an object in Cocos2d. For example, look in CCNode.m, and you will see this code:
+(id) node
{
return [[[self alloc] init] autorelease];
}
Other objects have other constructors, but this is the main example.
So, if you subclass CCNode, you can override the [init] method and do your own stuff when an object gets created. Just be sure to call [super init] so that CCNode can do its own initialization, too.

The init() method is being overridden in the scene. It is getting called within the base class when an instance of the scene is created. By overriding it, you get the opportunity to fire your own code.

Related

Magical function in SpriteBuilder projects

In cocos2d tutorials people initialize sprites/physical nodes in the init method, but in the cocos2d + SpriteBuilder tutorials it's written in this method:
- (void)didLoadFromCCB {}
In cocos2d only tutorials there is a method like that,but there is a line of code, that says what a kind of method it is,right? :
- (void)onEnter{
[super onEnter];
}
So, 'didLoadFromCCB' is a magical method for SpriteBuilder projects?
The didLoadFromCCB method is sent to each node after the node has been loaded by CCBReader. It runs that method when it is implemented by that node's class. It behaves like a protocol method without having to implement a protocol (nor is there a protocol). See also this github issue.
The CCBReader method that runs didLoadFromCCB does so like this after loading is complete:
+ (void) callDidLoadFromCCBForNodeGraph:(CCNode*)nodeGraph
{
for (CCNode* child in nodeGraph.children)
{
[CCBReader callDidLoadFromCCBForNodeGraph:child];
}
if ([nodeGraph respondsToSelector:#selector(didLoadFromCCB)])
{
[nodeGraph performSelector:#selector(didLoadFromCCB)];
}
}
The onEnter method is defined by the CCNode class, so it is documented in the CCNode class reference and Xcode is able to suggest the method when starting to type it.

CCCallFunc: Selector not being called in inherited class

I'm trying to schedule selectors with CCCallFunc but the selector isn't getting called.
This works:
[self launchCreature];
This does not:
id launchCreatureAction = [CCCallFunc actionWithTarget:self selector:#selector(launchCreature)];
[self runAction:launchCreatureAction];
launchCreature is defined in the parent class, and the CCCallFunc works when used in the parent class. Nothing is showing up in the debugger when I run the action; it just doesn't run the code. (It never hits the breakpoint I have defined at the first line of launchCreature.)
Am I missing about something about how to use CCCallFunc, or how self works in subclasses?
Edit: I misremembered how my code is set up. launchCreature and the code above are BOTH defined in the parent class. The difference between working and not working is that it worked in an instance of the parent class, but doesn't work in an instance of the subclass. This no longer seems to work in the parent class either. Sorry for the confusion.
It works for me. You could try making a call to [super launchCreature] in [self launchCreature] in the subclass and see if the break point is being hit.

Cocos2d HOWTO access CCNode using getChildByTag from another class

Could someone help me, I am trying to access a CCLabelTTF that resides in a CCLayer subclass(GameLayer), but I want to access it from another Player class(also a CCLayer). I thought
[self getChildByTag: DEBUG_LABEL];
searches the scene and finds the object that matches it and returns a pointer to it, since all objects are stored in a tree data structure.
I was able to access the label through trial and error using the follwing code but would appreciate if someone could advise if I am not understanding the getChildByTag method or if there is a way of searching the scene for an object without using the code below.
CCLabelTTF *lbl = (CCLabelTTF *)[[[[CCDirector sharedDirector] runningScene] getChildByTag: GAME_LAYER_TAG] getChildByTag: DEBUG_LABEL_TAG];
Please advise.
getChildByTag only checks direct children of the parent CCNode calling it. It will not check children's children (grandchildren, if you will).
For example, if your node hierarchy looks like this:
MyCCLayer1->MyCCLayer2->MyCCSprite->MyCCLabel
Calling MyCCLayer1 only has direct access to MyCCLayer2 via the getChildByTag call. In turn, MyCCLayer2 could call getChildByTag to get MyCCSprite, and then MyCCSprite could call getChildByTag to get MyCCLabel.

initWithSpriteFrameName not initializing class after upgrade to cocos2d v2.0

I have been building the SpaceViking project described in the book "Learning Cocos2D". I had got to chapter 10 on Box2D when I started to experience problems. I then attempted to upgrade cocos2D from version 1 to version 2.0. After making the necessary changes to fix deprecations, the code no longer works. Specifically, I have found that when game objects or enemy objects are created, their init methods are no longer being called. For example, when the following line is executed:
RadarDish *radarDish = [[RadarDish alloc] initWithSpriteFrameName:#"radar_1.png"];
the RadarDish init method is not called. I then looked into the execution of initWithSpriteFrameName in both version 1 and version 2 and found that CCSprite.m has change such that the init method is no longer called. In the version 2 source code, initWithSpriteFrameName method calls initWithSpriteFrame which calls initWithTexture which calls:
-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect rotated:(BOOL)rotated
{
if( (self = [super init]) ) {
Consequently, RadarDish init method is not called. Instead, CCNode init method is called. However in version 1, initWithSpriteFrameName calls initWithSpriteFrame which calls initWithTexture which has this code:
// IMPORTANT: [self init] and not [super init];
if( (self = [self init]) ){
[self setTexture:texture];
[self setTextureRect:rect];
}
That allows the RadarDish init method to be called.
What can I do to resolve this? It seems unlikely that the book would need to change to support the upgrade to cocos2d v2.0, so I suspect I must be missing something else. But if I am wrong, then what would be the way to change this code to cause the RadarDish init method to be called?
Ok, I was wrong. "cocos2d 2.x is different in many details from v1.x so can't expect v1.x code to just work under 2.x". If you are going to follow the book "Learning Cocos2D", I would recommend loading cocos2d-iphone version 1.0.1. Otherwise, you will be faced with many incompatibilities as well as a lot of deprecations to fix.
But if you really want to use the latest cocos2d, then there are some things you should do. 1) Follow the instructions in this link. 2) You are going to have a lot of deprecations and changes to fix, so use this link to understand how to fix those deprecations and changes. 3) You'll need to update the Joystick classes also, so go to this link to get those changes, 4) you'll have to google the rest to find solutions.
Now as for the solution to the problem I mentioned here, there are likely multiple solutions but I will offer one below (thanks to Sylvan's answer above):
In each of the GameObjects, EnemyObjects, and PowerUps, I added a method to override initWithFrameName. This will circumvent the use of the objects init method. For example, for the RadarDish, I added the following:
-(id) initWithSpriteFrameName:(NSString*)frameName {
if( (self=[super init]) ) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### RadarDish initialized");
[self initAnimations]; // 1
characterHealth = 100.0f; // 2
gameObjectType = kEnemyTypeRadarDish; // 3
[self changeState:kStateSpawning]; // 4
}
}
return self;
}
This allows the GameObject and GameCharacter init methods to run before the CCSprite's initWithSpriteFrameName method to run.
The Viking GameObject had to have a slightly different solution because it is initialized with initWithSpriteFrame rather than initWithSpriteFrameName. But the override implementation is basically the same as the example of RadarDish above.
(Aside from all the changes necessary to overcome the deprecations) The above change allowed everything else in the examples of "Learning Cocos2D" to remain intact.
Good Luck.
I think you are having trouble because you are keeping your init method as it was and not modifying it. You are calling a method initWithSpriteFrameName: on your RadarDish class but that class doesn't have that method name, so it looks to its superclass. I think you could have simply renamed your init method to initWithSpriteFrameName: and you would have been fine. Like this:
// RadarDish.m
-(id) initWithSpriteFrameName:(NSString*)frameName {
if ((self = [super initWithSpriteFrameName:frameName])) {
// init anything here
}
return self;
}
Just don't also have a method named init and expect it to get called.

Cocos2d with ARC: what is the best implementation of singleton pattern for GameScene when having multiple levels?

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:
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
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 :).
I'll focus on the questions on the bottom:
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.
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.