How to run a method within a CCAction? - iphone

I am currently on the brink of throwing my computer at the wall because I cannot figure this out. I have done about 200 Google searches, and every link is clicked up to like page 6. I cannot find an answer. So here's the dirt:
I want my Enemies class to contain a shoot method. Simple enough right? Well, I have the action to move the enemies in the HelloWorldLayer method. I want to find a way to have (some type of) CCAction call that method from the Enemies.m class. Please help! And #Lukman your Object Oriented Programming answer didn't work. Thanks!
EDIT:
Here's what's in HelloWorldLayer.m that is necessary to the answer:
action = [CCSequence actions:
[CCMoveBy actionWithDuration:1 position:ccpMult(ccpNormalize(ccpSub(moveToPoint, buffDude.position)), 75)],
[CCMoveBy actionWithDuration:3 position:ccp(buffDude.position.x,buffDude.position.y)],
nil];
CCCallFuncO *a = [CCCallFuncO actionWithTarget:buffDude selector:(#selector(shoot:)) object:buffDude];
CCSequence *seq = [CCSequence actions:action,a, nil];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:seq];
[buffDude runAction:repeat];
And here's what is in Enemies.m:
#implementation BigAndStrongEnemy
+(id)enemy {
BigAndStrongEnemy *enemy = nil;
if((enemy = [[[super alloc] initWithFile:#"bigAndStrongEnemy.gif"] autorelease])) {
enemy.hp = 200;
enemy.pointsWorth = 1000;
}
return enemy;
}
-(void)spriteMoveFinished:(id)sender {
CCSprite *b = (CCSprite *)sender;
[self removeChild:b cleanup:YES];
}
-(void)shoot {
CCSprite *b = [CCSprite spriteWithFile:#"bullet.gif"];
b.position = ccp(self.position.x,self.position.y);
[self addChild:b];
[bullets addObject:b];
CGSize winSize = [[CCDirector sharedDirector] winSize];
CGPoint point = CGPointMake((winSize.width - (winSize.width - self.position.x)),0);
[b runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:0.5 position:point],
[CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)],
nil]];
}
-(void)shoot:(id)sender {
BigAndStrongEnemy *e = (BigAndStrongEnemy *)sender;
[e shoot];
}
#end

as far as i understood CCCallFunc is what you need

hmm... hope this works:
id *func = [CCCallFunc actionWithTarget:buffDude selector:#selector(shoot)];

Related

How to display the score in the game over layer in cocos2d

I've a problem when I try to show the final score in the Game Over Layer in a game in cocos2d. There's an algorithm that modify the value of my variable increment, that contains the points of the user and then the game shows them.
increment = increment + 50;
[pointLabel setString: [NSString stringWithFormat: #"Points: %i", increment]];
and then a function controls if the user has or not lives in his play
-(void)gameOver:(int)value punteggio:(id)punti{
if (value == 1) {
//WIN
}else if(value == 2){
if (life > 1) { // 1
life = life - 1;
for (CCSprite *spr in spriteLifeArray) {
if (life == spr.tag) {
[self removeChild:spr cleanup:YES];
}}}
else {
// LOSE
[...]
[[CCDirector sharedDirector] replaceScene:[GameOver node]];
}
}}
Then the GameOverLayer is called. This is the .h file
#interface GameOver : CCNode {
CGSize size;
CCLabelTTF *label1;
CCLabelTTF *label2;
CCLabelTTF *labelpnt;
CCLabelTTF *labelscore;
}
-(void)restart;
Here the .m file
#implementation GameOver
+(id) scene {
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
GameOver *layer = [GameOver node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init{
if( (self=[super init] )) {
size = [[CCDirector sharedDirector] winSize];
label1 = [CCLabelTTF labelWithString:#"Game Over" fontName:#"Marker Felt" fontSize:40];
label1.position = ccp(size.width/2 , size.height/2+20+50 );
labelpnt = [CCLabelTTF labelWithString:#"Punteggio" fontName:#"Marker Felt" fontSize:20];
labelpnt.position = ccp(size.width/2 , size.height/2+50-100 );
labelscore = [CCLabelTTF labelWithString:#"100" fontName:#"Marker Felt" fontSize:20];
[labelscore setString: [NSString stringWithFormat: #" 0 "]];
[labelscore setColor:ccc3(255, 1, 1)];
labelscore.position = ccp(size.width / 2, size.height/2+50-130);
label2 = [CCLabelTTF labelWithString:#"Ricomincia" fontName:#"Marker Felt" fontSize:25];
CCMenuItemLabel *back = [CCMenuItemLabel itemWithLabel:label2 target:self selector:#selector(restart)];
CCMenu *menu= [CCMenu menuWithItems:back, nil];
menu.position = ccp(size.width/2 , size.height/2-50+50);
[self addChild: label1];
[self addChild: labelpnt];
[self addChild: labelscore];
[self addChild: menu];
}
return self;
}
-(void) restart {
[[CCDirector sharedDirector] replaceScene:[HelloWorldLayer node]];
}
How can I show the final value of my int increment in the game over layer? How can I pass it throught the classes?
Use UserDefault. Here is code
//Save score in game screen
int highScore = 234;
[[NSUserDefaults standardUserDefaults] setInteger:12 forKey:#"HighScore"];
[[NSUserDefaults standardUserDefaults] synchronize];
//get score in game over
int highScore = [[NSUserDefaults standardUserDefaults] integerForKey:#"HighScore"];
NSString *score = [NSString stringWithFormat: #"%d", highScore];
CCLabelTTF *scoreLabel = [CCLabelTTF labelWithString:score fontName:#"Marker Felt" fontSize:40];
Hey in that Case You should take a separate layer for displaying the Score lives or whatever you want to display simultaneously.
In HUD Layer i.e Heads-Up Display class You should write some basic code to display a CCLabelBMFont to the screen that says "Your Score", “You Win” or “You Lose” and a button underneath that says “Restart”.
When the restart button is tapped, it creates a new instance of the ActionLayer switches to it.
Write a Score method scoreUpdate, this method will have the logic for the score calculation like whenever a bullet hit to monster and update it to the CCLabelBMFont label.
And then All you need to do just call that method.
Here is the one of the best tutorial for such requirement.
See

Adding a game over scene

I want to add this game over scene to this game that I am trying to do for my homework, and I seem to not have it where if you kill the target the game over scene will pop up. I tried putting my code in every line and see if it will finally work but no it didn't. So now I have to ask for some help.
. m file
- (void)addTarget10 {
Boss *target10 = nil;
if ((arc4random() % 2) == 0) {{
target10 = [WeakAndFastBoss9 boss9];
}} else {
target10 = [WeakAndFastBoss9 boss9];
}
[[SimpleAudioEngine sharedEngine] playEffect:#"lastboss.mp3"];
// Determine where to spawn the target along the Y axis
CGSize winSize = [[CCDirector sharedDirector] winSize];
int minY = target10.contentSize.height/2;
int maxY = winSize.height - target10.contentSize.height/2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
// Create the target slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
target10.position = ccp(winSize.width + (target10.contentSize.width/2), actualY);
[self addChild:target10 ];
// Determine speed of the target
int minDuration = target10.minMoveDuration;
int maxDuration = target10.maxMoveDuration;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
id actionMove = [CCMoveTo actionWithDuration:actualDuration position:ccp(- target10.contentSize.width/2, actualY)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteMoveFinished9:)];
[target10 runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
target10.tag = 1;
[_targets addObject:target10];
}
-(void)gameLogicboss9:(ccTime)dt {
[self unschedule:_cmd];
[self addTarget10];
}
- (void)updateboss9:(ccTime)dt {
CGRect projectileRect = CGRectMake(projectile.position.x - (projectile.contentSize.width/2),
projectile.position.y - (projectile.contentSize.height/2),
projectile.contentSize.width,
projectile.contentSize.height);
BOOL bossHit = FALSE;
NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
for (CCSprite *target1 in _targets) {
CGRect target1Rect = CGRectMake(target1.position.x - (target1.contentSize.width/2),
target1.position.y - (target1.contentSize.height/2),
target1.contentSize.width,
target1.contentSize.height);
if (CGRectIntersectsRect(projectileRect, target1Rect)) {
[targetsToDelete addObject:target1];
bossHit = TRUE;
Boss *boss = (Boss *)target1;
boss.hp--;
if (boss.hp <= 0 ) {
[targetsToDelete addObject:target1];
}
break;
}
}
for (CCSprite *target in targetsToDelete) {
[_targets removeObject:target];
[self removeChild:target cleanup:YES];
_projectilesDestroyed++;
if (_projectilesDestroyed > 2) {
}
}
if (bossHit) {
//[projectilesToDelete addObject:projectile];
}
[targetsToDelete release];
}
-(void)spriteMoveFinishedboss9:(id)sender {
CCSprite *sprite = (CCSprite *)sender;
[self removeChild:sprite cleanup:YES];
if (sprite.tag == 1) { // target
[_targets removeObject:sprite];
} else if (sprite.tag == 2) { // projectile
[_projectiles removeObject:sprite];
} }
This the game over scene I want to add when target 10/ boss 9 is killed
GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:#"You Lose"];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
Right now my other game over scene is when the sprite is moved passed screen.If you need me to answer any questions feel free to ask.
For scene replace you try this for your game code..
First Add this code to your GameOverScene class
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
GameOverScene *layer = [GameOverScene node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
Make your GameOverClass is subclass of CCLayer,
Than when You want to change scene do this
[[CCDirector sharedDirector] replaceScene:[GameOverScene scene]];
Ok, so you have to create a new scene. This can simple be done using File->New File and make it a subclass of NSObject. You then change the subclass to an CCLayer. As a test, you can just copy your code from the hello world layer. Next, just import the new class in your helloworld layer class and create a instance of it. Then in a method use [[CCDirector sharedDirector] replaceScene:sceneName];
You can use this site for more info, its very helpful, just read through it and you will find your answer:http://www.raywenderlich.com/352/how-to-make-a-simple-iphone-game-with-cocos2d-tutorial

Cocos-2d actions -- CCallFunc not doing anything

Basically, I'm trying to animate a sprite. When moving right I want one animation to play, and when moving left another to play. Here's my code-- nothing is happening at all, the sprite is merely shown on the screen.
-(void)moveLeft{
[sprite stopActionByTag:0];
NSMutableArray *spriteFrames2 = [NSMutableArray array];
CCSpriteFrame *frame4 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesLeft10.png"];
CCSpriteFrame *frame5 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesLeft11.png"];
CCSpriteFrame *frame6 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesLeft12.png"];
[spriteFrames2 addObjectsFromArray:[NSArray arrayWithObjects:frame4,frame5,frame4,frame6, nil]];
CCAnimation *anim = [CCAnimation animationWithFrames:spriteFrames2 delay:0.15f];
CCAnimate *animate = [CCAnimate actionWithAnimation:anim];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:animate];
repeat.tag = 1;
[sprite runAction:repeat];
id moveToLeft = [CCMoveTo actionWithDuration:3.0 position:CGPointMake(0, sprite.position.y)];
[sprite runAction:moveToLeft];
}
-(void)moveRight{
[sprite stopActionByTag:1];
CGSize winSize = [[CCDirector sharedDirector]winSize];
NSMutableArray *spriteFrames = [NSMutableArray array];
CCSpriteFrame *frame1 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesRight6.png"];
CCSpriteFrame *frame2 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesRight4.png"];
CCSpriteFrame *frame3 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesRight5.png"];
[spriteFrames addObjectsFromArray:[NSArray arrayWithObjects:frame1,frame2,frame1,frame3, nil]];
CCAnimation* anim = [CCAnimation animationWithFrames:spriteFrames delay:0.15f];
CCAnimate* animate = [CCAnimate actionWithAnimation:anim];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:animate];
repeat.tag = 0;
[sprite runAction:repeat];
id moveToRight = [CCMoveTo actionWithDuration:3.0 position:CGPointMake(winSize.width, sprite.position.y)];
[sprite runAction:moveToRight];
}
-(id) init
{
if( (self=[super init])) {
[[CCSpriteFrameCache sharedSpriteFrameCache]addSpriteFramesWithFile:#"KnightImages2.plist"];
sprite = [CCSprite spriteWithSpriteFrameName:#"KnightSpritesRight6.png"];
sprite.position = ccp(240, 180);
[self addChild:sprite];
id actionSequence = [CCSequence actions:[CCCallFunc actionWithTarget:self selector:#selector(moveRight)],[CCCallFunc actionWithTarget:self selector:#selector(moveLeft)], nil];
CCRepeatForever *repeatForever = [CCRepeatForever actionWithAction:actionSequence];
[sprite runAction:repeatForever];
}
return self;
}
The way you've set this up is that the following happens, frame by frame:
Frame 1
execute moveRight
execute moveLeft
Frame 2
execute moveRight
execute moveLeft
Ad infinitum.
Since you're starting the animation all over every time you call moveRight or moveLeft, nothing really happens. You need to wait some time before running another animation for it to actually play. You can use the CCDelayTime action for this:
id actionSequence = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(moveRight)],
[CCDelayTime actionWithDuration:2],
[CCCallFunc actionWithTarget:self selector:#selector(moveLeft)],
[CCDelayTime actionWithDuration:2],
nil];

Cocos2d Chipmunk: Touch Shapes?

Today i got a cocos2d question!
I got a few shapes and the space-manager set up in my .m file by using nodes:
- (CCNode*) createBlockAt:(cpVect)pt
width:(int)w
height:(int)h
mass:(int)mass
{
cpShape *shape = [smgr addRectAt:pt mass:mass width:w height:h rotation:0];
cpShapeNode *node = [cpShapeNode nodeWithShape:shape];
node.color = ccc3(56+rand()%200, 56+rand()%200, 56+rand()%200);
[self addChild:node];
return node;
}
- (CCNode*) createCircleAt:(cpVect)pt
mass:(int)mass
radius:(int)radius
{
cpShape *shape = [smgr addCircleAt:pt mass:mass radius:radius];
cpShapeNode *node1 = [cpShapeNode nodeWithShape:shape];
CCSprite *sprt = [CCSprite spriteWithFile:#"fire.png"];
node1.color = ccc3(56+rand()%200, 56+rand()%200, 56+rand()%200);
[self addChild:node1];
return node1;
}
then i actually make the shapes, set up a background, alloc and start the space-manager in my init method:
- (id) init
{
[super init];
CCSprite *background = [CCSprite spriteWithFile:#"BGP.png"];
background.position = ccp(240,160);
[self addChild:background];
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:NO];
//allocate our space manager
smgr = [[SpaceManagerCocos2d alloc] init];
smgr.constantDt = 1/55.0;
[smgr addWindowContainmentWithFriction:1.0 elasticity:1.0 inset:cpvzero];
[self createBlockAt:cpv(160,50) width:50 height:100 mass:100];
[self createBlockAt:cpv(320,50) width:50 height:100 mass:100];
[self createBlockAt:cpv(240,110) width:210 height:20 mass:100];
[self createCircleAt:cpv(240,140) mass:25 radius:20];
[smgr start];
return self;
}
Now, i want a shape to be removed if it's touched. What is the best way to do that???
I hope that somebody out there can help me :)
Edit:
Plz somebody, start a bounty! Or i never get this answered :(
-DD
How about like this?
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint point = [self convertTouchToNodeSpace:touch];
cpShape *shape = [smgr getShapeAt:point];
if (shape) {
[self removeChild:shape->data cleanup:YES];
[smgr removeAndFreeShape:shape];
}
return YES;
}

Countdown timer in cocos2d?

I'm trying to create a countdown timer in cocos2d, but I can not help and would like to resolve this problem, my code is below this, perhaps the logic is wrong but I can not fix.
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {
CCSprite *background = [CCSprite spriteWithFile:#"backgame.png"];
CGSize size = [[CCDirector sharedDirector] winSize];
[background setPosition:ccp(size.width/2, size.height/2)];
[self addChild: background];
[self schedule:#selector(countDown:)];
}
return self;
}
-(void)countDown:(ccTime)delta
{
CCLabel *text = [CCLabel labelWithString:#" "
fontName:#"BallsoOnTheRampage" fontSize:46];
text.position = ccp(160,455);
text.color = ccYELLOW;
[self addChild:text];
int countTime = 20;
while (countTime != 0) {
countTime -= 1;
[text setString:[NSString stringWithFormat:#"%i", countTime]];
}
}
Your int countTime = 20; is declaring itself every time to be 20. Also, your while loop will decrement the countTimer as fast as the system can update the CCLabel. If you're trying to do a real timer, you want it to decrement ONLY when countDown: is called. Not during a while-loop.
Try this:
#interface MyScene : CCLayer
{
CCLabel *_text;
}
#property (nonatomic, retain) int countTime;
#end
#implementation MyScene
#synthesize countTime = _countTime;
-(id) init {
if( (self=[super init] )) {
CCSprite *background = [CCSprite spriteWithFile:#"backgame.png"];
CGSize size = [[CCDirector sharedDirector] winSize];
[background setPosition:ccp(size.width/2, size.height/2)];
[self addChild: background];
_countTime = 20;
_text = [CCLabel labelWithString:[NSString stringWithFormat:#"%i", self.countTime]
fontName:#"BallsoOnTheRampage" fontSize:46];
text.position = ccp(160,455);
text.color = ccYELLOW;
[self addChild:_text];
[self schedule:#selector(countDown:) interval:0.5f];// 0.5second intervals
}
return self;
}
-(void)countDown:(ccTime)delta {
self.countTime--;
[_text setString:[NSString stringWithFormat:#"%i", self.countTime]];
if (self.countTime <= 0) {
[self unschedule:#selector(countDown:)];
}
}
#end
Your count allways becomes 20 in your countDown.
You should also move this to your init:
CCLabel *text = [CCLabel labelWithString:#" "
fontName:#"BallsoOnTheRampage" fontSize:46];
text.position = ccp(160,455);
text.color = ccYELLOW;
[self addChild:text];
Then you should use:
[self schedule:#selector(countDown:) interval:1.0f];
Then instead of using CCLabel you should use CCLabelBMFont. It's much faster :)