SpriteKit声音消耗问题

时间:2013-11-27 21:51:47

标签: ios objective-c macos sprite-kit

为了进一步熟悉SpriteKit,我制作了一个简单的小应用程序,可以在你点击或触摸的任何位置(在7秒后自动消失)产生随机(3个选项)球,然后应用物理和重力,并以设定的间隔操纵重力完成。这很简单,工作得很好。但是,我决定给它添加声音,这样当撞击墙壁或彼此时,球会产生噪音。

工作时,它提出了一个我自己无法解决的问题:如果你产生了无法处理它的那么多球,那么无声版将会滞后,但声音版本继续滞后。我检查了Xcode可靠的Debug Navigator,发现应用程序的内存继续扩展,每个球都添加到场景中。无声的版本并没有在内存上扩展得那么多,并且在将球从父母身上移除之后还收回了一些内容。

我不禁想到我的实现中出现了一些错误,它不仅会在声音文件被使用后保留在内存中,还会保留每个文件的倍数。

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

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

@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秒后球会自动消失,我会假设有任何孩子(我还假设应该包括动作,例如声音动作)。

我甚至做了一个方法来确认没有节点在它们消失后没有粘附:

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

然后将结果复制到剪贴板,从而生成场景中所有节点和子节点的列表。 (“(())(())”在那里是为了在find / replace中搜索一个独特的模式来制作新行。

编辑:我也注意到在所有事情都已经消亡之后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];
}

联系人监听器几乎完全相同(我删除了球的转换并将操作应用于世界节点)。我也尝试使用字典而不是数组,它工作正常。

方法不适用于某些原因

我会删除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

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

1 个答案:

答案 0 :(得分:1)

我看到了几个问题....通过宣布SKAction ballSound01,它会坚持下去再次使用。此外,我按照你的想法,通过删除每个节点,它将删除每一个动作,但我再次认为,通过声明它,这将不会发生。此外,SKActions可以应用于多个节点,因此我认为您不能真正将它们视为所运行对象的子项。

我认为你最好声明一个固定在那些SKActions上的数组,而不是每次在soundBankRandomizer中创建它。

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

 if (isWallHit == YES) {

 } else{

 }

更重要的是因为默认块永远不会运行。因为isWallHit是YES或NO,所以这两个条件就是它,不需要默认值。

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

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

你可以打电话给......

[[SoundManager sharedSounds] playRandomWallSound];

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