In a SpriteKit app, I am playing a click sound when the user moves a block on the screen, but this causes terrible lagging (fps dropping to near zero).
Code:
• self.audioPlayers
is a strong NSMutableArray which holds currently playing AVAudioPlayer
s and removes them when the audioPlayerDidFinishPlaying:
delegate method is called.
• The blockShouldMove
method compares the touch location to its previous location and only returns YES if the user has moved the cursor enough distance so we only play a maximum of around 8 sounds simultaneously.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *oneTouch = [touches anyObject];
CGPoint location = [oneTouch locationInNode:self];
if (![self blockShouldMove:location]) {
return;
}
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"click_01.mp3" ofType:nil];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:soundFilePath] error:nil];
[audioPlayer prepareToPlay];
audioPlayer.volume = 1.0;
audioPlayer.numberOfLoops = 0;
audioPlayer.delegate = self;
[audioPlayer play];
[self.audioPlayers addObject:audioPlayer];
}
On both simulators and real devices (iOS and tvOS), if I make circles with my finger, the fps drops to almost nothing until well after I have even released my finger.
If I remove the whole AVAudioPlayer
and use [SKAction playSoundFileNamed:@"click_01.mp3" waitForCompletion:NO]
, everything works fine. But unfortunately, SKAction
sound handling is terrible for my purpose because the volume cannot be set.
Is there anything that can be done to make this better?
This is an indirect sort of "answer", more a comment but it's too long to write it in that form.
I ran into something like this issue with a SpriteKit game. I solved it by dropping the AVAudioPlayer
s and instead using the scene's audio engine. I connected a bunch of AVAudioPlayerNode
s to the main mixer node and started them playing (nothing). Whenever I'd want a sound effect of some sort, I'd grab the next audio player node (round robin fashion, and there were enough of them so that I was sure it would be idle) and schedule a preloaded sound buffer for it.
The point is that for more complicated audio in SpriteKit, you may need to go through the audio engine and build a sound graph that's appropriate for what you want to do. Multiple AVAudioPlayer
s existing at once seem to be stepping on each other somehow and causing lag.
However if you didn't try the SKAudioNode
functionality yet, I'd say do that first. You should be able to run an action on the audio node to adjust volume. I didn't go that route since I couldn't get the stereo panning to work the way I wanted with that setup. If that works for what you want to do, it's probably simpler than setting up your own sound graph.
If you want to see the solution I eventually used, you can look at https://github.com/bg2b/RockRats/blob/master/Asteroids/Sounds.swift