I have a list of about 10 avatars and I am using CCScrollLayer to display paging. Currently, it only shows 1 avatar per page, and I would much prefer if it showed 3 avatars per page but I am unsure of how to do this.
I've attempted to ensure that a new page only generates if there is a MODULUS of 3, but it causes issues because parts of the code needs to be available, such as adding things to the menu.
When I try to use a MODULUS (tied to an if statement) it complains that my menu is out of scope.
My code follows;
// Avatars are generally 70x72
//
GameStateManager *state = [GameStateManager sharedGameStateManager];
NSLog(#"listOfPlayers.size = %d", [state.listOfPlayers count]);
// Menu of playable characters
int i=0;
NSMutableArray *pagesArray = [NSMutableArray array];
// --
for (Player *p in state.listOfPlayers)
{
// create a blank layer for page
CCLayer *page = [CCLayer node];
[page setContentSize:CGSizeMake(200, 100)];
CCMenu *menu = [CCMenu menuWithItems:nil];
[menu setContentSize:CGSizeMake(200, 72)];
[menu alignItemsHorizontallyWithPadding:9.0f];
[page addChild:menu];
// --
NSLog(#"p: %# (%#) -- locked: %d, playable: %d", p.name, p.fileName, [p.isLocked intValue], [p.isPlayable intValue]);
//int isLocked = [p.isLocked intValue];
int isPlayable = [p.isPlayable intValue];
NSString *fileName = [NSString stringWithFormat:#"hold_%#", p.fileName];
//if ( (isLocked == 0) && (isPlayable == 1) )
if (isPlayable == 1)
{
CCSprite *avatarOff = [CCSprite spriteWithSpriteFrameName:fileName];
CCSprite *avatarOn = [CCSprite spriteWithSpriteFrameName:fileName];
CCMenuItemSprite *menuItem = [CCMenuItemSprite itemFromNormalSprite:avatarOff selectedSprite:avatarOn target:self selector:nil];
[menuItem setTag:i];
[menu addChild:menuItem];
[pagesArray addObject:page];
i++;
}
} // next
// Now create the scroller and pass-in the pages (set widthOffset to 0 for fullscreen pages)
CCScrollLayer *scroller = [[CCScrollLayer alloc] initWithLayers:[NSMutableArray arrayWithArray:pagesArray] widthOffset: 200];
// finally add the scroller to your scene
[self addChild:scroller];
Screenshot follows. It shows 1 avatar per page.
seems to me your are seeing what you are programming. Try
// Avatars are generally 70x72
//
GameStateManager *state = [GameStateManager sharedGameStateManager];
NSLog(#"listOfPlayers.size = %d", [state.listOfPlayers count]);
// Menu of playable characters
int i=0;
NSMutableArray *pagesArray = [NSMutableArray array];
// --
CCLayer *page=nil;
CCMenu *menu=nil;
int avisOnPage=0;
for (Player *p in state.listOfPlayers)
{
if(0==avisOnPage) {
// create a blank layer for page
page = [CCLayer node];
[page setContentSize:CGSizeMake(200, 100)];
menu = [CCMenu menuWithItems:nil];
[menu setContentSize:CGSizeMake(200, 72)];
[menu alignItemsHorizontallyWithPadding:9.0f];
[page addChild:menu];
[pagesArray addObject:page];
} // if new page
// --
NSLog(#"p: %# (%#) -- locked: %d, playable: %d", p.name, p.fileName, [p.isLocked intValue], [p.isPlayable intValue]);
//int isLocked = [p.isLocked intValue];
int isPlayable = [p.isPlayable intValue];
NSString *fileName = [NSString stringWithFormat:#"hold_%#", p.fileName];
//if ( (isLocked == 0) && (isPlayable == 1) )
if (isPlayable == 1)
{
CCSprite *avatarOff = [CCSprite spriteWithSpriteFrameName:fileName];
CCSprite *avatarOn = [CCSprite spriteWithSpriteFrameName:fileName];
CCMenuItemSprite *menuItem = [CCMenuItemSprite itemFromNormalSprite:avatarOff selectedSprite:avatarOn target:self selector:nil];
[menuItem setTag:i];
[menu addChild:menuItem];
avisOnPage++;
i++;
if(3==avisOnPage) avisOnPage=0;
} // if isPlayable
} // for player
// Now create the scroller and pass-in the pages (set widthOffset to 0 for fullscreen pages)
CCScrollLayer *scroller = [[CCScrollLayer alloc] initWithLayers:pagesArray widthOffset: 200];
// finally add the scroller to your scene
[self addChild:scroller];
Related
Thank you for taking the time to look at my question, As I am very new to programming any help or pointers in the right direction is much appreciated.
Overview...
I have a simple game I am trying to create and it includes a class for 'Ball' sprite that is fired from the left of the screen to the right. The speed at which this "Ball" sprite is created is random and each time the "Ball" moves past the right of the screen a point is added to the score.
Problem....
The problem I have is when 2 "Balls" fire at almost the same time (which happens some times as its random) they pass the right hand of the screen less then a second apart and when this happens it only seems to add 1 when it should be adding 2 to the score (as 2 balls has passed the right side).
Below is the code for my Ball launcher class (use this class to call the ball randomly).
If you have used or gone through 'Ray Wenderlichs' Learn Cocos2D then allot of this will look familiar as after going through his book I have tried to edit his code to do what I want (to me after reading the book its the best way to learn, by messing with the code etc).
#implementation BL
#synthesize delegate;
#synthesize lauchingAnim;
#synthesize afterlauchingAnim;
-(void) dealloc {
delegate = nil;
[lauchingAnim release];
[afterlauchingAnim release];
[super dealloc];
}
//--The below shootPhaser method takes the current direction the launcher is facing and asks the delegate (gameplaylayer) to create the ball moving in that direction.
//--The below createPhaserWithDirection method was declared (created) in the GamePlayLayerDelegate protocol.
-(void)shootPhaser {
CGPoint phaserFiringPosition;
PhaserDirection phaserDir;
CGPoint position = [self position];
float xPosition = position.x + position.x * 0.51f;
float yPosition = position.y + position.y * 0.045f;
{
phaserDir = kDirectionRight;
}
phaserFiringPosition = ccp(xPosition, yPosition);
[delegate createPhaserWithDirection:phaserDir andPosition:phaserFiringPosition];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
id action = nil;
characterState = newState;
switch (newState) {
case kStatespawning:
// CCLOG(#“launcher->Changing State to Spwaning");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#“lancher_1.png"]];
break;
case kStateIdle:
// CCLOG(#“laucher->Changing state to idle");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"lancher_1.png"]];
break;
case kStateFiring:
// CCLOG(#“launcher->Changing State to firing");
action = [CCSequence actions:
[CCAnimate actionWithAnimation:lauchingAnim],
[CCCallFunc actionWithTarget:self
selector:#selector(shootPhaser)],
[CCAnimate actionWithAnimation:afterlauchingAnim],
[CCDelayTime actionWithDuration:2.0f],
nil];
lauchingAnim.restoreOriginalFrame = NO;
afterlauchingAnim.restoreOriginalFrame = NO;
[self changeState:kStateIdle];
break;
case kStateDead:
CCLOG(#“launcher->changing state to dead");
break;
default:
CCLOG(#"unhandled state %d in launcher", newState);
break;
}
if (action !=nil) {
[self runAction:action];
}
}
-(void)updateStateWithDeltaTime: (ccTime)deltaTime andListOfGameObjects:(CCArray*)listOfGameObjects {
if (characterState == kStateFiring) {
// 5
if (characterState != kStateFiring) {
// If RadarDish is NOT already taking Damage
[self changeState:kStateFiring];
return;
}
}
if ((([self numberOfRunningActions] == 0) && (characterState != kStateDead)) ) {
// CCLOG(#"launcher Going to Idle!!!");
[self changeState:kStateIdle];
return;
}
}
-(void)initAnimations {
[self setLauchingAnim:[self loadPlistForAnimationWithName:#"lauchingAnim" andClassName:NSStringFromClass([self class])]];
[self setAfterlauchingAnim:[self loadPlistForAnimationWithName:#"afterlauchingAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### Laauncher initialized");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kBallLaucher;
// 3
[self changeState:kStateIdle];
}
}
return self;
}
#end
Below is the code for my "Ball" class...
#import “Ball.h"
#implementation Ball
#synthesize delegate;
#synthesize myDirection;
#synthesize travelingAnim;
#synthesize ScoreAnim;
-(void) dealloc {
delegate = nil;
[travelingAnim release];
[ScoreAnim release];
[super dealloc];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
[self setCharacterState:newState];
CGSize screenSize1 = [CCDirector sharedDirector].winSize;
CGPoint position = [self position];
CGPoint endPosition = ccp(screenSize1.width*1.5f, screenSize1.height*0.20f);
id action = nil;
//id action1 = nil;
switch (newState) {
case kStatespawning:
CCLOG(#“Spawning Ball");
[self setDisplayFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Ball_1.png"]];
break;
case kStateTravelling: {
movementAction = [CCMoveTo actionWithDuration:5.0f
position:endPosition];
}
break;
case kStateScore:
PLAYSOUNDEFFECT(SCORE);
CCLOG(#“Ball Past Left Of Screen => Add 1 to Score");
action = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
break;
default:
CCLOG(#“Ball -> Unknown CharState %d",
characterState);
break;
}
if (action !=nil)
[self runAction:action];
}
-(void)removeSelf{
CCLOG(#"Removing Ball Object Has Scored.");
[self setVisible:NO];
[self removeFromParentAndCleanup:YES];
return;
}
-(void)updateStateWithDeltaTime:(ccTime)deltaTime andListOfGameObjects:(CCArray *)listOfGameObjects {
CGPoint currentSpitePosition = [self position];
CGSize screenSize = [CCDirector sharedDirector].winSize;
if (currentSpitePosition.x > screenSize.width*1.1f) {{
[self changeState:kStateScore];
}
}
return;
}
if ([self numberOfRunningActions] == 0) {
if (characterState == kStatespawning) {
[self changeState:kStateTravelling];
return;
}
}
}
-(void)initAnimations {
[self setTravelingAnim:[self loadPlistForAnimationWithName:#"travelingAnim" andClassName:NSStringFromClass([self class])]];
[self setScoreAnim:[self loadPlistForAnimationWithName:#"ScoreAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### Ball Initialised");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kEnemyTypeBall;
[self changeState:kStatespawning];
}
}
return self;
}
#end
Below is the code for my "GameplayLayer" class...
#import "GamePlayLayer.h"
#import “Ball.h"
#import “BL.h"
#implementation GamePlayLayer
#synthesize delegate;
-(void) dealloc {
delegate = nil;
[super dealloc];
}
-(void) update:(ccTime)deltaTime {
CCArray *listOfGameObjects =
[sceneSpriteBatchNode children];
for (GameCharacter *tempChar in listOfGameObjects) {
[tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];
}
GameCharacter *tempChar = (GameCharacter*)[sceneSpriteBatchNode
getChildByTag:kEnemyTypeBall];
if ([tempChar characterState] == kStateScore) <==HERE I AM SEEING IF THE BALL HAS SCORED - THIS IS ALSO WHERE I THINK I MAY BE GOING WRONG SOMEHOW.
{
CCLOG(#"Add 1 Points To Score");
[self addPoint];
return;
}
}
-(void)addPoint
{
score = score + 1;
[scoreLabel setString:[NSString stringWithFormat:#"$%i", score]]; <===ADDING THE POINT TO THE SCORE
return;
}
-(void) createObjectOfType: (GameObjectType)objectType
withHealth:(int)initialHealth atLocation:(CGPoint)spawnLocation withZValue:(int)ZValue {
if (kBallLaucher == objectType) {
CCLOG(#"Creating launcher Object");
BL *ballLauncher = [[[BL alloc] init] initWithSpriteFrameName:#“launcher_1.png"];
[ballLauncher setCharacterHealth:initialHealth];
[ballLauncher setPosition:spawnLocation];
[sceneSpriteBatchNode addChild:tBT z:ZValue tag:kBallLaucher ];
[ballLauncher setDelegate:self];
[ballLauncher release];
}
}
**BELOW IS HOW I CONTROL WHEN THE BALL IS FIRED**
-(void)ChangeStateLaucher:(int)brandnewState withState:(CharacterStates)newState andObject:(GameObjectType)objectType; {
BL *bL = (BL*)
[sceneSpriteBatchNode getChildByTag:kBallLaucher];
int x = (arc4random() % 2);
if (x==0) {
CCLOG(#"Start Laucher Firing");
[bL changeState:kStateFiring];
count = 0;
}
if (x==1) {
CCLOG(#"No Laucher Firing");
count = count + 1;
if (count == 2) {
CCLOG(#"No Laucher Firing x2 - Start Laucher Firing");
[bL changeState:kStateFiring];
} else if (count > 3) {
CCLOG(#"No Laucher Firing x3 - Start Laucher Firing");
[bL changeState:kStateFiring];
}
}
[delegate ChangeStateLaucher:x withState:kStateFiring andObject:objectType];
}
-(void)createPhaserWithDirection:(PhaserDirection)phaserDirection andPosition:(CGPoint)spawnPosition {
CCLOG(#"Creating Ball from Gameplay Layer");
Ball *ballSprite = [[Ball alloc]initWithSpriteFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Ball_1.png"]];
[ballSprite setPosition:spawnPosition];
[ballSprite setMyDirection:phaserDirection];
[ballSprite setCharacterState:kStatespawning];
[ballSprite setCharacterHealth:3.0f];
[sceneSpriteBatchNode addChild:ballSprite z:20 tag:kEnemyTypeBall];
[ballSprite release];
}
-(id)init {
self = [super init];
if (self !=nil) {
CGSize screenSize = [CCDirector sharedDirector]. winSize;
self.TouchEnabled = YES;
srandom(arc4random()); // Seeds the random number generator
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"scene1atlas.plist"]; // 1
sceneSpriteBatchNode =
[CCSpriteBatchNode batchNodeWithFile:#"scene1atlas.png"]; // 2
[self createObjectOfType:kBallLaucher withHealth:3 atLocation:ccp(screenSize.width * 0.05f, screenSize.height * 0.822f) withZValue:10];
[gameBeginLabel setPosition:ccp(screenSize.width/2,screenSize.height/2)]; // 2
[self addChild:gameBeginLabel]; // 3
id labelAction = [CCSpawn actions:
[CCFadeOut actionWithDuration:2.5f],
nil]; // 4
[gameBeginLabel runAction:labelAction];
lives = 3;
scoreLabel = [CCLabelBMFont labelWithString:#"$0"
fntFile:#“BallTest.fnt"];
scoreLabel.position = ccp(screenSize.width * 0.5f, screenSize.height * 0.9f);
[self addChild:scoreLabel
z:-1 tag:kNewScoreTagValue];
[self scheduleUpdate];
}
return self;
}
#end
Once again I would like to thank anyone that has taken the time to look at this question regardless if you have posted a pointer or answer or not.
Thanks.
I think that when there are more than one Ball objects on the screen, the GamePlayLayer update method is unable to pick up on the state change on one of the balls at times because that ball is removed as soon as it enters kStateScore.
If you add a certain delay before removing the ball , the update method should pick up the changes in all the balls and your problem should be resolved.
In your Ball.m -(void)changeState:(CharacterStates)newState function , modify the action on state kStateScore like
action = [CCSequence actions:[CCDelayTime actionWithDuration:0.2],
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
Try this out and let me know if this worked for you!
EDIT
The above solution would end up adding more than one point as the ball would remain in kStateScore state for a long time due to delay. In order to counter that problem , we can introduce a new state call kStateDead and change the ball state to kStateDead after adding the point. This will ensure a point is added only once and also we can put the responsibility of removing the ball in kStateDead state. The new code would like as follows :
Ball.m
case kStateScore:
PLAYSOUNDEFFECT(SCORE);
CCLOG(#“Ball Past Left Of Screen => Add 1 to Score");
break;
case kStateDead:
action = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
default:
CCLOG(#“Ball -> Unknown CharState %d",
characterState);
break;
and in your Gameplayer.m
if ([tempChar characterState] == kStateScore) <==HERE I AM SEEING IF THE BALL HAS SCORED - THIS IS ALSO WHERE I THINK I MAY BE GOING WRONG SOMEHOW.
{
CCLOG(#"Add 1 Points To Score");
[self addPoint];
[tempChar changeState:kStateDead]; //change the state to dead after the point is added
return;
}
I wish to clone the effects of the button animations found in Candy Crush Saga.
And also I wish to know how to do such fluid & beautiful animations.
Is it possible with Cocos2d-iPhone?
This is the link of Candy Crush Sage:
http://www.youtube.com/watch?v=KAMUWIqYN24
Is it done using image sequences?
It is possible. Just run animation on buttons normal sprite.
GTAnimSprite *frame_normal = [GTAnimSprite spriteWithFile:#"play_button_normal.png"];
GTAnimSprite *frame_sel = [GTAnimSprite spriteWithFile:#"play_button_selected.png"];
frame_sel.color = ccc3(128,128,128);
CCMenuItemSprite *plyBtn = [CCMenuItemSprite itemWithNormalSprite:frame_normal
selectedSprite:frame_sel
target:self
selector:#selector(playBtnPress:) ];
plyBtn.position = ccp(size.width*0.77f, size.height*0.1f);
CCMenu *menu2 = [CCMenu menuWithItems:plyBtn, nil];
menu2.position = ccp(0.0f,0.0f);
[self addChild:menu2 z:2 ];
//Here is class file:GTAnimSprite.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface GTAnimSprite : CCSprite
{
bool bouncing;
float counter;
}
#end
//Here is class file:GTAnimSprite.mm
#import "GTAnimSprite.h"
#implementation GTAnimSprite
-(void)onEnter
{
[super onEnter];
counter = 0.0f;
bouncing = true;
[self scheduleUpdate];
}
-(void)update:(ccTime)dt
{
if (bouncing)
{
counter += dt;
self.scaleX = ( (sin(counter*10) + 1)/2.0 * 0.1 + 1);
self.scaleY = ( (cos(counter*10) + 1)/2.0 * 0.1 + 1);
if (counter > M_PI*10){
counter = 0;
}
}
}
-(void)onExit
{
[self unscheduleUpdate];
[super onExit];
}
#end
HERE IS XCODE SAMPLE SOURCE: https://www.box.com/s/52i4xyznetyyc329evcu
This is how I do it using CCActions
First, I define the CCAction:
id scaleHorDown = [CCScaleTo actionWithDuration:duration * 5/30.f scaleX:0.75f scaleY:1.0f];
id scaleHorBouncing = [CCEaseBounceIn actionWithAction:scaleHorDown];
id scaleVerDown = [CCScaleTo actionWithDuration:duration * 5/30.f scaleX:1.0f scaleY:0.65f];
id scaleVerBouncing = [CCEaseBounceInOut actionWithAction:scaleVerDown];
id shrink = [CCSequence actions:scaleHorBouncing,scaleVerBouncing, nil];
id swell = [CCScaleTo actionWithDuration: duration * 15/30.f scale:1.10f];
id swellEase = [CCEaseElasticOut actionWithAction:swell];
id resetScale = [CCScaleTo actionWithDuration:duration * 5/30.f scale:1.0f];
id resetScaleBouncing = [CCEaseInOut actionWithAction:resetScale];
id buttonAction = [CCSequence actions: shrink, swellEase, resetScaleBouncing, nil];
Then I run the animation over the desired sprites when CCMenuItem is initialized
CCMenuItem aMenuItem = [CCMenuItemSprite itemFromNormalSprite:buttonNormalSprite
selectedSprite:buttonSelectedSprite
block:^(id sender) {
//play animation highlighting button
[buttonSelectedSprite runAction:buttonAction]];
}}];
In my case I'm only running the animation when the button is pressed.
I'm using a CCParallaxNode for a scrolling background in my GameLayer and I'm getting a bunch of errors and I can't figure out why.
"Initializer element is not a compile-time constant" and also "Unk
#implementation Background
// Load the sprites for each parallax layer, from background to foreground.
CCSprite* para1 = [CCSprite spriteWithFile:#"color.png"];
CCSprite* para2= [CCSprite spriteWithFile:#"ship-hd.png"];
CCSprite* para3 = [CCSprite spriteWithFile:#"twit.png"];
CCSprite* para4 = [CCSprite spriteWithFile:#"Start.png"];
// Set the correct offsets depending on the screen and image sizes.
para1.anchorPoint = CGPointMake(0, 1);
para2.anchorPoint = CGPointMake(0, 1);
para3.anchorPoint = CGPointMake(0, 0.6f);
para4.anchorPoint = CGPointMake(0, 0);
CGPoint topOffset = CGPointMake(0, screenSize.height);
CGPoint midOffset = CGPointMake(0, screenSize.height / 2);
CGPoint downOffset = CGPointZero;
// Create a parallax node and add the sprites to it.
CCParallaxNode* paraNode = [CCParallaxNode node];
[paraNode addChild:para1 z:1 parallaxRatio:CGPointMake(0.5f, 0) ositionOffset:topOffset];
[paraNode addChild:para2 z:2 parallaxRatio:CGPointMake(1, 0) positionOffset:topOffset];
[paraNode addChild:para3 z:4 parallaxRatio:CGPointMake(2, 0) positionOffset:midOffset];
[paraNode addChild:para4 z:3 parallaxRatio:CGPointMake(3, 0) positionOffset:downOffset];
[self addChild:paraNode z:0 tag:ParallaxSceneTagParallaxNode];
// Move the parallax node to show the parallaxing effect.
CCMoveBy* move1 = [CCMoveBy actionWithDuration:5 position:CGPointMake(−160, 0)];
CCMoveBy* move2 = [CCMoveBy actionWithDuration:15 position:CGPointMake(160, 0)];
CCSequence* sequence = [CCSequence actions:move1, move2, nil];
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequence];
[paraNode runAction:repeat];
#end
You need to enclose your code in an -(id)init method, like this:
#implementation Background
- (id)init
{
if (self = [super init]) {
// Load the sprites for each parallax layer, from background to foreground.
CCSprite* para1 = [CCSprite spriteWithFile:#"color.png"];
// ... the rest of your code ... //
}
return self;
}
#end
I keep getting the same crash report "'Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt'".
This is getting pretty annoying now, i only wanted to make the Backgound scrolling infinitely.
Here is my code:
-(id) init {
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
CGSize screenSize = [CCDirector sharedDirector].winSize;
/*CCSprite *Player = [CCSprite spriteWithFile:#"Icon.png"];
Player.position = ccp(screenSize.width/2 -110, screenSize.height/2);
Player.scale = 0.7;
[self addChild:Player];
[self scheduleUpdate];*/
CCLOG(#"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height);
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
bool doSleep = true;
world = new b2World(gravity, doSleep);
world->SetContinuousPhysics(true);
// Debug Draw functions
/*m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2DebugDraw::e_shapeBit;
//flags += b2DebugDraw::e_jointBit;
//flags += b2DebugDraw::e_aabbBit;
//flags += b2DebugDraw::e_pairBit;
//flags += b2DebugDraw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);*/
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2PolygonShape groundBox;
// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
// top
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);
//Set up sprite
BG1 = [CCSprite spriteWithFile:#"SkyAndClouds.png"];
BG1.anchorPoint = ccp(0,0);
BG1.position = ccp(0, 0);
BG1.scale = 0.5;
[self addChild:BG1];
BG2 = [CCSprite spriteWithFile:#"SkyAndClouds.png"];
BG2.anchorPoint = ccp(0,0);
BG2.position = ccp([BG1 boundingBox].size.width-1,0);
[self addChild:BG2];
[self schedule: #selector(AnimatedBG:) interval:0];
CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:#"Stick.png" capacity:150];
[self addChild:batch z:0 tag:kTagBatchNode];
[self addNewSpriteWithCoords:ccp(screenSize.width/2-110, screenSize.height/2)];
[self addZombieWithCoords:ccp(screenSize.width/2+240, screenSize.height/2)];
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Stick, you Better Run!" fontName:#"Helvetica" fontSize:16];
[self addChild:label z:0];
[label setColor:ccc3(0,0,255)];
label.position = ccp( screenSize.width/2, screenSize.height-17);
[self schedule: #selector(tick:)];
}
return self;
}
-(void)AnimatedBG {
BG1.position = ccp(BG1.position.x-1,BG1.position.y);
}
Your AnimatedBG method should be defined as:
-(void) AnimatedBG:(ccTime)dt {
}
You should also make sure you have a definition for your scheduled tick: method:
-(void) tick:(ccTime)dt {
}
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.