What I have is a method that creates CCSprites:
-(void)createDebrisAtPosition:(CGPoint)position{
NSInteger numberOfPieces = [random randomWithMin:5 max:20];
for (int i=0; i<numberOfPieces; i++) {
CCSprite *debris = [CCSprite spriteWithImageNamed:#"debri.png"];
debris.position = position;
debris.physicsBody = [CCPhysicsBody bodyWithRect:CGRectMake(0, 0, debris.contentSize.width, debris.contentSize.height) cornerRadius:0];
debris.physicsBody.collisionType = #"debris";
debris.name = #"Debris";
CCActionRemove *removeAction = [CCActionRemove action];
CCActionSequence *sequence = [CCActionSequence actions:[CCActionDelay actionWithDuration:2.0], removeAction, nil];
[physics addChild:debris];
//physics is a CCPhysicsNode here
[debris runAction:sequence];
}
}
This method then gets invoked during specific collision events:
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair enemy:(EnemyNode*)enemy projectile:(ProjectileNode*)projectile
{
[enemy removeFromParent];
[projectile removeFromParent];
[self createDebrisAtPosition:enemy.position];
return NO;
}
Expected behavior: CCSprites should appear and then get removed only after 2.0 secs.
Actual behavior: CCSprites appear for a split-second, then instantly get removed.
I also tried CCActionInterval, CCActionEaseOut, but they didn't work (And they shouldn't, according to the docs, but CCActionDelay — should, but not working). I changed the order of method invocation (runAction after and before addChild), as well as the order of action invocation this didn't work as well. Don't mind the CCActionDelay declaration directly in the CCActionSequence — I tried to declare it as a separate variable, with zero luck.
What am I misunderstanding here?
I'm new here so I'm not allowed to comment yet (this would perhaps be better suited as a comment) but: I haven't been able to recreate your problem. The problem is not related to CCActionDelay or the actions you are running on the debris sprite. You can test this yourself by running your sequence in a different setup. Ergo: there must be a problem somewhere else in your code. I'm sorry, but I cannot help any further based on the example code you've posted.
Related
I am programming a game, and using a SKLabel to view the current score. The problem is that when score changes it doesn't display the change in the screen at the moment, it does after a second more or less. What can I do to see the changes in the moment I use [sklabelscore setTextScore:++self.score]. Can I force render or something similar?
I call the setTextScore when the user touches an enemy, with touchesBegan:withEvent:
setTextScore: implementation is
SKLabelNode* scoreLabel=(SKLabelNode*)[self childNodeWithName:#"scoreLabel"];
scoreLabel.text=[NSString stringWithFormat:#"Score: %d",self.score];
It seems most likely that the ++score you increment is a local variable and not the same as self.score.
You call the method as follows:
[sklabelscore setTextScore:++score]
Which means its signature with code must be:
-(void) setTextScore:(int)theNewScore
{
SKLabelNode* scoreLabel=(SKLabelNode*)[self childNodeWithName:#"scoreLabel"];
scoreLabel.text=[NSString stringWithFormat:#"Score: %d",self.score];
}
So you're passing in theNewScore but instead of using that in the format string you use self.score which may never be updated if the incremented ++score variable is a local variable (ie never assigns its new value to self.score).
Solved... I feel like an idiot :S
The problem was I fade out the enemy when I touched it, and then, after 0.5 seconds, changes the label. I put that out of the block and all working fine.
Changed setTextScore: method because was redundant (thanks #LearnCocos2D)
...
SKAction* fade=[SKAction fadeOutWithDuration:0.5];
[node runAction:fade completion:^{
[node removeFromParent];
self.enemyNumber--;
self.score++;
SKLabelNode* scoreLabel=(SKLabelNode*)[self childNodeWithName:#"scoreLabel"];
scoreLabel.text=[NSString stringWithFormat:#"Score: %d",self.score];
}];
The new form (outside of the block):
...
self.score++;
SKLabelNode* scoreLabel=(SKLabelNode*)[self childNodeWithName:#"scoreLabel"];
scoreLabel.text=[NSString stringWithFormat:#"Score: %d",self.score];
SKAction* fade=[SKAction fadeOutWithDuration:0.5];
[node runAction:fade completion:^{
[node removeFromParent];
}];
Thanks for your help and sorry for asking this stupid question...
I'm having a lot of trouble figuring this one out. This method is imported from another class into my viewController.m It works fine if I copy the code into an IBAction in the same file. And the "test midi" is logging when it is supposed to. So the IBOutlets and animation code seem OK but for some reason this method does not do what it is supposed to
- (void) source:(theSource*)data dataReceived:(const dataList *)theList
{
led.animationImages = [NSArray arrayWithObjects:[UIImage imageNamed:#"led-highlighted.png"],[UIImage imageNamed:#"led-passive.png"],nil];
led.animationDuration = 0.3;
led.animationRepeatCount = 20;
[led startAnimating];
NSLog(#"test");}
it also doesn't work if I simply put this to swap the image. The method is being called because it is logging to console.
midiLed.image = [UIImage imageNamed:# "led-highlighted.png"];
The problem may lie in how you are calling this method. It should be on the main thread, and after calling it, your code should return to the run loop (i.e. your code should be "done") before changes to the user interface will have effect.
If this doesn't help you find it, please share the code calling this method.
What thread is -source:dataReceived: being called on? If you're using CoreMIDI, your MIDI-received function will get called on a separate, high-priority thread.
You should not touch the UI from a non-main thread, because UIKit isn't thread safe.
Here's one way to get the UI running on the main thread:
- (void) source:(theSource*)data dataReceived:(const dataList *)theList
{
dispatch_async(dispatch_get_main_queue(), ^{
led.animationImages = [NSArray arrayWithObjects:[UIImage imageNamed:#"led-highlighted.png"],[UIImage imageNamed:#"led-passive.png"],nil];
led.animationDuration = 0.3;
led.animationRepeatCount = 20;
[led startAnimating];
NSLog(#"test");
});
}
My code has two Bullet-related classes. Bullet and BulletCache. The BulletCache creates a certain number of
I have moved on to just creating a new bullet creating method meant to shoot off the bullets. I used the CCFuncN method but the game is currently throwing NSException errors:
CCAction* action = [CCSequence actions:
[CCAnimate actionWithAnimation:[profile getAnimation:#"attack" index:currentDir]],
[CCCallFuncN actionWithTarget:self selector:#selector(shootBulletFrom:)],
nil];
NSInvalidArgumentException', reason: '-[Player shootBulletFrom:]: unrecognized selector sent to instance 0x703ec70'
edit:
For further help and advice here is the shootBulletFrom method in the BulletCache.
This method is in the BulletCache
-(void) shootBulletFrom:(CGPoint)startPosition velocity:(CGPoint)velocity frameName:(NSString*)frameName
isPlayerBullet:(bool)isPlayerBullet
{
CCArray* bullets = [batch children];
CCNode* node = [bullets objectAtIndex:nextInactiveBullet];
NSAssert([node isKindOfClass:[Bullet class]], #"not a Bullet!");
Bullet* bullet = (Bullet*)node;
[bullet shootBulletAt:startPosition velocity:velocity frameName:frameName
isPlayerBullet:isPlayerBullet];
nextInactiveBullet++;
if (nextInactiveBullet >= [bullets count])
{
nextInactiveBullet = 0;
}
}
I was also recommended to change the [CCCallFuncN] call at the bottom to:
[CCCallFuncN actionWithTarget:self selector:#selector(shootBulletFrom:shotPos velocity:velocity frameName:#"bullet1big.png" isPlayerBullet: YES)],
But then I got the compile Error: Expected ':' before Velocity
You have not mentioned the code for shootBulletFrom, and the error denoted that there is some mistake in the same. May be you have not declared the function in .h file or some other. So if possible mention so.
You can go through this and this links. They are having good examples for bullet firing apps. Hope that helps you.
Is it possible to check if there are actions currently running in a CCNode class in Cocos2d? I'd like to know if a CCMoveBy is still running or not.
You can use [self numberOfRunningActions] on any CCNode. In your case, it sounds like you want to know if there are simply any actions running or not, so it's not a big deal to know the exact number beforehand.
We can easily check if specific actions run by using getActionByTag method and action.tag property.
There is no need to introduce the CCCallFuncN callbacks or counting numberOfRunningActions.
Example.
In our app it is important to let the jumpAction to be finished prior to executing another jump. To prevent triggering another jump during an already running jump action
the critical jump section of code is protected as follows:
#define JUMP_ACTION_TAG 1001
-(void)jump {
// check if the action with tag JUMP_ACTION_TAG is running:
CCAction *action = [sprite getActionByTag:JUMP_ACTION_TAG];
if(!action) // if action is not running execute the section below:
{
// create jumpAction:
CCJumpBy *jumpAction = [CCJumpBy actionWithDuration:jumpDuration position:ccp(0,0) height:jumpHeight jumps:1];
// assign tag JUMP_ACTION_TAG to the jumpAction:
jumpAction.tag = JUMP_ACTION_TAG;
[sprite runAction:jumpAction]; // run the action
}
}
You can always add a method to indicate when the method is finished, and then toggle some BOOL or something like that to indicate it is not running, and put a start method to toggle the BOOL to indicate it started:
id actionMove = [CCMoveTo actionWithDuration:actualDuration
position:ccp(-target.contentSize.width/2, actualY)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteMoveFinished:)];
id actionMoveStarted = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteMoveStarted:)];
[target runAction:[CCSequence actions:actionMoveStarted, actionMove, actionMoveDone, nil]];
Modified from here.
In the two #selector methods:
-(void) spriteMoveStarted:(id)sender {
ccMoveByIsRunning = YES;
}
and:
-(void) spriteMoveFinished:(id)sender {
ccMoveByIsRunning = NO;
}
where ccmoveByIsRunning is the BOOL I'm referring to.
EDIT: As xus has pointed out, you should actually not do this and instead use [self numberOfRunningActions] as others have pointed out.
Bear with me, this one is hard to explain. I hope some hero out there knows what’s going on here. Some history needed;
One of my cocoa objects, “Ball” represents a small graphic. It only makes sense within a view. In some of the Ball’s methods, it asks the view to redraw. Most importantly, it asks the view to redraw whenever the Ball’s position parameter is set. This is achieved in the setter.
Here’s the mouthful, as suggested:
In View.m
- (void)mouseUp:(NSEvent *)theEvent {
if (![runnerPath isEmpty]) {
[walkPath removeAllPoints];
[walkPath appendBezierPath:runnerPath];
[runnerPath removeAllPoints];
[[self held] setStep:0];
[[self held] setPath:walkPath];
[NSTimer scheduledTimerWithTimeInterval:.01 target:[self held] selector:#selector(pace) userInfo:nil repeats:YES];
}
}
In Ball.m
- (void)pace {
CGFloat juice = 10;
BOOL loop = YES;
while (loop) {
if ([self step] == [[self path] elementCount]) {
if ([[self timer] isValid]) {
[[self timer] invalidate];
}
[[self path] removeAllPoints];
// #throw([NSException exceptionWithName:#"test" reason:#"reason" userInfo:nil]);
}
if (loop) {
CGFloat distance;
NSPoint stepPoint;
if ([[self path] elementCount] > 0) {
NSPoint returnPoints[2];
[[self path] elementAtIndex:[self step] associatedPoints:returnPoints];
stepPoint = returnPoints[0];
distance = pixelDistance([self position], stepPoint);
}
if (distance <= juice) {
[self setPosition:stepPoint];
if (distance < juice) {
juice -= distance;
loop = YES;
[self setStep:[self step]+1];
} else {
loop = NO;
}
} else {
NSPoint cutPoint = moveAlongBetween([self position], stepPoint, juice);
[self setPosition:cutPoint];
loop = NO;
}
}
}
}
could you also tell how you handle exceptions? since normally an unrecognized selector will end your program. Maybe you need an exception rather than an unrecognized selector. Try:
#throw([NSException exceptionWithName:#"test" reason:#"reason" userInfo:nil]);
If this would fix it as well, you're doing something after this code which freezes the app.
edit: thanks for the code update.
There's some weird stuff going on here! I'm not going to rewrite the whole thing, so here's some pointers:
first of all: you're looping inside some routine that is called from a timer loop. Is that intended? There is no way to pause execution within that while() loop, so it will happen in a blink anyway. You would need to keep some state information in the class. E.g. adding a loop counter every time pace is called.
second: if you start a timer, it will call your selector with the timer as an argument. So define the function as -(void)pace:(NSTimer*)timer, and use timer, not [self timer] (the latter will not be your timer anyway, if you don't assign it!)
third: you're firing 100 times a second. That is a lot, and presumably higher than the refresh rate of any device you're writing this for. I think 20/sec is enough.
fourth: to be sure, if you change it to -(void)pace:(NSTimer*)timer, don't forget to use #selector(pace:) (i.e. don't forget the :)
fix those things, and if it's still broken, update your question again and put in comment so we will know. Good luck!
Try calling
for (NSView *each in [self views]) {
...
}
I'm assuming that views is an array, so fast enumeration applies to it directly and there is no need to call allObjects.
A couple of other points.
Have you set a Global breakpoint of objc_exception_throw? This will apply to all Xcode projects and is so useful I'm surprised it isn't set by default.
You say you looked at the Console for errors. I take it, then, that you didn't set a breakpoint on the code and step into it to see exactly what is happening when your execution reaches that point? Have a look at the Xcode Debugging Guide