SpriteKit对同一类型的多个对象进行碰撞检测

时间:2014-10-26 19:25:23

标签: ios sprite-kit

我目前正在与朋友一起使用Objective-C和SpriteKit进行游戏。我的朋友和我都是SpriteKit的新手,所以我们正在开展的这个项目更多的是学习经验。但当然,我们遇到了一些麻烦。

游戏相当简单:有一个球在屏幕周围飞行,并从iPhone屏幕的边缘和边缘反弹。用户控制屏幕底部的桨叶以使球向上偏转以防止球到达屏幕的底部。每次球击中球拍时,屏幕顶部的得分计数器递增1。

这是我们正在努力实现的目标。当游戏得分等于5时,我们会在场景中添加一个新球。新球开始自动移动,但每当我们试图检测新球是否已经击中屏幕边缘时,都没有检测到碰撞。

这是我们返回球对象的方法:

+(id)ball
{
    // the ball is a random image from google
    Ball *ball = [Ball spriteNodeWithImageNamed:@"ball"];

    // set the position of the ball
    ball.position = CGPointMake(0, 80);

    // set ball name property
    ball.name = @"ball";

    // give the ball a physics body
    ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ball.size];

    ball.physicsBody.affectedByGravity = NO;

    return ball;
}

调用此方法可以在场景中移动球:

-(void)move:(int)deltaX withDeltaY:(int)deltaY
{
    SKAction *testMoveRight = [SKAction moveByX:deltaX y:deltaY duration:0.03];

    // this will repeat the action over and over
    SKAction *move = [SKAction repeatActionForever:testMoveRight];
    [self runAction:move];
}

这些是我们的碰撞检测类别:

// this category is for the original ball in the game
static const uint32_t ballCategory = 0x1 << 0;
// this category is for the second ball that gets added to the game
static const uint32_t ball2Category = 0x1 << 1;  
static const uint32_t paddleCategory = 0x1 << 2;
static const uint32_t topBarrierCategory = 0x1 << 3;
static const uint32_t leftBarrierCategory = 0x1 << 4;
static const uint32_t rightBarrierCategory = 0x1 << 5;
static const uint32_t gameOverBarrierCategory = 0x1 << 6;

这是我们的didBeginContact方法:

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

    if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }
    else {
        firstBody = contact.bodyB;
        secondBody = contact.bodyA;
    }


    // first ball contact
    if ((firstBody.categoryBitMask & ballCategory) != 0 && (secondBody.categoryBitMask & rightBarrierCategory) != 0) {
        [ball move:-15 withDeltaY:0];
    }

    else if ((firstBody.categoryBitMask & ballCategory) != 0 && (secondBody.categoryBitMask & topBarrierCategory) != 0) {
        [ball move:0 withDeltaY:-20];
    }

    else if ((firstBody.categoryBitMask & ballCategory) != 0 && (secondBody.categoryBitMask & leftBarrierCategory) != 0) {
        [ball move:15 withDeltaY:0];
    }

    else if ((firstBody.categoryBitMask & ballCategory) != 0 && (secondBody.categoryBitMask & paddleCategory) != 0) {
        [ball move:0 withDeltaY:20];

        // increment score
        self.score++; 
        // update the score label
        self.deathLabel.text = [NSString stringWithFormat:@"%i", self.score];

        // add a new ball to the scene if the score is 5
        if (score == 5) {
            [scene addChild:ball2];
            [ball2 move:5 withDeltaY:10];
        }
    }

    // second ball contact detection
    if ((firstBody.categoryBitMask & ball2Category) != 0 && (secondBody.categoryBitMask & rightBarrierCategory) != 0) {
        [ball2 move:-15 withDeltaY:0];
    }
}

为什么我们添加到场景中的第二个球没有检测到它是否与右侧屏障(屏幕右边缘)发生碰撞?当它到达屏幕的右边缘时,它不会像我们想要的那样反弹并向左移动。有什么建议?

1 个答案:

答案 0 :(得分:1)

简单地将physicsBody添加到球中是不够的,还必须设置类别,接触和碰撞位掩码。首先,我会将球的物理体改为

ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.frame.size.width/2];

循环物理机构首先效率更高,第二更好。除此之外,每个屏障都不需要单独的类别。将它们合二为一:

self.size = self.view.bounds.size;

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, 0, self.frame.size.height);
CGPathAddLineToPoint(path, NULL, self.frame.size.width, self.frame.size.height);
CGPathAddLineToPoint(path , NULL, self.frame.size.width, 0);
CGPathAddLineToPoint(path, NULL, 0, 0);
SKShapeNode *bounds = [SKShapeNode shapeNodeWithPath:path];
bounds.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromPath:path];
CGPathRelease(path);
bounds.physicsBody.categoryBitMask = boundsCategory;

正如你所看到的,每个SpriteNode都有一个categoryBitmask,它有一个碰撞位掩码(这意味着它与这些类别反弹/冲突)和一个联系位掩码(我没有在这里设置一个,但是当这种联系发生时,调用didBeginContact方法)。在您的代码中,不会调用didBeginContact方法,但您也不需要它来进行弹跳。为了澄清,弹跳将自行发生,这意味着你不需要任何接触方法或位掩码,只需对球施加一个力/冲动,当它触及边界时它就会反弹。

至于球,你不需要每个球的另一个类别 - 这是冗余。对于创建的每个球,只需将categoryBitmask设置为相同的东西:

ball.physicsBody.categoryBitMask = ballCategory;
ball.physicsBody.collisionBitMask = boundsCategory;

通过添加这些行,您告诉编译器每个球都是'ballCategory'并且它应该与任何'boundsCategory'对象发生冲突。希望这有帮助,祝你好运!