I am trying to construct a number of scenes in my GameManager singleton init.
The scene is created via
- (id)init
{
self = [super init];
if (self) { // 'mainScene' is an autorelease object.
mainScene = [CCScene node];
...
}
GameManger holds a strong reference to mainScene:
#interface GameManager : NSObject
{
CCScene* mainScene;
}
But if I try to push the scene later with
[[CCDirector sharedDirector] pushScene:mainScene];
I get EXC_BAD_ACCESS
If I create and immediately push then everything works. Shouldn't the default __strong reference keep the object allocated?
Thanks in advance for any help....
Figured it out ... [CCScene node] is a convenience factory method that does:
[[[self alloc] init] autorelease];
but since I am using arc...I dont want that - I want
mainScene = [[CCScene alloc]init];
instead of
mainScene = [CCScene node];
Related
I don't understand why the following is happening and I hope someones here can explain it.
I have a GameLayer (CCLayer) Class and a Food (CCNode) Class.
In the Gamelayer Class I create a bunch of food Objects that have a sprite as a property.
And I want to add these sprites to a CCSpriteBatchNode.
spriteBatch = [CCSpriteBatchNode batchNodeWithFile:#"bol.png"];
[self addChild:spriteBatch];
for (int i = 0; i < 1000; i++) {
Food * tempfood = [Food foodWithParentNode:self];
[spriteBatch addChild:[tempfood mySprite]];
[self addChild:tempfood];
}
When I use the code above, the sprites all show up on screen but don't move. (They should because I scheduled an Update in the Food Class (see below) and in this update the position of the food objects is changed)
-(id)initWithParentNode:(CCNode *)parentNode{
if((self = [super init]))
{
mySprite = [CCSprite spriteWithFile:#"bol.png"];
CGSize screenSize = [[CCDirector sharedDirector] winSize];
[[self mySprite] setPosition:CGPointMake(screenSize.width/2, screenSize.height/2)];
[self scheduleUpdate];
}
return self;
}
-(void) update:(ccTime)delta
{
... DO SOME position calculations ...
[[self mySprite] setPosition:CGPointMake(newX, newY)];
}
But if I move the code that adds the sprite to the batch from the Game class to the food class the update that changes the position does work and the foods are moving on screen.
BUT WHY?
So this gives:
-(id)initWithParentNode:(CCNode *)parentNode{
if((self = [super init]))
{
mySprite = [CCSprite spriteWithFile:#"bol.png"];
CGSize screenSize = [[CCDirector sharedDirector] winSize];
[[self mySprite] setPosition:CGPointMake(screenSize.width/2, screenSize.height/2)];
[[ (GameLayer*) parentNode spriteBatch] addChild:mySprite];
[self scheduleUpdate];
}
return self;
}
I really can't see the difference between calling
[[ (GameLayer*) parentNode spriteBatch] addChild:mySprite];
from the food class or :
[spriteBatch addChild:[tempfood mySprite]];
from the 'parent' GameLayer
Ruben, the mySprite is a property with retain attribute?
The Food class can be loosing the memory reference of this property...
on init, try to set mySprite using self.mySprite, to retain this.
on .m or .h, put:
#property (nonatomic, retain) CCSprite *mySprite
and on init, use:
self.mySprite = [CCSprite spriteWithFile:#"bol.png"];
To be able to pan and zoom some of the content in screen I decided to use CCLayerPanZoom extension. When I look at the source code I can see that it derives from the CCLayer class. So I change the parent class of the node that's pushed to the navigation stack from CCLayer to CCLayerPanZoom. But what I get when the app launches is just a black screen. To make it cleaner, I created a new class, derived it from CCLayerPanZoom, added a test sprite onto it in the init method and pushed it to the navigation stack in the AppDelegate.m. Still I got nothing, just a black screen. Here're the two methods that I've implemented in my class:
#interface TestPanZoom : CCLayerPanZoom {
}
+(CCScene *) scene;
#end
#implementation TestPanZoom
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
TestPanZoom *layer = [TestPanZoom node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id)init{
if(self=[super init])
{
CCSprite *sprite=[CCSprite spriteWithFile:#"Default.png"];
sprite.scale=0.5;
[self addChild:sprite];
}
return self;
}
#end
I've got an end of level layer added to game, each level has its own scene. I want to be able to restart the current scene. Obviously the scene will change but the layer will remain the same. How is this done. I've tried-
CCScene *currentScene = [[CCDirector sharedDirector]runningScene];
[[CCDirector sharedDirector]replaceScene:currentScene];
Thanks
This does not work because you can't replace the same scene object with itself:
CCScene *currentScene = [[CCDirector sharedDirector]runningScene];
[[CCDirector sharedDirector]replaceScene:currentScene];
Instead you have to create a new instance of your scene, like so:
[[CCDirector sharedDirector] replaceScene:[YourSceneClass scene]];
If you don't know what the current scene class is, then this ought to work:
CCScene *currentScene = [CCDirector sharedDirector].runningScene;
CCScene *newScene = [[[currentScene class] alloc] init];
[[CCDirector sharedDirector] replaceScene:newScene];
Assuming you're using ARC as everyone should these days. Otherwise add an autorelease.
I ran into the same problem. I tried this
CCScene *currentScene = [CCDirector sharedDirector].runningScene;
CCScene *newScene = [[[currentScene class] alloc] init];
[[CCDirector sharedDirector] replaceScene:newScene];
and it gave me a blank screen.
The problem is this line
CCScene *newScene = [[[currentScene class] alloc] init];
[currentScene class] actually returns CCScene..
Hence
[CCScene alloc] init] gives us a blank screen.
The way how I got around this problem was by setting tag for each of my scene class.
For example:
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
scene.tag = 1;
// 'layer' is an autorelease object.
GameOneLayer * layer = [[[GameOneLayer alloc] init];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
Hope this helps.
I would be very grateful for any advice you can offer as I am growing increasingly frustrated with an issue I am having - I also appreciate that the issue I am having is due to my lack of knowledge / understanding.
In an attempt to further my knowledge and stretch myself, I have chosen to create a PlayerStats class that handles the players scoring - and in time, health, etc.
I have the GameLevelLayer and PlayerStats classes implemented as follows:
GameLevelLayer.m as follows:
#import "GameLevelLayer.h"
#import "Player.h"
#import "PlayerStats.h"
#interface GameLevelLayer() {
CCTMXTiledMap *map;
Player *player;
PlayerStats *playerStats;
}
#end
#implementation GameLevelLayer
#synthesize grabber = _grabber;
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
GameLevelLayer *layer = [GameLevelLayer node];
PlayerStats *hudLayer = [PlayerStats node];
[scene addChild: layer];
[scene addChild: hudLayer];
return scene;
}
-(id) init {
if( (self=[super init]) ) {
CGSize screenSize = [[CCDirector sharedDirector] winSize];
playerStats = [[PlayerStats alloc]init];
...........
}
PlayerStats.m is as follows:
-(id) init
{
if ((self = [super init])) {
CGSize screenSize = [[CCDirector sharedDirector] winSize];
score = [CCLabelTTF labelWithString:#"Score : 0" dimensions:CGSizeMake(100,20) hAlignment:UITextAlignmentRight fontName:#"Marker Felt" fontSize:(18.0)];
int margin = 5;
score.position = ccp(screenSize.width - (score.contentSize.width/2) - margin, score.contentSize.height/2 + margin);
[self addChild:score z:99];
}
return self;
}
-(void)numberOfItemsCollected:(int)collected {
NSString *str = [score string];
CCLOG(#"What does the label say %#", str);
// This is actually displaying the correct string of what the score should be ..
[score setString:[NSString stringWithFormat:#"Score : %d", collected]];
}
When (from the GameLevelLayer.m) I initiate
[playerStats numberOfItemsCollected:5];
the CCLog shows that the label should show Score : 5 but the label itself does not update.
Any advice would be greatly appreciated as I am very aware that I am misunderstanding the issue.
I think the issue is to do with the Layer that I am updating not being the one I believe it is....
Thanks in advance.
I had declared the CCLabelTTF *score as an instance variable in the PlayerStats Class header. What a difference a night away from code makes.
Finally I managed to solve this issue set string to an empty string then reSet it to your string
[label setString:#""];
[label setString:yourString];
Here's the code I know to change scenes with cocos2d:
[[CCDirector sharedDirector] replaceScene:[HelloWorld scene]];
But I wonder if it's possible I can switch the scenes with some parameters.
I tried this method:
HelloWorld *scene = [HelloWorld scene];
[scene initWithInput:0];
[[CCDirector sharedDirector] replaceScene:scene];
-(void)initWithInput:(int)input is what I wrote for test in HelloWorld class.
And it does't work, does any one know how to do it?
Try overriding the scene method. Something like
+(id) sceneWithInput:(int) i
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorld *layer = [HelloWorld nodeWithInput:i];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
Then you would call
[[CCDirector sharedDirector] replaceScene:[HelloWorld sceneWithInput:0]];