SKScene not rendering when I need to update a SKLabelNode - rendering

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...

Related

CCActionDelay not working as expected

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.

AVAudioPlayer Number Of Loops only taking effect after being played through once

I'm a little stumped by this weird occurrence:
I have a UIButton, which once tapped either sets a loop for an audio player, or resets it to 0 (no loop). Here is the method -
-(void)changeLoopValueForPlay:(int)tag toValue:(bool)value{
AVAudioPlayer *av = [self.playerArray objectAtIndex:tag];
if(value){
[av setNumberOfLoops:100];
[av prepareToPlay];
}
else{
[av setNumberOfLoops:0];
}
}
Now for some reason, the loop will only take effect after the player plays through the audio one time, meaning that the looping value doesn't take affect immediately, but the "numberOfLoops" value of the player is in fact set to 100 when I check its value before playing. I'm assuming this has something to do with the initialization or loading of the player, but I don't re-initialize it between those two plays (one without loop, the other with). Any idea why this is happening? If you want to see any other code please let me know.
This fixed the problem, however I feel as if this is a work-around instead of a direct solution. What I did is just create a new AVAudioPlayer with the numberOfLoops value set to whatever it is I wanted and replace that player with the existing player, instead of changing the value of the already existing player.
I workaround the issue by abandoning numberOfLoops altogether and doing my own logic instead.
First, set the delegate of the AVAudioPlayer:
self.audioPlayer.delegate = self;
Next, implement -audioPlayerDidFinishPlaying:successfully: of the delegate:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer*)player successfully:(BOOL)flag
{
if(flag && <#(bool)i_want_to_repeat_playing#>)
{
[self.audioPlayer play];
}
}
Just replace <#(bool)i_want_to_repeat_playing#> with your desired logic, e.g., check if a counter has reached a certain threshold.

How to run two methods in sequence at time delay?

[self goToStage:currentStage];
[actor deathAnimation];
I have these two methods. I want to call it one by one in sequence. When one is completed after second one is started.
//this method used in nsobject subclass
-(void)deathAnimation {
//Play death animation
}
//this method in cclayer subclass
-(void)goToStage:(int)stage {
//changing scene
}
IN my tick function at particular event i want to call in seqeuence
I use following code but it not working
[CCSequence actions:[CCCallFunc actionWithTarget:actor selector:#selector(deathAnimation)], [CCDelayTime actionWithDuration:4.0], [CCCallFuncN actionWithTarget:self selector:#selector(goToStage:)], nil];
Now what i do? Please tell me.. Is there something wrong?
You can use CCDelayTime, that is used to give a delay, and CCCallBlock,to pass a method to be invoked, like this:
[self runAction:[CCSequence actions:[CCDelayTime actionWithDuration:1],
[CCCallBlock actionWithBlock:^{[self goToStage:currentStage];}],
[CCDelayTime actionWithDuration:1],
[CCCallBlock actionWithBlock:^{[actor deathAnimation}], nil]];
Define a block in you inteface as e.g.
typedef void(^MyCompletion)(void);
edit you deathAnimation to take a block parameter as
- (void)deathAnimationWithCompletion:(MyCompletion)finish {
//..death animation
//...
//when animation finishes
finish(); // This will call your completion block
}
Call this method as
[self deathAnimationWithCompletion:^{
[self goToStage:2];
}];
You can read up on blocks at Ray Wenderlichs fantastic blog.
Hope it helps!
EDIT DUE TO COMMENT
In cocos2d I think you can also make a sequence like this
id aFuncCall = [CCCallFunc actionWithTarget:self selector:#selector(deathAnimation)];
id antoherFuncCall = [CCCallFunc actionWithTarget:self selector:#selector(goToSecondStange:)];
CCSequence *sequence = [CCSequence actions:aFuncCall,anotherFuncCall, nil];
[self runAction:sequence];
But my cocos2d programming skills are bit outdated so not sure if this works...
You can call
[actor deathAnimation];
at the end of the goToStage function, that way it will execute everything in goToStage before calling deathAnimation.
You could put a delay timer in the goToStage if you require it to wait for a specific amount of time.

Object changes from NSMutableArray to NSData to NSString

I have a program which works normally. Then I downloaded some code from http://github.com/matej/MBProgressHUD to show a progress meter when doing something.
This is the code that makes the progress meter pop up.
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
This will show a progress meter while the method myTask is running.
This is the code for the showWhileExecuting method.
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
methodForExecution = method;
targetForExecution = [target retain];
objectForExecution = [object retain];
// Launch execution in new thread
taskInProgress = YES;
[NSThread detachNewThreadSelector:#selector(launchExecution) toTarget:self withObject:nil];
// Show HUD view
[self show:animated];
}
If I use this to call the function myTask then one of my class properties will change from an NSMutableString to an NSData object somewhere, and then later on it will change to an NSString. I don't see anywhere in the code that causes this to change, so it's probably some kind of bug. Is memory getting corrupted? What's causing this to happen?
Most likely it's a memory (retain/release issue). If you don't properly retain an object, it may get released out from under you. At that point, the memory will be reclaimed by the OS, which may decide to store something else there. Try turning on NSZombies, and double checking your retain/release/autoreleases.

Confounding Cocoa problem — program hangs unless there’s an unrecognised method call

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