I am running Instruments and it indicates that the SimpleAudioEngine is leaking memory. The screenshot is attached. The memory leak is multiple times although the screenshot only shows one instance.
Also, sometimes it points to the following implementation (my code):
-(void) preloadGameSounds
{
// pre load the background sound
[[SimpleAudioEngine sharedEngine] preloadEffect:#"farm_background_sound.mp3"];
// pre load the game sounds
[[SimpleAudioEngine sharedEngine] preloadEffect:#"chickenlayingegg.mp3"];
// setup ding sound
[[SimpleAudioEngine sharedEngine] preloadEffect:#"ding.caf"];
// egg pop sound
[[SimpleAudioEngine sharedEngine] preloadEffect:#"baloonpop.wav"];
// preload applause sound
[[SimpleAudioEngine sharedEngine] preloadEffect:#"applause.mp3"];
// wrong answer sound
[[SimpleAudioEngine sharedEngine] preloadEffect:#"wrong_answer_sound.wav"];
}
When changing the scenes I also unload the sound using the following implementation:
-(void) unloadSoundEffects
{
[[SimpleAudioEngine sharedEngine] unloadEffect:#"applause.mp3"];
//[[SimpleAudioEngine sharedEngine] unloadEffect:#"wrong_answer_sound.wav"];
[[SimpleAudioEngine sharedEngine] unloadEffect:#"ding.caf"];
[[SimpleAudioEngine sharedEngine] unloadEffect:#"chickenlayingegg.mp3"];
}
This memory leak is making the FPS of the game to go low and making game slower and slower!
From the cocosdenshion FAQ:
What should I retain/release?
SimpleAudioEngine, CDAudioManager and
CDSoundEngine APIs are all accessed
through a shared singleton instance.
This is a common pattern that is used
throughout Cocoa Touch and cocos2d.
The shared instance should not be
retained or released.
If you need to completely shut down
CocosDenshion and free all resources
it was using then call the end method
on the highest level API you are
using. For example if you are using
SimpleAudioEngine then just call
[SimpleAudioEngine end].
If you use CDSoundSource objects you
must obtain them through one of the
factory methods such as
soundSourceForFile. The CDSoundSource
that is returned is autoreleased, that
means if you want to use it outside
the scope of the current method you
must retain it. If you retain a
CDSoundSource you should release it
when you are finished using it.
are you using simulator to run the leak tools ?
i encounter the same leak in simulator but not on device.
try using device to run the leak tools
Related
I'm using Xcode with Cocos2d version 3.0.
I want to drag sprites around the screen. I've done so successfully using the following code:
(void) touchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInNode:self];
sprite1.position=touchLocation;
sprite2.position=touchLocation;
sprite3.position=touchLocation;
sprite4.position=touchLocation;
}
However, sometimes the sprites stop moving after a second. It's not a lag, because they never catch back up with my movement. They just stop! If I let go and start moving my touch again, the sprites start moving fine again / sometimes do the 'freeze thing' again.
Is it a memory issue?
Ok, I'm sure it must be memory. I copied this code onto a simple game with hardly any sprites and it worked perfectly.
Ok I've got it!
I had to unEnable the UISwipeGestureRecognizers while I moved the sprite.
The game was registering my touchesMoved movement as a swipe, and cancelling the touchesMoved commands.
Cocos2d-iphone 1.0.1.
My main game scene requires the player to have a finger pressing the screen for the character to move.
While the character moves (thus the finger pressing the screen), a battle might occur. Such battle triggers a new CCScene which is pushed into the CCDirector (so the main game scene still exists within memory).
During the battle, naturally, the player will probably release his finger at some point.
When the battle is over, this CCScene is popped. Thus, the main game scene returns.
Now here is a problem: the main game scene still thinks that the finger that was initially pressing the screen before the battle is still pressing it at the same point, thus the joystick is still "functioning" and the player is still moving despite the fact that the finger is no longer pressing the screen.
Is there a way to "reset" the screen touches? I'd like to do so in the onEnter method of the main game scene. I tried something like
self.isTouchEnabled = NO;
self.isTouchEnabled = YES;
Hoping that such would re-register the touch dispatcher and thus causing some kind of reset. It did not work.
Basically, I need a way to tell the main game scene "dude, no one's touching the screen despite whatever you think".
Edit
Note that, if you press the screen again, it is fixed.
To make sure I understand the problem:
After you pop the battle scene, the character continues moving around as if the user never released his finger from the joystick?
My initial thought is that you have a deeper underlying design flaw somewhere in the code. It is my understanding that you want to use at least one layer to handle input. Try shifting everything in that 'game' CCScene to a 'game' CCLayer and add the layer as a child of the scene. Then register the layer to receive touch input.
If that doesn't work, you could always use a flag. Compare the scene you are in to the scene you want to be in, and if not, don't activate the joystick controller. Kind of hack-y but it works.
I've run into this kind of problem myself.
How i've fixed it:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches){
[touchesOnScreen addObject:touch];
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches){
[touchesOnScreen removeObject:touch];
}}
- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[touchesOnScreen removeAllObjects];
}
So basically iOS keeps a pointer to every touch on the screen. What you do is manage those touches yourself with an NSSet. When you push the scene just set every UITouch from the set to nil. That is equivalent to UITouchPhaseCancelled. This will cancel all your touches on the screen. If the user still has his finger on the screen that touch wont be registered anymore...even if he moves it or something.
Note : you need to remove all objects in ccTouchesCancelled (it's called when the user has 6 or more touches on the screen) or else when he touches again the app crashes. If your app needs more than 5 touches you need to find a way around that.
Possibly the simplest solution is to have your CCDirector, on completion of popping your battle scene, call first resignFirstResponder then 'becomeFirstResponder` on the offending view; off the top of my head anyway it seems that ought to forward on the touches to another responder, hopefully letting it fizzle out when no one else handles it.
A more complicated dive into the UIView methods provides this private call cancelTouchTracking. Most likely it does something similar to what #skytz has put together. Warning: That last will mostly likely get you rejected in the app store process, but for your info there it is.
I'd opt for the simpler solution and hope it works. O_o
I'm handling the touches manualy through a class that holds every touchable sprite. Also, every menu is a CCLayer, and when I change from one menu to the other, I use the CCTransition### and get the new scene.
So, the layers are "still there" and when I touch one menu, the previous layer also receive it... like when I start the game in a new CCLayer, and then the menus are still affected for the multi touch...
If I use single touch, I can swallow the touches, but the game must have multi touch...
What can I do? Thank you all!
You need to consume the touch event in whatever layer needs it. After a layer consumes it, the touch will no longer be propagated to "previous" layers.
http://www.cocos2d-iphone.org/api-ref/0.9.0-beta/_c_c_touch_delegate_protocol_8h_source.html
To consume a touch event, you return YES in the ccTouchBegan delegate method.
EDIT: changed method name to match CCStandardTouchDelegate
- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
if (condition)
return YES;
else
return NO;
}
You can set up the priority to 0 for the top layer and priority to 1 for the layer under it like this.
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
You could try to use the property
self.isTouchEnabled = NO;
within the layer, you don't want to receive touches.
If you you use CCButton objects you need to disable them.
I hope it helps...
Greetings Anselm
I currently have a dial that rotates on touch. I would like to include a "click" sound effect when the dial is moving. I have the click sound working, but it is a bit chaotic.
I currently put this in my touches moved:
[[SimpleAudioEngine sharedEngine] playEffect:#"ButtonClick.m4a"];
But when I rotate the dial the sound effects goes crazy (i.e. CLICKCLICKLICKCLICKCLICK...) when i would like to to do this: Click..Click..Click..Click..Click..
Instead of the "click" sound restarting at each movement of the dial, how can I achieve a "click" sound that finishes the full sound file and starts over each time (i.e. loop)?
Most game developers should have encountered the "low framerate" issue at least once when developing games. But I, on the other hand, am making a sudoku-like puzzle game where low framerate is not a problem since it does not have constantly moving sprites/elements, and in fact I plan to decrease the framerate so that the game will take less CPU time and hence reduce the power consumption of the iDevices; all this just as a courtesy to the players :)
I already know that I can control the framerate in Cocos2d-iphone by modifying animationInterval:
[[CCDirector sharedDirector] setAnimationInterval:1.0/60];
But I'm having troubles on the strategy on when to lower the framerate and when to revert to the normal framerate (60Hz). At this point I only managed to define the strategy for touch event, that is:
Start with lower framerate. On ccTouchBegan, revert to normal framerate. On ccTouchEnded, switch to lower framerate. Make sure multitouch is handled accordingly.
I'm left with two more conditions:
Handling CCActions on CCNodes: as long as some CCNodes have running actions, revert to normal framerate.
Particle system: as long as there exist some particle systems that are emitting particles, revert to normal framerate.
Basically I need to be able to detect if there are actions that are still running on any sprites/layers/scene, also if some particle systems are still emitting particles. I prefer to not having the checking done on individual objects, and I'd rather have a simple [SomeClass isActionRunning]. I imagine that I might be able to do this by checking the list of "scheduled" object but I'm not sure how. I'd appreciate if you could also suggest some other conditions where I need to revert to normal framerate.
Hmm.. I would recommend that you set it to 30fps.. Yes.. Its the rate that screen refreshes.. But the most important is how you code the game.. It must be efficient.. Rather than running extra processes checking if something is running or not.. It may eat up slightly more processing power..
though i know it's not a very clean way but you can hack CCScheduler class and check if there are any object in scheduledMethods. and i guess you have to check if the objects there are yours since cocos2d itself schedule some classes.
This might be what you are looking for, at least regarding actions.
When you run an action you can call a block at the end of your action, in which you reset the frame rate.
[[CCDirector sharedDirector] setAnimationInterval:1.0/60];//set your fast frame rate
[self runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:0.5f position:ccp(100,100)], //Do whatever action it is you want to do
[CCCallBlock actionWithBlock:^
{
[[CCDirector sharedDirector] setAnimationInterval:1.0/30]; //Revert to slow frame rate... could also check within the block for other actions
}],
nil]];