I have a physics-based 2D game made with SpriteKit. There are an increasing number of little balls colliding with a number of blocks. Each time a ball hits a block I want to play a sound.
Currently I play sounds using playSoundFileNamed(_:waitForCompletion:)
with its waitForCompletion
parameter always set to false (so that it can play multiple sounds in quick sequence).
This works very well in terms of performance and frame-rate, but I'm getting an increasing number of crash reports regarding a failing deallocation.
This only happens when the GameScene is deallocated, never during the game itself.
I tried to substitute the SKAction with a SKAudioNode
but that is not able to play multiple sounds in rapid succession, so the game does not feel right.
I tried to incorporate GameAudioPlayer but the frame rate drops significantly when playing a high number of sounds.
This is currently how I play sounds:
class GameScene: SKScene {
private var popSound = SKAction.playSoundFileNamed("Pop.wav", waitForCompletion: false)
private var starSound = SKAction.playSoundFileNamed("Star.wav", waitForCompletion: false)
private var whooshSound = SKAction.playSoundFileNamed("Whoosh.wav", waitForCompletion: false)
// ...
func didEnd(_ contact: SKPhysicsContact) {
// ball-to-block collision
self.run(popSound)
}
}
And this is the stack trace coming from Crashlytics
Fatal Exception: NSRangeException
0 CoreFoundation 0x1ec81527c __exceptionPreprocess
1 libobjc.A.dylib 0x1eb9ef9f8 objc_exception_throw
2 CoreFoundation 0x1ec78ece8 _CFArgv
3 CoreFoundation 0x1ec700298 -[__NSArrayM removeObjectsInRange:]
4 SpriteKit 0x20354ee3c -[SKSoundSource purgeCompletedBuffers]
5 SpriteKit 0x20354f0d4 -[SKSoundSource dealloc]
6 SpriteKit 0x203523b14 -[SKPlaySound .cxx_destruct]
7 libobjc.A.dylib 0x1eb9ee7cc object_cxxDestructFromClass(objc_object*, objc_class*)
8 libobjc.A.dylib 0x1eb9fe6b8 objc_destructInstance
9 libobjc.A.dylib 0x1eb9fe720 object_dispose
10 SpriteKit 0x203485c08 -[SKAction dealloc]
11 SpriteShot 0x1005f8418 @objc GameScene.__ivar_destroyer (GameScene.swift)
12 libobjc.A.dylib 0x1eb9ee7cc object_cxxDestructFromClass(objc_object*, objc_class*)
13 libobjc.A.dylib 0x1eb9fe6b8 objc_destructInstance
14 libobjc.A.dylib 0x1eb9fe720 object_dispose
15 UIKitCore 0x219145b28 -[UIResponder dealloc]
16 SpriteKit 0x203504be4 -[SKNode dealloc]
17 SpriteKit 0x2034bade0 -[SKScene dealloc]
18 SpriteShot 0x1005f83d4 @objc GameScene.__deallocating_deinit (<compiler-generated>)
19 SpriteKit 0x2034e2004 -[SKView _update:]
20 SpriteKit 0x2034dd7f4 __51-[SKView _vsyncRenderForTime:preRender:postRender:]_block_invoke.351
21 SpriteKit 0x2034dcbf8 -[SKView _vsyncRenderForTime:preRender:postRender:]
22 SpriteKit 0x2034dfc54 __29-[SKView setUpRenderCallback]_block_invoke
23 SpriteKit 0x203522c18 -[SKDisplayLink _callbackForNextFrame:]
24 QuartzCore 0x1f0b9cf90 CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long)
25 QuartzCore 0x1f0c66b10 display_timer_callback(__CFMachPort*, void*, long, void*)
26 CoreFoundation 0x1ec780a8c __CFMachPortPerform
27 CoreFoundation 0x1ec7a7690 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
28 CoreFoundation 0x1ec7a6ddc __CFRunLoopDoSource1
29 CoreFoundation 0x1ec7a1c00 __CFRunLoopRun
30 CoreFoundation 0x1ec7a10b0 CFRunLoopRunSpecific
31 GraphicsServices 0x1ee9a179c GSEventRunModal
32 UIKitCore 0x21911b978 UIApplicationMain
33 SpriteShot 0x1005f1910 main + 16 (Ball.swift:16)
34 libdyld.dylib 0x1ec2668e0 start