我有一个使用SpriteKit制作的基于物理的2D游戏。 越来越多的小球与许多块碰撞。 每当球碰到一个障碍物时,我都想发出声音。
目前,我使用playSoundFileNamed(_:waitForCompletion:)
并将其waitForCompletion
参数始终设置为false来播放声音(这样它可以快速播放多种声音)。
这在性能和帧速率方面非常有效,但是我收到越来越多的关于失败的重新分配的崩溃报告。
仅当在游戏本身从不取消分配GameScene时,才会发生这种情况。
我尝试用SKAudioNode
代替SKAction,但是不能连续快速播放多种声音,因此游戏感觉不对。
我尝试加入GameAudioPlayer,但是在播放大量声音时帧频会大大降低。
这是我现在播放声音的方式:
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)
}
}
这是来自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
答案 0 :(得分:1)
强烈建议您在此处阅读我的答案:https://stackoverflow.com/a/56615200/5277976。
我的最终结论是对所有内容都使用AVAudioPlayer:音乐和声音。创建AVAudioPlayer实例,使每种声音的最大数量最适合您的情况。此处给出一个示例:https://www.oreilly.com/library/view/ios-swift-game/9781491920794/ch04.html。
请注意,O'Reilly所做的不完全是我所说的或最终要做的:在他们的情况下,它们允许您生成更多的AVAudioPlayers,但鉴于我们将有多少重叠的声音,我发现这对性能产生了很大影响一次-如果您有很多相同类型的重叠声音,在某个时候就应该调用它,但是我让您自己决定。
在我们的案例中,如果我们有经常播放且重叠的声音,我们将保留几个播放器(但不是无限播放器,例如在O'Reilly的例子中)。请注意,重叠播放的声音越多,演奏的效果就越差。据我所知,这是没有办法的。保持尽可能多的声音给1个播放器。为此,我们有一个单独的枚举。
PlaySoundFileNamed重量更轻,性能似乎更好;不幸的是,它实际上已经损坏了–在IAP购买过程中它使我们的应用程序崩溃了,那是不对的。考虑到它已经损坏了多长时间,我怀疑它是否会修复。 SKAudioNode具有您所描述的局限性,我将方钉插入圆孔的尝试由于各种原因而失败。
正如我在那儿解释的那样,拥有许多AVAudioPlayer实例并不是理想的解决方案,但是当涉及到它时,这是最坏的情况中最好的。
我还要指出,像这样的问题使我们完全脱离了SpriteKit,现在我们使用Unity。