I have created a loading scene separate class derived from CCScene which I can reuse in other projects. I have added a label called "Loading.." inside it.
#interface LoadingScene : CCScene
{
TargetScenes targetScene_;
}
I want to give effects to label while other scene is loading but I am not able to do so..
In .m file here goes the code for adding labels:
-(id) initWithTargetScene:(TargetScenes)targetScene
{
if ((self = [super init]))
{
targetScene_ = targetScene;
CCLabelTTF* label = [CCLabelTTF labelWithString:#"Loading ..." fontName:#"Marker Felt" fontSize:64];
label.color = ccWHITE;
CGSize size = [[CCDirector sharedDirector] winSize];
label.position = CGPointMake(size.width / 2, size.height / 2);
CCBlink *blink = [CCBlink actionWithDuration:2 blinks:10];
[label runAction:blink];
[self addChild:label];
}
return self;
}
The blink is not work.. None of the actions are working and giving me big headache...
Please can someone let me know the reason behind it? Also, How to overcome it?
Have you overridden onEnter in your class? Did you remember to call [super onEnter]?
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"];
I am developing a simple game app.
I am required to have two screens one on left & other on right, just like on scroll view with pagination having 2 screens to toggle between. I am detecting swipes with the help of RootViewController class and successfully swiping left and right screens.
But the problem is I have to have infinitely running rotateBy action on both screens each running this action on single sprite placed on center of screen.
I am using a main scene called SelectWorld & two are it's sub scenes.
First screen is called factory & other is called stack.
Following is my code:
Select World Screen:
#implementation SelectWorld
+(CCScene*)scene
{
CCScene* scene = [CCScene node];
SelectWorld* layer = [SelectWorld node];
[scene addChild:layer];
return scene;
}
-(id)init
{
if((self = [super init]))
{
[RootViewController singleton].swipeCallBackHandler = self;
[[RootViewController singleton] enableSwipeDetection];
factory = [[ColorfulFactory scene] retain];
stack = [[CoinsStack scene] retain];
[self addChild:factory];
isOnFactory = YES;
}
return self;
}
-(void)swipedLeft
{
if(isOnFactory)
{
[self removeAllChildrenWithCleanup:YES];
isOnFactory = NO;
CCTransitionScene* transitionalScene = [CCTransitionSlideInR transitionWithDuration:0.3 scene:stack];
[[CCDirector sharedDirector] replaceScene:transitionalScene];
}
}
-(void)swipedRight
{
if(!isOnFactory)
{
[self removeAllChildrenWithCleanup:YES];
isOnFactory = YES;
CCTransitionScene* transitionalScene = [CCTransitionSlideInL transitionWithDuration:0.3 scene:factory];
[[CCDirector sharedDirector] replaceScene:transitionalScene];
}
}
Here's the code for factory only, same goes for stack-
#implementation ColorfulFactory
+(CCScene*)scene
{
CCScene* scene = [CCScene node];
ColorfulFactory* layer = [ColorfulFactory node];
[scene addChild:layer];
return scene;
}
-(id)init
{
if((self = [super init]))
{
CGSize size = [CCDirector sharedDirector].winSize;
CCLabelTTF* info = [CCLabelTTF labelWithString:#"Colorful Factory" fontName:#"Helvetica" fontSize:35.0];
info.position = ccp(size.width/2, size.height-50);
[self addChild:info];
CCSprite* sprite = [CCSprite spriteWithFile:#"Icon.png"];
sprite.position = ccp(size.width/2, size.height/2);
[self addChild:sprite];
sprite.tag = 123;
CCRotateBy* rotateBy = [CCRotateBy actionWithDuration:60 angle:360.0];
CCRepeatForever* repeatForever = [CCRepeatForever actionWithAction:rotateBy];
[sprite runAction:repeatForever];
repeatForever.tag = 456;
}
return self;
}
Now the problem is, in first two swipes actions are running fine, but as soon as I try to swipe more than two or three times, actions got stopped. I haven't written a single line to stop this in both the classes. I require it there running forever.
Your mistake is that you're re-using the same scene object for every scene change. You can't do that in cocos2d, you have to create a new instance of the scene object.
You should never retain a scene object, since there ought to be only one active scene at any one time (with the exception of the duration of a scene transitioning to another scene - but that's handled by cocos2d internally).
For example:
CCScene* factory = [ColorfulFactory scene];
CCTransitionScene* transitionalScene = [CCTransitionSlideInR transitionWithDuration:0.3 scene:factory];
[[CCDirector sharedDirector] replaceScene:transitionalScene];
If you need both objects in memory, make them layers (or nodes), not scenes. Don't use replaceScene but instead animate the layers with regular moveTo actions in and out of the screen.
I don't know whats wrong, maybe because i'm using more layers?
I think that my other layer (sliding menu grid subclass is stealing the touches...)
//
// BGLayer.m
// MainProject
//
// Created by NSSL1 on 8/30/12.
// Copyright (c) 2012 MyCompanyName. All rights reserved.
//
#import "BGLayer.h"
#import "GameManager.h"
#import "MainMenuLayer.h"
#interface BGLayer()
-(void)displayBGMenu;
#end
#implementation BGLayer
-(void)backtomenu:(CCMenuItem*)itemPassedIn{
CCLOG(#"why I can't reach here?");
[[GameManager sharedGameManager] runSceneWithID:kMainMenuScene];}
-(id)init {
self = [super init];
if (self != nil) {
self.isTouchEnabled=YES;
[self displayBGMenu];
}
return self;
}
-(void)displayBGMenu{
NSString* backLabelstring = [NSString stringWithFormat:#"Back to Menu"];
CGSize screenSize = [CCDirector sharedDirector].winSize;
//Shadow
CCLabelTTF *backLabel = [CCLabelTTF labelWithString:backLabelstring fontName:#"Marker Felt" fontSize:32];
backLabel.position=CGPointMake(screenSize.height*0.5f, screenSize.width*0.1f);
backLabel.color = ccBLUE;
CCMenuItem* backbtnitem=[CCMenuItemLabel itemWithLabel:backLabel target: self
selector:#selector(backtomenu:)];
[backbtnitem setTag:21];
menu = [CCMenu menuWithItems:backbtnitem ,nil];
menu.position = CGPointMake((screenSize.width / 2), screenSize.height*0.1f);
menu.tag = 200;
[self addChild:menu z:20 tag:200];
}
#end
I had an issue with layers swallowing touches in the same type of class, and to avoid this someone suggested me to add this in the grid button item class init method:
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES];
The effect is that the class calling this method would not be greedy towards the parent's layer touch detector by detecting only the touches in its area, and would allow the user to scroll the menu in a smooth way as well as pressing other items in the parent layer (such as your menu).
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];
I am coding a modal layer in cocos2d and I would like to use the OpenGL glScissor API call to crop the inside of a CCScrollLayer that I am using,
Basically,
Present some kind of Modal sprite
Put CCScrollLayer with assets on the modal sprite
I want to crop the CCScrollLayer so that it does not overflow the sprite.
Seems pretty simple.
The problem I'm having is that the visit function never seems to be get hit and thus glScissor never gets implemented.
I'm not sure what I'm doing wrong.
The way I do modals is I use blocks to handle a Yes and No condition/state.
// This appears in my "Pick City scene"
-(id) init
{
if ((self = [super init]))
{
NSLog(#"City list");
for (City *cityObj in listOfCities)
{
citySpriteOff = [CCSprite spriteWithSpriteFrameName:#"city.png"];
citySpriteOn = [CCSprite spriteWithSpriteFrameName:#"city_on.png"];
int x = [cityObj.x intValue];
int y = [cityObj.y intValue];
CCMenuItemSprite *mapMenuItem = [CCMenuItemSprite itemFromNormalSprite:citySpriteOff selectedSprite:citySpriteOn target:self selector:#selector(btnCity:)];
[mapMenuItem setTag:i];
[mapMenuItem setIsRelativeAnchorPoint:YES];
[mapMenuItem setAnchorPoint:CGPointMake(0, 0)];
[mapMenuItem setPosition:CGPointMake(x, y)];
[mapMenuItem setIsEnabled:YES];
[mapMenu addChild:mapMenuItem];
i++;
} // next
[self addChild:mapMenu z:2];
}
}
-(void) btnCity:(id)sender
{
NSLog(#"clicked a city button");
int tag = [sender tag];
City *cityObj = [listOfCities objectAtIndex:tag];
NSLog(#"You clicked on city: %#", cityObj.name);
[self lockLayers];
CCLayer *layer = [CCLayer node];
[self addChild:layer z:100];
[MapModalLayer ConfirmCity:cityObj onLayer:layer yesBlock:^{[self btnConfirmedCity:cityObj];} noBlock:^{[self unlockLayers];}];
}
-(void) btnConfirmedCity:(City *)cityObj
{
NSLog(#"confirmed city: %#", cityObj.name);
}
#pragma mark - Lock/Unlock layers
-(void) lockLayers
{
[self MenuStatus:NO Node:self];
}
-(void) unlockLayers
{
[self MenuStatus:YES Node:self];
}
// Disabled/Enable layers
-(void) MenuStatus:(BOOL)_enable Node:(id)_node
{
for (id result in ((CCNode *)_node).children) {
if ([result isKindOfClass:[CCMenu class]]) {
for (id result1 in ((CCMenu *)result).children) {
if ([result1 isKindOfClass:[CCMenuItem class]]) {
((CCMenuItem *)result1).isEnabled = _enable;
}
}
}
else
[self MenuStatus:_enable Node:result];
} // next
}
The actual modal code appears here,
#implementation MapModalLayer
- (id)init {
self = [super init];
if (self) {
// This method never seems to be called
NSLog(#"MapModalLayer init");
}
return self;
}
// This method never seems to be called
- (void) visit {
NSLog(#"Visit");
if (!self.visible)
return;
glPushMatrix();
glEnable(GL_SCISSOR_TEST);
glScissor(50, 50, 100 , 150);
[super visit];
glDisable(GL_SCISSOR_TEST);
glPopMatrix();
}
+ (void) CloseAlert: (CCSprite*) alertDialog onCoverLayer: (CCLayer*) coverLayer executingBlock: (void(^)())block {
// shrink dialog box
[alertDialog runAction:[CCScaleTo actionWithDuration:kAnimationTime scale:0]];
// in parallel, fadeout and remove cover layer and execute block
// (note: you can't use CCFadeOut since we don't start at opacity 1!)
[coverLayer runAction:[CCSequence actions:
[CCFadeTo actionWithDuration:0.2f opacity:0],
[CCCallBlock actionWithBlock:^{
[coverLayer removeFromParentAndCleanup:YES];
if (block) block();
}],
nil]];
}
+(void) ConfirmCity:(City *)cityObj onLayer:(CCLayer *)layer yesBlock :(void (^)())yesBlock noBlock:(void (^)())noBlock
{
CCLayerColor *coverLayer = [CoverLayer new];
[layer addChild:coverLayer z:INT_MAX]; // put to the very top to block application touches
[coverLayer runAction:[CCFadeTo actionWithDuration:kAnimationTime opacity:80]]; // smooth fade-in to dim with semi-transparency
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *dialog = [CCSprite spriteWithSpriteFrameName:#"modal.png"];
[dialog setPosition:CGPointMake(winSize.width/2,winSize.height/2)];
[dialog setTag:kDialogTag];
//
// We put our CCScrollLayer *scroller content here... it doesn't matter what it is right now
//
//
// Finally we put our accept/reject buttons
//
// Tick/Cross buttons
CCSprite *closeButtonOn = [CCSprite spriteWithSpriteFrameName:#"btn_close.png"];
CCSprite *tickButtonOn = [CCSprite spriteWithSpriteFrameName:#"btn_accept.png"];
// add one or two buttons, as needed
CCMenuItemSprite *opt1Button = [CCMenuItemSprite itemFromNormalSprite:closeButtonOn
selectedSprite:nil
block:^(id sender){
// close alert and call opt1block when first button is pressed
[self CloseAlert:dialog onCoverLayer: coverLayer executingBlock:noBlock];
} ];
[opt1Button setPosition:CGPointMake(-200, -120)];
// create second button, if requested
CCMenuItemSprite *opt2Button = [CCMenuItemSprite itemFromNormalSprite:tickButtonOn
selectedSprite:nil
block:^(id sender){
// close alert and call opt2block when second button is pressed
[self CloseAlert:dialog onCoverLayer: coverLayer executingBlock:yesBlock];
} ];
[opt2Button setPosition:CGPointMake(40, -120)];
CCMenu *menu = [CCMenu menuWithItems:opt1Button, opt2Button, nil];
[menu setContentSize:dialog.contentSize];
[dialog addChild:menu z:2];
[coverLayer addChild:dialog];
}
#end
I think its because I only ever use MapModalLayer's private methods only.
But I am not sure.
Is there a way to allow me to use the glScissor in a modal like explained above?
I've tried moving the glScissor code to the display of the modal, but it never seems to do anything.
To confirm it is working, I moved the glScissor code to the parent and it seems to work fine.
Thus, how do I make the modal layer use/work with glScissor?
I have since resolved this problem.
I used the Viewport from http://pastebin.com/tWsEbxvJ
The way I did it was:
CoverLayer *coverLayer = [CoverLayer new];
[layer addChild:coverLayer z:INT_MAX]; // put to the very top to block application touches
[coverLayer runAction:[CCFadeTo actionWithDuration:kAnimationTime opacity:80]]; // smooth fade-in to dim with semi-transparency
// ------------------------------
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *dialog = [CCSprite spriteWithSpriteFrameName:#"modal.png"];
[dialog setPosition:CGPointMake(winSize.width/2,winSize.height/2)];
[dialog setTag:kDialogTag];
.. // Build your pagesArray here for ScrollLayer...
// Now create the scroller and pass-in the pages (set widthOffset to 0 for fullscreen pages)
CCScrollLayer *scroller = [[CCScrollLayer alloc] initWithLayers:pagesArray widthOffset: 350];
[scroller setShowPagesIndicator:NO];
// finally add the scroller to your scene
//[dialog addChild:scroller];
Viewport *cn = [[Viewport alloc] initWithRect:CGRectMake(3, 0, dialog.contentSize.width-12, dialog.contentSize.height-5)];
[cn addChild:scroller];
[scroller release];
[coverLayer addChild:dialog z:1];
This allows me to put a modal on a layer, put a CCScrollLayer and make sure that the contents do not spill over the internals of the modal sprite I have made (this is why the width configuration looks a bit odd).
Thanks.