I have a ship and I want to make it to shoot every 1 sec. So here's what I did,
#property (nonatomic, assign) float lastDamageTime;
and in the update method,
if (CACurrentMediaTime - self.lastDamageTime > 1)
{
self.lastDamageTime = CACurrentMediaTime;
[self shoot];
}
But the problem is, say I pause the game right after the ship shoot a bullet, and then 1 sec later, I resume the game, the if statement pass the check, and the ship will shoot another bullet immediately. And that's not what I want.
So, what can I do to make sure the ship fire bullets each sec weather I pause the game or not? Should I use CCTimer instead?
Thank you in advance for your answer.
You could try and schedule your shoots as a CCRepeatForever action, so that cocos2d deals with pause for you. It'd look something like this (assuming you're inside a custom ShipSprite).-
CCDelayTime *delay = [CCDelayTime actionWithDuration:1.0];
CCCallFunc *funcShoot = [CCCallFunc actionWithTarget:self selector:#selector(shoot)];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:[CCSequence actionOne:delay two:funcShoot]];
[self runAction:repeat];
Instead of CCRepeatForever you can try something like this
[self schedule:#selector(shoot) interval:1];
In your pause code unschedule the shooting
[self unschedule:#selector(shoot)];
In your code where it detects an enemy call
[self unschedule:#selector(shoot)];
[self performSelector:#selector(shoot)];
[self schedule:#selector(shoot) interval:1];
And it will shoot immediately, however it will only shoot once and then restart at 1 second intervals etc... so if you need to change that you can just adjust the timings
Related
I use this code to show game over menu after _hero sprite and _enemy sprite collide :
if (CGRectIntersectsRect(_hero.boundingBox,_enemy.boundingBox)) {
CCActionCallFunc *_callShowMenu=[CCActionCallFunc actionWithTarget:self selector:#selector(showMenu)];
[self runAction:_callShowMenu];
// EDIT : I also remove both sprites when collision happens.
[_hero removeFromParent];
[_enemy removeFromParent];
}
In _callShowMenu I just stop all actions and show a sprite with half transparent black background image and buttons.
Sometimes when collision happens, it seems to me, that _callShowMenu is called twice, because
background is completely black, like there is the same image behind. Has anyone had a similar problem? (Mostly background image is half-transparent, as it should be).
EDIT:
-(void)showMenu{
[[CCDirector sharedDirector] pause];
CCSprite *_halfTransparentBackground=[CCSprite spriteWithImageNamed:#"halfTransparentBackground.png"];
_halfTransparentBackground.position=ccp(160, 280);
[self addChild:_blackBack z:5];
}
I found a solution using BOOL. Actually everyone uses BOOL in this case, so I don't need to reinvent the wheel.
BOOL doNotCallMeTwice;
Somewhere in the didLoad method:
doNotCallMeTwice=NO;
In the collision detection method:
if (doNotCallMeTwice==NO) {
[self showMenu];
}
And finally:
-(void)showMenu{
doNotCallMeTwice=YES;
}
Possibly, showMenu was called twice(or much more times),because collision detection is in the update method.
Evening all,
I'll start this question in the time honoured tradition by saying that I've had a good old search on SO and also in the wider world but I'm not quite getting my head around this...
I've implemented a sneakyJoystick which works wonderfully (It moves my sprite around quite happily) however I've now done myself a mischief in thinking about it's positioning.
What I'd like to do is simply change it's position to a touch location and have it move my sprite around but this seems to be out of my knowledge pool. I might be being an idiot but I cannot work it out.
The touch events are already sorted in the sneakyjoystick classes (available on github https://github.com/0xPr0xy/sneaky-joystick-cocos2d). At the moment if I create the joystick during the init method in a class called controlsLayer then everything works fine; Joystick appears and it allows me to move to sprite around
-(id) init
{
if( (self=[super init]) ) {
myJoystickBase = [[[SneakyJoystickSkinnedBase alloc] init] autorelease];
myJoystickBase.backgroundSprite = [CCSprite spriteWithFile:#"dpad.png"];
myJoystickBase.thumbSprite = [CCSprite spriteWithFile:#"joystick.png"];
myJoystickBase.joystick = [[SneakyJoystick alloc] initWithRect:CGRectMake(0, 0, 128, 128)];
myJoystickBase.position = ccp(64, 64);
myJoystickBase.backgroundSprite.opacity = 100;
myJoystickBase.thumbSprite.opacity = 100;
[self addChild:myJoystickBase];
myJoystick = [myJoystickBase.joystick retain];
[self scheduleUpdate];
}
return self;
}
So to begin with I thought about looking at how I could simply get it to show and hide itself and set it's location. To do this I created a ccTouchesbegan method which contains pretty much the same code as my init method did before. This works fine up to a point (the joystick appears centred wherever I touch) but the issue now is that I cannot interact with it. The joystick appears where i want but it will not recognise my movements (the stick on the joystick does not move which in turn means that my sprite is not being told to move either)
if( (self=[super init]) ) {
self.isTouchEnabled = YES;
[self scheduleUpdate];
}
return self;
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
location = [self convertToNodeSpace:location];
myJoystickBase = [[[SneakyJoystickSkinnedBase alloc] init] autorelease];
myJoystickBase.backgroundSprite = [CCSprite spriteWithFile:#"dpad.png"];
myJoystickBase.thumbSprite = [CCSprite spriteWithFile:#"joystick.png"];
myJoystickBase.joystick = [[SneakyJoystick alloc] initWithRect:CGRectMake(0, 0, 128, 128)];
myJoystickBase.position = location;
myJoystickBase.backgroundSprite.opacity = 100;
myJoystickBase.thumbSprite.opacity = 100;
[self addChild:myJoystickBase];
myJoystick = [myJoystickBase.joystick retain];
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[self removeChild:myJoystickBase cleanup:YES];
}
So to my uninitiated brain this is saying that when I touch the controlsLayer class it happily does what I've asked it to but it will not then pass this touch to other classes.
In a nutshell can a touch event be passed to multiple classes at the same time? The sneaky joystick class uses CCTargetedTouchDelegate which worked fine when it was the only thing looking for a touch. However now that I've added a -(void)ccTouchesBegan: in another class it's not happy.
Can anyone tell me if the problem is with the way I'm handling touches or is it possibly an issue with the way that I allocate the joystick in the touch method? Should I be allocating the joystick in the init method and be doing something else in the touchesBegan method? Trial and error isn't getting me anywhere useful at the moment. Feel like I'm banging my head against a brick wall. I'm happy to post up the full class files if necessary. Does this make sense?
The problem is that you create the joystick during touch began, and remove it on touch ended. This does not allow the joystick to receive a touch began event, since the event is already being processed at the time the joystick is being created.
You're also leaking the joystick due to the unnecessary retain (use ARC, please!).
Try creating the joystick in init, disable & hide it until a touch began event is received. This is also faster than recreating the joystick on every touch.
Also, should you have multiple touches enabled, keep in mind that you can receive up to 5 touch began events (5 fingers) without any touch ended in between. That would create 5 joysticks, but only remove one! Every time!
I'm triggering several animations inside CCCallBlock. Each animation inside has a duration but the number of them is defined in runtime. In the game there's state machine and I need to wait until the last animation is finished, but I can't use this:
if([self numberOfRunningActions] > 0)
because the action is a CCCallBlock with duration 0.
How I can set a duration for the CCCallBlock action? Is possible to do it in runtime?
It might be wrong for a lot of reasons, but I came up with this:
[CCCallBlock actionWithBlock:^{
float duration = [self.cardsMatrix peepCards];
[self runAction:[CCDelayTime actionWithDuration:duration]];
}];
here self is the CCLayer with the state machine that controls the game.
CCCallBlock runs a block which is a C function (sort of). You can't set a duration for a function.
You can add another CCCallBlock to your animation, put them in a CCSequence and when this CCCallBlock runs, you know that the animation is done.
[self runAction:[CCSequence actions:[CCCallBlock actionWithBlock:^{
NSLog(#"my block");
}], [my next function], nil]];
I have this setup:
A layer has a sprite as a child. The sprite has this code in its init method:
id fadeOut = [CCFadeOut actionWithDuration:1.0f];
id death = [CCCallFunc actionWithTarget:self selector:#selector(die)];
self.deathAction = [CCSequence actions:fadeOut, death, nil];
[self runAction:deathAction_];
The death action calls the sprite's 'die' method in which its status is changed to 'dead'.
On its update method the layer checks all children and removes the ones wich are 'dead' with:
[self removeChild:child cleanup:YES];
The problem is that the child sprite still has retaincount of 2 after this line. As I understand it is kept by CCCallFunc. If I omit this deathAction and instead remove the sprites that have zero opacity (when they are faded), the code is working and the dealloc method gets called.
How should I remove the sprite by using an action properly?
You don't show all of your code. But it seems you are keeping a reference to the action and your likely forgetting to release that.
retainCount is notoriously unreliable : link so please don't use it and certainly don't count on it to be accurate.
I am doing a game like chess. As soon my player move is completed(if he starts move from one place to another), my AI move is started (before my player reaching his destination ). Sometimes i find difficult which AI coin is moved now. how to delay it.
If your player movement is bounded by Core Animation, you can setup the setAnimationDidStopSelector to a custom STOP function and starts your AI there.
If you have a game loop with states, just add enough states (e.g. user_move_began, user_move_ended, ai_think_began, ai_think_ended, ai_move_began .. ) to sequence the flow.
I think I understand what you are saying now.
You need to do a CCSequence, with your AI function call in a CCCallFunc at the end:
CCSequence *playerMove = [CCSequence actions:
[CCMoveTo actionWithDuration: 0.4f position: CGPointMake(10,10)],
[CCCallFunc actionWithTarget: self selector: #selector(doAIstuff)],
nil];
[playerSprite runAction: playerMove];
You need to have your AI be a callable function (i.e. doAIstuff). It is confusing to that you say the code is in draw().