I'd like to run a callback/selector when a Cocos2d CCParticleExplosion is completely finished. How do I do this?
I tried using scheduleOnce with the same duration as the emitter, but that finish too soon since I guess the duration of the emitter controls for how long it will emit new particles, but not how long the complete animation lasts.
Try sequencing the action (using CCSequence) with a CCCAllFunc Action. After one action runs, the other runs, the CCCAllFunc can be assigned to the selector/method of your choice.
Not sure if this is acceptable, but I tested and "it works on my mac".
in CCParticleSystem.h
// experimental
typedef void (^onCompletedBlock)();
#property (readwrite, copy) onCompletedBlock onComplete;
in CCParticleSystem.m
#synthesize onComplete;
in the same file update method,
_particleCount--;
if( _particleCount == 0 && self.onComplete)
{
[self onComplete]();
[[self onComplete] release];
}
if( _particleCount == 0 && _autoRemoveOnFinish ) {
[self unscheduleUpdate];
[_parent removeChild:self cleanup:YES];
return;
}
In your code
particleSystem.autoRemoveOnFinish = YES;
particleSystem.position = ccp(screenSize.width/2, screenSize.height/4);
particleSystem.onComplete = ^
{
NSLog(#"completed ..");
};
again, quite experimental..
Related
In cocos2d v3, I could not find something like CCTargetedAction.
It is required in my project, so I copied code from cocos2d v2.
#interface CCTargetedAction : CCActionInterval
/** This is the target that the action will be forced to run with */
#property(readwrite,nonatomic,retain) id forcedTarget;
#property(readwrite,nonatomic,retain) CCActionFiniteTime* action;
/** Create an action with the specified action and forced target */
+(id)actionWithTarget:(id)target
action:(CCActionFiniteTime*)action;
/** Init an action with the specified action and forced target */
-(id)initWithTarget:(id)target
action:(CCActionFiniteTime*)action;
#end
#implementation CCTargetedAction
+(id)actionWithTarget:(id)target
action:(CCActionFiniteTime*)action
{
return [(CCTargetedAction*)[self alloc] initWithTarget:target
action:action];
}
-(id)initWithTarget:(id)target
action:(CCActionFiniteTime*)action
{
self = [super initWithDuration:action.duration];
if(self)
{
self.forcedTarget = target;
self.action = action;
}
return self;
}
-(id)copyWithZone:(NSZone*)zone
{
CCAction *copy = [(CCTargetedAction*) [[self class] allocWithZone: zone]
initWithTarget:_forcedTarget
action:[_action copy]];
return copy;
}
- (void) startWithTarget:(id)aTarget
{
[super startWithTarget:aTarget];
[_action startWithTarget:_forcedTarget];
}
- (void) stop
{
[_action stop];
}
- (void) update:(CCTime) time
{
[_action update:time];
}
#end
But my CCTargetedAction runs action twice.
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CCActionCallBlock* block = [CCActionCallBlock actionWithBlock:^{
CCLOG(#"call block");
}];
CCTargetedAction* action = [CCTargetedAction actionWithTarget:self
action:block];
[self runAction:action];
}
If I touch the screen once, then the log message is output twice.
2014-04-07 22:09:57.439 TargetedActionTest[3924:60b] call block
2014-04-07 22:09:57.455 TargetedActionTest[3924:60b] call block
Why this code runs action twice?
Thank you.
This problem is solved by overwriting -(BOOL)isDone method.
-(BOOL)isDone
{
return [_action isDone];
}
I was referring to this post.
http://cocos2d-x.org/forums/6/topics/39546
If the below 'delayingTest' is called N times, I expect it to eventually call doIt N times as well. However this is not the case, it seems as if the schedule overwrites any previous schedule, supposedly having the same selector. Anyway around this?
-(void)delayingTest {
if (_delay) {
[self schedule:#selector(delayingTest) interval:1.0f repeat:0 delay:1.0f];
}
else {
[self doIt];
}
}
one way would be :
dont use _delay as int but bind it with timestamp storing it in NSDate and comparing the timestamps when next time your method gets called.
Try This:
-(void)delayingTest
{
if (_delay)
{
[self unschedule:#selector(delayingTest)];
[self schedule:#selector(delayingTest) interval:1.0f];
}
else
{
[self doIt];
}
}
I'm running cocos2d with cocosbuilder 2.1 and I use the cocosbuilder animation delegate (CCBAnimationManagerDelegate::completedAnimationSequenceNamed) to get notified when an animation has completed and take actions like firing another cocosbuilder animation.
It runs fine the first time the foodfactoryshow animation is run from the delegate and after the animation is completed it also runs restoration animation correctly. However, when restoration animation is completed, the parameter name for -(void) completedAnimationSequenceNamed method is NULL!?
-(void) completedAnimationSequenceNamed:(NSString*)name
{
if ([name isEqualToString:#"foodfactoryshow"])
{
[manager runAnimationsForSequenceNamed:#"restoration"];
}
if ([name isEqualToString:#"restoration"])
{
[self colorLayerChanged];
self.gameLayer.isLock = true;
}
}
Is this a bug or am I not supposed to run animations from the CCBAnimationManagerDelegate::completedAnimationSequenceNamed method!?
Thanks in advance for your help.
I believe it is a CCBReader bug. There is an open issue about it in the CocosBuilder github page (https://github.com/cocos2d/CocosBuilder/issues/121). It should be fixed in the latest version of CocosBuilder + CCBReader, however, if you want to use the 2.1 version you can change the "sequenceCompleted" method of CCBAnimationManager to the following:
- (void) sequenceCompleted
{
NSString *completedSequenceName = [runningSequence.name copy];
int nextSeqId = runningSequence.chainedSequenceId;
runningSequence = NULL;
if (nextSeqId != -1)
{
[self runAnimationsForSequenceId:nextSeqId tweenDuration:0];
}
[delegate completedAnimationSequenceNamed:completedSequenceName];
[completedSequenceName release];
}
I'm studying Strougo/Wenderlich tutorial (space Viking project). I got troubles with chapter 4.
In RadarDish.m:
-(void)initAnimations
{
[self setTransmittingAnim: [self loadPlistForAnimationWithName:#"transmittingAnim" andClassName:NSStringFromClass([self class])]];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
id action = nil;
[self setCharacterState:newState];
switch (newState) {
.
.
case kStateIdle:
action = [CCAnimate actionWithAnimation:transmittingAnim
restoreOriginalFrame:NO];
break; }
if (action != nil) {
[self runAction:action];
}
}
-(id)init
{
self=[super init];
if (self!=nil) {
.
.
[self initAnimations];
.
.
}
return self;
}
Exact the same code as in the tutorial. Failure:
*** Assertion failure in -[CCAnimate initWithAnimation:], /Users/macowner/Documents/examples/SpaceViking/SpaceViking/libs/cocos2d/CCActionInterval.
Using debugger with breakpoints, i noticed that value of transmittingAnim = nil.
So, if i put line with
[self setTransmittingAnim:
[self loadPlistForAnimationWithName:#"transmittingAnim" andClassName:NSStringFromClass([self class])]];
into case of
-(void)changeState then animation works correctly.
Why [self initAnimations] from (id)init is not called?
Im using cocos2d v.2 templates.
Great thanks in advance.
I had problems because I have been building project using cocos 2d v.2.0, while tutorial is based on cocos 2d templates v.1.x.x If you are going to follow the book "Learning Cocos2D", I strongly recommend you loading cocos2d-iphone version 1.0.1. Here is the link download cocos2d 1.x.x branch
if you still want to use latest cocos2d templates, I can give you some advice:
Follow the instructions in this link cocos2d v2.0 migration guide
You are going to have a lot of deprecations and changes to fix, so use this link to understand how to fix those deprecations and changes.
Now few words about the solution of the problem I mentioned here. In each of the GameObjects, EnemyObjects, and PowerUps, I added a method to override initWithFrameName.
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### RadarDish initialized");
[self initAnimations]; // 1
characterHealth = 100.0f; // 2
gameObjectType = kEnemyTypeRadarDish; // 3
[self changeState:kStateSpawning]; // 4
}
}
return self;
}
This allows the GameObject and GameCharacter init methods to run before the CCSprite's initWithSpriteFrameName method to run.
The Viking GameObject had to have a slightly different solution because it is initialized with initWithSpriteFrame rather than initWithSpriteFrameName. However, the override implementation is basically the same as the example of RadarDish above.
when the game scene is done, and i replace to menuScene , everything is ok, but when i replace it back to the gameScene , i allways get the same crash, and just cant figure out what could be the cause to this:
the crash is in the CCscheduler.m class in the next method :
-(void) update: (ccTime) dt
{
if( elapsed == - 1)
elapsed = 0;
else
elapsed += dt;
if( elapsed >= interval ) {
impMethod(target, selector, elapsed); **//this line gets exc_BAD ! !**
elapsed = 0;
}
}
#end
any direction? is it memory? or timers? what should i check for ?
thanks.
Well, it seems to me that you haven't unscheduled all selectors and the function is called on a deallocated object. Try calling [self unscheduleAllSelectors] on your scenes before replacing them.