I have a level scene in my game in which I am selecting the level. In this scene I am displaying the level selected in a CCLabelTTF. Now I want to pass the value displayed on this label to my main scene. I am doing this as follows:
HelloWorld *hello=[HelloWorld getInstance]; //HelloWorld is main scene
hello.strLevel=[lblLevel string]; //strLevel is NSString to which I am passing the label text
[[CCDirector sharedDirector]replaceScene:[HelloWorld node]];
In my HelloWorld scene I am using singleton to share the value of label used in Level scene.
//HelloWorld.h
#interface HelloWorld : CCColorLayer
{
NSString *strLevel;
}
#property(nonAtomic,retain)NSString *strLevel;
+(HelloWorld*)getInstance;
HelloWorld.mm
#implementation HelloWorld
#synthesize strLevel;
static HelloWorld *instance=nil;
__________________________
//Some code
__________________________
+(HelloWorld*)getInstance
{
if(instance==nil)
{
instance=[HelloWorld new];
}
return instance;
}
However this isn't working.As soon as control reaches
instance=[HelloWorld new];
init() is called. And why not. However when the control reaches back to Level scene at the line where I am passing the value, nothing happens and HelloWorld shows the value null for strLevel.
I know singleton is a better way to pass values than AppDelegate. But I am unable to do so.Can someone correct me?
Thanks
Use singleton. What should my Objective-C singleton look like? this is a good discussion on the singleton in obj-c. good luck
[EDIT]
HelloWorld *hello=[HelloWorld getInstance]; //HelloWorld is main scene
hello.strLevel=[lblLevel string]; //strLevel is NSString to which I am passing the label text
[[CCDirector sharedDirector]replaceScene:[HelloWorld node]];
The HelloWorld instance that you're passing to the replaceScene is not the same as
the HelloWorld *hello you passed the singleton instance to. That's why there is no strLevel value in it. The strLevel value is placed in your HelloWorld singleton though. Try
NSLog(#"%#",[[HelloWorld getInstance] strLevel]); //somewhere in the code
Related
I'm building a cross platform app, and 1 of those platforms is Macos.
Now the core of my code is written in C++, and Obj-C++.
I create a window like this:
NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(x, y, width, height) styleMask:macosWindowStyle backing:NSBackingStoreBuffered defer:false];
but I wanted to listen to the window. I could've subclassed it, but I chose not to.
The other way to get events from the NSWindow was to set a delegate.
Now since my code was in Obj-C++, I couldn't have a C++ class inherit from a Obj-C protocol.
So, I created a Obj-C header, which implemented NSWindowDelegate.
Here it is:
#interface SomeClass : NSObject<NSWindowDelegate>
#end
I overrode windowShouldClose as such:
- (BOOL)windowShouldClose:(NSWindow *)sender {
NSLog(#"Hello!");
return true;
}
and in my Obj-C++ file, I did this:
NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(x, y, width, height) styleMask:macosWindowStyle backing:NSBackingStoreBuffered defer:false];
SomeClass* someClass = [[SomeClass alloc] init];
[window setDelegate:someClass];
However, when I pressed the X button, nothing happened.
I then proceeded to test the same thing in Swift, same result.
I then realized that the delegate was being destroyed because it was a weak reference.
My question is, how do I get around this?
OK, I figured it out.
I for some reason thought I could not have Obj-C class pointers in my Obj-C++ code. I thought that was one of the limitations.
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.
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.
I have two CCMenu instances. At some point in the game, menu A is overlapped by menu B. However, when I press a button within menu B, the one that "gets it" is menu A.
How can I give touch priority to CCMenu B?
I tried this:
[[CCTouchDispatcher sharedDispatcher] setPriority:-130 forDelegate:menuB];
However, Xcode says that this delegate (menuB) was not found.
Okay, I fixed this, but I still think there should be a better way.
First, we have to edit CCMenu's interface. We have to create a new integer property.
#interface CCMenu : CCLayer <CCRGBAProtocol>
{
tCCMenuState state_;
CCMenuItem *selectedItem_;
GLubyte opacity_;
ccColor3B color_;
int extraTouchPriority; // Our new integer
}
#property (readwrite) int extraTouchPriority;
Now change the registerWithTouchDispatcher method to this:
-(void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority - extraTouchPriority swallowsTouches:YES];
}
Done. Now, when you have to give your CCMenu instance more priority than others, just give a higher extraTouchPriority value to it after initializing it.
I had the same problem. What i did is copied the entire CCMenu from cocos2d library, renamed it and then modified kCCMenuTouchPriority to what i wanted. Note that you have to rename kCCMenuTouchPriority for the custom menu. I used kkCCMenuTouchPriority.
I called it in code like this:
CCMenuPopUp *menu =[CCMenuPopUp menuWithItems:item1,nil];
I tried to subclass it but i ran into some problems and gave up and gone with the solution above.
the CCTouchDispatcher thing doesn't work because the menu isn't inited yet when you call it
Here's anoter variation on one of the anwers above, which doesn't alter the cocos2D code base, because that is bad practice: https://gist.github.com/tudormunteanu/6174624
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.