SpriteKit(用Xcode的Objective-C编写的iOS应用程序)中多个不同集合的问题



我目前正在用Objective-C构建一个iOS应用程序。这个应用的想法是,你有某种火箭飞船在小行星带中导航。它以纵向模式播放。现在,有两种不同类型的小行星。普通的,当你撞到它们时会让你输掉,而金色的,你会为了获得硬币而射击。

对于碰撞,我使用的是 Ray Wenderlich SpriteKit 教程中的代码。类别设置如下:

static const uint32_t playerCategory     =  0x1 << 0;
static const uint32_t asteroidCategory        =  0x1 << 1;

要运行的代码是这样的:

- (void)player:(SKSpriteNode *)player didCollideWithAsteroid:(SKSpriteNode *)asteroid {
[self runAction:[SKAction playSoundFileNamed:@"Explosion.mp3" waitForCompletion:NO]];

NSLog(@"Hit");
[self.player removeFromParent];
[asteroid removeFromParent];
SKAction *actionMoveDone = [SKAction removeFromParent];
SKAction * loseAction = [SKAction runBlock:^{
    SKTransition *reveal = [SKTransition crossFadeWithDuration:0.5];
    SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];
    [self.view presentScene:gameOverScene transition: reveal];
}];
[self.asteroid runAction:[SKAction sequence:@[loseAction, actionMoveDone]]];
}

碰撞检测代码是这样的:

- (void)didBeginContact:(SKPhysicsContact *)contact
{
// 1
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
    firstBody = contact.bodyA;
    secondBody = contact.bodyB;
}
else
{
    firstBody = contact.bodyB;
    secondBody = contact.bodyA;
}
// 2
if ((firstBody.categoryBitMask & playerCategory) != 0 &&
    (secondBody.categoryBitMask & asteroidCategory) != 0)
{
    [self player:(SKSpriteNode *)firstBody.node didCollideWithAsteroid:(SKSpriteNode *)secondBody.node];
}
}

像这样,一切都很好,玩家撞上一颗小行星,检测到碰撞,播放音效,"游戏结束"场景取代当前场景。

当我尝试添加另一种小行星时,问题就开始了。对于类别,我尝试了很多东西,例如

static const uint32_t playerCategory     =  0x1 << 0;
static const uint32_t asteroidCategory        =  0x1 << 1;
static const uint32_t bulletCategory     =  0x1 << 2;
static const uint32_t goldAsteroidCategory        =  0x1 << 3;

static const uint32_t playerCategory     =  0x1 << 0;
static const uint32_t asteroidCategory        =  0x1 << 1;
static const uint32_t bulletCategory     =  0x1 << 0;
static const uint32_t goldAsteroidCategory        =  0x1 << 1;

甚至

static const uint32_t playerCategory     =  0x1 << 0;
static const uint32_t asteroidCategory        =  0x1 << 1;
static const uint32_t bulletCategory     =  1x1 << 0;
static const uint32_t goldAsteroidCategory        =  1x1 << 1;

以及我能想到的这些事情的几乎任何组合。

当我子弹击中小行星时要运行的代码如下:

- (void)bullet:(SKSpriteNode *)bullet didCollideWithGoldAsteroid:(SKSpriteNode *)goldAsteroid
{
  [self runAction:[SKAction playSoundFileNamed:@"ding.m4a" waitForCompletion:NO]];
NSLog(@"Hit");
[bullet removeFromParent];
[goldAsteroid removeFromParent];
[self plusOneCoin];
}

碰撞检测代码是相同的,但进行了一些小的编辑,将碰撞信息替换为相关信息。

出于某种原因,没有任何效果。根据我使用的类别设置代码,要么

    当我遇到一颗普通的小行星
  1. 时,我输了(正如预期的那样),如果我遇到一颗金色的小行星,什么也没发生(正如预期的那样),但是当我拍摄一颗金色小行星时,除了运行代码之外,什么都没有发生。
  2. 当我射击时,我自动输了
  3. 当我射击时,没关系,但是如果我击中任何小行星,我就输了。

我不确定发生了什么,所以任何帮助将不胜感激。如果我需要发布更多代码或细节以便你们能够提供帮助,我很乐意这样做。

您的问题来自您在 didBeginContact 方法中处理联系人的方式。有几种方法可以处理联系人。有些简单,有些更复杂。考虑这种更简单的方法:

- (void)didBeginContact:(SKPhysicsContact *)contact {
    uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
    if (collision == (playerCategory | asteroidCategory)) {
        // do something
    }
}

你可以更复杂一点。例如,您有 2 种类型的小行星,但不想为每种小行星使用唯一的类别,因为您拥有的类别数量有限。您可以通过向小行星添加名称属性来实现此目的,例如 myNode.name = @"GoodRock";myNode.name = @"BadRock"; .现在在您的联系人方法中:

- (void)didBeginContact:(SKPhysicsContact *)contact {
    uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
    if(collision == (playerCategory | asteroidCategory)) {
        if(([contact.bodyA.node.name isEqualToString:@"GoodRock"]) || ([contact.bodyB.node.name isEqualToString:@"GoodRock"])) {
            // do something
        }
        if(([contact.bodyA.node.name isEqualToString:@"BadRock"]) || ([contact.bodyB.node.name isEqualToString:@"BadRock"])) {
            // do something else
        }
    }
}

这允许您对大量不同的小行星类型仅使用一个接触类别。

另一种选择是为每个小行星分配一个唯一的名称。如果您需要确切地知道哪颗小行星被击中,您可能需要这样做。也许每隔一段时间,你就会为三相点创建一个"惊喜"的小行星,或者为每颗小行星撞击创建一个分裂动画。

在这种情况下,您需要为每个小行星分配一个唯一的名称,如下所示:

// create an int variable and +1 every time you create a new asteroid
asteroidCounter++;
[myNode setName:[NSString stringWithFormat:@"asteroid-%i", asteroidCounter]];

然后,您需要将您创建的每颗新小行星存储在一个可变数组中:

[asteroidArray addObject:myNode];

在联系人方法中,您可以像这样枚举数组:

- (void)didBeginContact:(SKPhysicsContact *)contact {
    uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
    if(collision == (playerCategory | asteroidCategory)) {
        for(SKSpriteNode *object in myArray) {
            if(([contact.bodyA.node.name isEqualToString:@"asteroid-3"]) || ([contact.bodyB.node.name isEqualToString:@"asteroid-3"])) {
                // do something to asteroid #3
            }
        }
    }
}

如果使用最后一个选项,则需要记住从数组中删除不再使用的任何节点(已销毁,屏幕外等)。如果你不这样做,它不会让你的代码崩溃,但这是一个很好的做法。特别是如果你不断向阵列添加新的小行星。

最新更新