ios - SpriteKit声音内存消耗问题

标签 ios objective-c macos sprite-kit

为了进一步熟悉 SpriteKit,我制作了一个简单的小应用程序,它会在您单击或触摸的任何位置生成一个随机(3 个选择)球(7 秒后自动消失),然后应用物理和重力,并以设定的时间间隔操纵重力来完成。它很简单,而且效果很好。然而,我决定添加声音,这样球在撞击墙壁或彼此撞击时就会发出声音。

虽然它有效,但它带来了一个我自己无法解决的问题:如果你产生了太多它无法处理的球,无声版本就会滞后,但是声音此后版本继续滞后。我检查了 Xcode 值得信赖的调试导航器,发现应用程序的内存随着添加到场景中的每个球而持续扩展。无声版本的内存扩展没有那么大,而且在球从 parent 身边拿走后还回收了一些位。

我忍不住认为我的实现中存在一些问题,不仅在使用声音文件后将其保留在内存中,而且还保留每个文件的多个文件。

我很乐意与任何有请求的人分享我的项目,但这是所发生的方法的近似值:

初始化,声音 Action 被创建并存储在场景类中,以便稍后可以访问:

@interface REP_Balls () {
    SKAction* ballSound01;
}
@end

@implementation REP_Balls {
...
...
...
-(void) setUpSounds {
    NSArray* array = @[ @"ball_hit_01.wav",]; //I have more in the array, I'm just simplifying code for stackoverflow

    ballSound01 = [SKAction playSoundFileNamed:[array objectAtIndex:0] waitForCompletion:NO];

}

然后,我让接触监听器激活一种方法,该方法从可用的声音操作中随机化并返回其中之一:

-(void) didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody, *secondBody;

firstBody = contact.bodyA;
secondBody = contact.bodyB;

SKAction* randomSound;
//blah blah blah if categories match and whatnot, do this:
        REP_BallSpawn* ball = (REP_BallSpawn*) firstBody.node;
        randomSound = [self soundBankRandomizer:YES];

        [ball runAction:randomSound];

以及随机化的方法:

-(SKAction*) soundBankRandomizer:(BOOL)isWallHit {
NSArray* array;
switch (isWallHit) {
    case YES:
        array = @[ //according sound actions
              ];
        break;

    case NO:
        array = @[
                ballSound01, //and other sound actions
                  ];

    default:
        array = @[
                  //if neither case is true somehow, just choose from a bank of all sound actions
                  ];
        break;
}

int randomChoice = arc4random() % [array count];    
SKAction* sound = (SKAction*)[array objectAtIndex:randomChoice];
return sound;
}

除此之外,7 秒后,球会自动消失,我会假设有任何 child 与它们在一起(我也假设应该包括 Action ,例如声音 Action )。

我什至制定了一种方法来确认没有节点在消失后仍然存在:

[self enumerateChildNodesWithName:@"//*" usingBlock:^(SKNode *node, BOOL *stop) {
        string = [NSString stringWithFormat:@"%@  (())(())  %@", string, node];
    }];

然后将结果复制到剪贴板,从而生成当前场景中所有节点和子节点的列表。 (“(())(()) ”在那里用于在查找/替换中搜索独特的模式以创建新行)

编辑:我还注意到,在所有内容都消失后,CPU 使用率仍然很高。

请告诉我我做错了什么!

================================================== ====

所以我对我的代码做了一些更改。如下所述,我让它正常工作,但经过一些进一步的测试,它的具体实现方式似乎很挑剔。也许其他人会明白为什么,但这对我来说毫无意义。

有效的方法

与上面相同,但有以下更改:

-(void) setUpSounds {

NSArray* array = @[
                  @"ball_hit_01.m4a",
                  @"ball_hit_02.m4a",
                  @"ball_hit_03.m4a",
                  @"ball_hit_04.m4a",
                  @"wall_hit_01.m4a",
                  @"wall_hit_02.m4a",
                  @"floor_hit_01.m4a",
                  @"floor_hit_02.m4a",
                  ];


SKAction* ballSound01 = [SKAction playSoundFileNamed:[array objectAtIndex:0] waitForCompletion:NO];
    // etc


sounds = [NSArray arrayWithObjects:ballSound01, ballSound02, ballSound03, ballSound04, wallSound01, wallSound02, floorSound01, floorSound02, nil]; //is declared class wide

}



 -(SKAction*) soundBankRandomizer:(BOOL)isWallHit {

int randomChoice = arc4random() % ([sounds count] / 2);

if (isWallHit) {
    randomChoice += ([sounds count] / 2);
}

return [sounds objectAtIndex:randomChoice];
}

接触监听器几乎是相同的(我删除了球的类型转换并将 Action 应用到世界节点)。我也尝试过使用字典而不是数组,效果很好。

由于某种原因不起作用的方法

我将删除 soundBankRandomizer 方法,并从联系人监听器中删除任何操作调用。相反,我添加了这个方法:

-(void)playSound:(BOOL)isWallHit {

int randomChoice = arc4random() % ([sounds count] / 2);

if (isWallHit) {
    randomChoice += ([sounds count] / 2);
}

SKAction* randomSound = [sounds objectAtIndex:randomChoice];

[bgNull runAction:randomSound];

}

然后通过以下方式从联系人监听器调用它:

[self playSound:YES]; //for wall hit
[self playSound:NO]; //for ball on ball action

出于某种原因,这导致了与我最初遇到的完全相同的性能问题。这对任何人都有意义吗?!

最佳答案

我看到了几个问题......通过声明 SKAction ballSound01 它将保留下来并再次使用。我也遵循你的想法,通过删除每个节点,它会删除每个操作,但我再次认为通过声明它,这不会发生。此外,SKActions 可以应用于多个节点,因此我认为您不能真正将它们视为它们运行的​​对象的子节点。

我认为你最好声明一个保存这些 SKAction 的数组,而不是每次在 soundBankRandomizer 中创建它。

此外,我从未见过带有这样的 bool 变量的 switch 语句。只是这样做...

 if (isWallHit == YES) {

 } else{

 }

更重要的是因为默认 block 无论如何都不会运行。由于 isWallHit 是 YES 或 NO,这两个条件就是它,不需要默认值。

您可能需要考虑创建一个 Singleton 类作为声音管理器,并将其与节点完全分开。所以不要打电话...

randomSound = [self soundBankRandomizer:YES];  
[ball runAction:randomSound];

你可以这样称呼...

[[SoundManager sharedSounds] playRandomWallSound];

无需将特定节点作为播放声音的一部分。

关于ios - SpriteKit声音内存消耗问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20253792/

相关文章:

ios - PayUMoney IOS SDK 中未找到 CitrusGraphics 框架

objective-c - iOS:简单的动画

python - Mac上的python安装可以强制使用tcl/tk 8.6吗?

html - 如何在 iOS 中删除字符串中的评论 HTML 元素

使用 ios 9 beta 的 iOS 企业发行版并带有错误消息

iOS UItableviewCell 清除旧的单元格值

IOS 9 View 在底部被截断

ios - enumerateAssetsWithOptions 返回一个 alasset nil

iphone - 如何从 iPhone 查看日志?

ios - 我可以在 Mac OSX 应用程序中使用 UIDocument 吗?