When i call relpaceScene or pushScene in cocos2d, I can add some transitions to it like:
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1 scene:scene]];
but when i call popScene, it takes no parameters, and no transition can be added. Is that so?
How can i popScene with desired transitions?
Per Guy in the cocos2d forums this seems to work: Link to Forum
Add this to CCDirector.h, just after the declaration -(void)popScene (line 402):
- (void) popSceneWithTransition: (Class)c duration:(ccTime)t;
Then add this to CCDirector.m, just after the definition of -(void)popScene (line 768):
-(void) popSceneWithTransition: (Class)transitionClass duration:(ccTime)t;
{
NSAssert( runningScene_ != nil, #"A running Scene is needed");
[scenesStack_ removeLastObject];
NSUInteger c = [scenesStack_ count];
if( c == 0 ) {
[self end];
} else {
CCScene* scene = [transitionClass transitionWithDuration:t scene:[scenesStack_ objectAtIndex:c-1]];
[scenesStack_ replaceObjectAtIndex:c-1 withObject:scene];
nextScene_ = scene;
}
}
You can call the method like this:
[[CCDirector sharedDirector] popSceneWithTransition:[CCSlideInRTransition class] durat
You can use Category as follows:
#interface CCDirector (PopTransition)
- (void)popSceneWithTransition:(Class)transitionClass duration:(ccTime)t;
#end
#implementation CCDirector (PopTransition)
- (void)popSceneWithTransition:(Class)transitionClass duration:(ccTime)t {
[self popScene];
// Make Transition
if (nextScene_) {
CCScene* scene = [transitionClass transitionWithDuration:t scene:nextScene_];
[scenesStack_ replaceObjectAtIndex:([scenesStack_ count] - 1) withObject:scene];
nextScene_ = scene;
}
}
#end
This way you need not to modify CCDirector class.
I modified Kailash's answer to work with cocos2d 2.1.
CCDirector+additions.h
#import "cocos2d.h"
#interface CCDirector (additions)
-(void)popSceneWithTransition:(Class)transitionClass duration:(ccTime)t;
#end
CCDirector+additions.m
#import "CCDirector+additions.h"
#implementation CCDirector (additions)
-(void)popSceneWithTransition:(Class)transitionClass duration:(ccTime)t {
[self popScene];
// Make Transition
if (_nextScene) {
CCScene* scene = [transitionClass transitionWithDuration:t scene:_nextScene];
[_scenesStack replaceObjectAtIndex:([_scenesStack count] - 1) withObject:scene];
_nextScene = scene;
}
}
#end
A more concise version of the above that actually releases the scene so it and its textures can be cleaned up as needed.
CCDirector+addition.h
#import "cocos2d.h"
#interface CCDirector (addition)
- (void) popSceneWithTransition:(Class)transitionClass duration:(ccTime)t;
#end
CCDirector+addition.m
#import "CCDirector+addition.h"
#implementation CCDirector (addition)
- (void) popSceneWithTransition:(Class)transitionClass duration:(ccTime)t {
[_scenesStack removeLastObject];
NSUInteger count = [_scenesStack count];
NSAssert(count > 0, #"Don't popScene when there aren't any!");
CCScene* scene = [transitionClass transitionWithDuration:t scene:[_scenesStack lastObject]];
[self replaceScene:scene];
}
#end
Without having to mess with Cocos2d internals you can do it like:
CCDirector *director = [CCDirector sharedDirector];
CCScene *currentScene = [director runningScene];
CGSize contentSize = [currentScene contentSize];
CCLayerColor *black = [[CCLayerColor alloc] initWithColor:ccc4(0, 0, 0, 0) width:contentSize.width height:contentSize.height];
[currentScene addChild:black];
[black runAction:[CCSequence actionOne:[CCFadeTo actionWithDuration:1.0f opacity:255]
two:[CCCallFunc actionWithTarget:director selector:#selector(popScene)]]];
Related
I am working on a game that has the player controlling a spaceShip. I want to make it such that if the spaceShip sprite collides with spaceJunk the junk disappears.
Here is my code so far.
HelloWorldLayer.h
#import <GameKit/GameKit.h>
// HelloWorldLayer
#interface HelloWorldLayer : CCLayerColor
{
CCSprite *_starShip;
CCSprite *_Paddle1;
CCSprite *_paddle2;
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
HelloWorldLayer.M
// Import the interfaces
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
//Import SpaceThings
#import "SpaceThings.h"
#implementation HelloWorldLayer{
}
// Helper class method that creates a Scene with the HelloWorldLayer as the only child.
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(void)gameLgoic:(ccTime)dt
{
[self addSpaceThings];
}
-(void) addSpaceThings
{
// _junk = [CCSprite spriteWithFile:#"catplaceholder.png"];
SpaceThings * spaceThings = nil;
if (arc4random() % 2 == 0){
spaceThings = [[[Astroids alloc] init] autorelease];
}else{
spaceThings = [[[SpaceJunk alloc]init] autorelease];
}
//determine where to spawn the space junk along the X-axis
CGSize winSize = [CCDirector sharedDirector].winSize;
int minX = spaceThings.contentSize.width/2;
int maxX = winSize.width - spaceThings.contentSize.width/2;
int rangeX =maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
//spawn the sprite slightly above the screen
spaceThings.position = ccp(actualX, winSize.height+_starShip.contentSize.height/2);
[self addChild:spaceThings];
//set the speed of junk
int minDuration = spaceThings.minMoveDirection;
int maxDuarton = spaceThings.maxMoveDirection;
int rangeDuration = maxDuarton - minDuration;
float actualDuraton = (arc4random() % rangeDuration)+ minDuration;
//move junk logic
CCMoveTo * actionMove = [CCMoveTo actionWithDuration:actualDuraton position:ccp(actualX, -spaceThings.contentSize.height/2)];
CCCallBlockN *actionMoveDone = [CCCallBlockN actionWithBlock:^(CCNode *node){[node removeFromParentAndCleanup:YES];}];
[spaceThings runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
if (CGRectIntersectsRect(_starShip.boundingBox, spaceThings.boundingBox)){
[self removeChild:spaceThings cleanup:YES];
}
}
// on "init" you need to initialize your instance
-(id) init
{
if (self =[super initWithColor:ccc4(255,255,255,255)]){
CGSize winSize = [CCDirector sharedDirector].winSize;
_starShip = [CCSprite spriteWithFile:#"theShip.gif"];
_starShip.position = ccp(winSize.width/2, 1.25*_starShip.contentSize.height);
//make the starship appear
[self addChild:_starShip];
//make the paddles
//bottom left one
_Paddle1 = [CCSprite spriteWithFile:#"spritePaddle.jpeg"];
int bottomOfScreenX = 0 + _Paddle1.contentSize.width/2;
int bottomOfScreenY = 0+_Paddle1.contentSize.height/2;
_Paddle1.position = ccp(bottomOfScreenX,bottomOfScreenY);
[self addChild:_Paddle1];
//bottom right one
_paddle2 = [CCSprite spriteWithFile:#"spritePaddle.jpeg"];
int bottomRightOfScreenX = winSize.width - _paddle2.contentSize.width/2;
_paddle2.position = ccp(bottomRightOfScreenX, bottomOfScreenY);
[self addChild:_paddle2];
//make thingsInSpace spawn at a set interval
int setInterval = (arc4random() % 3);
[self schedule:#selector(gameLgoic:) interval:setInterval];
//enable touch
[self setIsTouchEnabled:YES];
}
return self;
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//make the screens and create events
CGSize winSize = [CCDirector sharedDirector].winSize;
NSSet *allTouches = [event allTouches];
UITouch *touch = [allTouches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
//check to see if the left paddle is being pressed
if (CGRectContainsPoint([_Paddle1 boundingBox], location)){
int bottomLeftOfScreenX = 0 + _Paddle1.contentSize.width/1.25;
//make an action
//the Y-coordinate of starShip must be the same since the ship must move parallel to it.
id action = [CCMoveTo actionWithDuration:2 position:ccp(bottomLeftOfScreenX,1.25*_starShip.contentSize.height) ];
//have starship call this action
[_starShip runAction:action];
[action setTag:1];
}
//check to see if the right paddle is being pressed
if (CGRectContainsPoint([_paddle2 boundingBox], location)){
int bottomRightOfScreenX = winSize.width - _paddle2.contentSize.width/1.25;
//make an action
//the Y-coordinate of starShip must be the same since the ship moves parallel to it
id action2 = [CCMoveTo actionWithDuration:2 position:ccp(bottomRightOfScreenX,1.25*_starShip.contentSize.height) ];
//starShip is now running this specified action
[_starShip runAction:action2];
[action2 setTag:2];
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//if the player stops touching the pads, all the aciton should stop.
//left button stop
[_starShip stopActionByTag:1];
//right button stop
[_starShip stopActionByTag:2];
}
#pragma mark GameKit delegate
-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
#end
spaceThings.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface SpaceThings : CCSprite
#property (nonatomic, assign) int minMoveDirection;
#property (nonatomic, assign) int maxMoveDirection;
-(id)initWithFile:(NSString *)file minMoveDirection:(int)minMoveDirection maxMoveDirection:(int)maxMoveDirection;
#end
#interface Astroids : SpaceThings
#end
#interface SpaceJunk : SpaceThings
#end
SpaceThings.M
#import "SpaceThings.h"
#implementation SpaceThings
-(id)initWithFile:(NSString *)file minMoveDirection:(int)minMoveDirection maxMoveDirection:(int)maxMoveDirection
{
if ((self =[super initWithFile:file])){
self.minMoveDirection = minMoveDirection;
self.maxMoveDirection = maxMoveDirection;
}
return self;
}
#end
#implementation Astroids
-(id)init
{
if (self = [super initWithFile:#"astroid.jpeg" minMoveDirection:2 maxMoveDirection:5]){
}
return self;
}
#end
#implementation SpaceJunk
-(id)init
{
if (self = [super initWithFile:#"blueDot.jpg" minMoveDirection:4 maxMoveDirection:7]){
}
return self;
}
#end
I'm wondering as to where to put CGRectInteresectRect part of the code. As I've currently tried placing within the SpaceJunk alloc init part and it doesn't remove the child when the ship collides with the junk.
int setInterval = (arc4random() % 3);
[self schedule:#selector(gameLgoic:) interval:setInterval];
Make setInterval value to float and give small value for schedular.
Also not do [spaceThings runAction inside loop. if required then do some condition check and run only if required.
Then your CGRectIntersectsRect will fire when intersect each other.
Happy Coding.
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'm beginner for cocos2d-iphone.
i have a problem with addchild to gamescene.
i made a simple class derived from CCSprite and i've tried to display this class.
but it didn't work and i don't know what is problem.
this is my code of class:
//myClass.h
#import "cocos2d.h"
#interface myClass:CCSprite{
}
#end
//myClass.m
#import "myClass.h"
#implementation myClass
-(id) init{
if( self = [super initWithFile:#"title.png"] ){
self.position = ccp(240, 240);
}
return self;
}
#end
and this is a part of gamescene:
//HelloWorldLayer.m
...
// this worked well.
// myClass* temp = [CCSprite spriteWithFile:#"title.png"];
// temp.position = ccp(240, 240);
// [self addChild:temp];
// but this won't work.
myClass* temp = [[myClass alloc] init];
[self addChild:temp];
...
what should i do to solve this problem?
If you check the code, initWithFile calls initWithTexture that calls init, so you are in a loop and that's cause the problem. Try to make another constructor or a class method that creates your sprite and set position.
For example
//myClass.h
#import "cocos2d.h"
#interface myClass:CCSprite{
}
-(id)initMy;
+(id)createMySprite;
#end
//myClass.m
#import "myClass.h"
#implementation myClass
-(id) initMy{
if( self = [super initWithFile:#"title.png"] ){
self.position = ccp(240, 240);
}
return self;
}
+(id)createMySprite {
return [[[self alloc] initMy] autorelease];
}
#end
I am creating a small game where objective is to tap and destroy mouse. I created a separate mouse class for it.
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "HelloWorldLayer.h"
#interface Mouse : CCNode <CCTargetedTouchDelegate> {
CCSprite *sprite;
HelloWorldLayer *HelloLayer;
}
-(id) initWithGame:(HelloWorldLayer *)aGame;
-(void) runFloatAction;
#property(nonatomic, retain) CCSprite *sprite;
#property(nonatomic, retain) HelloWorldLayer *HelloLayer;
#end
I am initializing like this in .m file:
-(id) initWithGame:(HelloWorldLayer *)aGame{
if ((self = [super init])) {
self.sprite = [CCSprite spriteWithFile:#"mouse.png"];
self.sprite.scale = 0.3f + CCRANDOM_0_1() * 0.5f;
self.sprite.position = ccp(CCRANDOM_0_1() * 480, CCRANDOM_0_1() * 320);
self.HelloLayer = aGame;
[aGame addChild:sprite];
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
priority:1
swallowsTouches:YES];
//[self runFloatAction];
}
return (self);
}
I want to remove sprite on tap. For which I am using this code in .m file: -
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
if ([self containsTouchLocation:touch]) {
[self.sprite removeFromParentAndCleanup:YES];
return YES;
}else{
return NO;
}
}
Unfortunately I am not able to remove sprite. Logically, we have to remove sprite from parent. But, its not working in actual.
The way I structure my classes is to have the Mouse class as a subclass of CCSprite. If you handle the touch events in the main game class than you just remove the mouse. You also need to enable touch events if you haven't done that.
I recommend you do the above and put this in your game class.
[self setIsTouchEnabled:YES];
Mouse *myMouse = [Mouse spriteWithImage:#"Mouse.png"];
[myMouse setPosition:CGPointMake(160, 240)];
[self addChild:myMouse];
Then just handle the touch events in your game class.