使用NSMutable对象数组进行碰撞检测

时间:2015-06-11 15:57:06

标签: sprite-kit nsmutablearray collision-detection

我尝试使用在屏幕左右两侧墙壁之间反弹的物体制作游戏,直到它们到达底部。

我在这些方法中创建我的对象,这些对象将对象存储在NSMutable数组中,并在游戏开始时调用。

-(void)createContent
{
    self.backgroundColor = [SKColor colorWithRed:0.54 green:0.7853 blue:1.0 alpha:1.0];

    world = [SKNode node];
    [self addChild:world];

    crabs = [NSMutableArray new];
    [self performSelector:@selector(createCrabs) withObject:nil afterDelay:1];
 }

-(void)createCrabs {
     crab = [HHCrab crab];
    [crabs addObject:crab];
    [world addChild:crab];
    crab.position = CGPointMake(world.scene.frame.size.width/12 , world.scene.frame.size.height/3.2);
    [crab moveLeft];

    //Next Spawn
    [self runAction:[SKAction sequence:@[
            [SKAction waitForDuration:10],
            [SKAction performSelector:@selector(createCrabs) onTarget:self],
    ]]];
}  

这将无休止地创建新对象,但问题始于碰撞检测。最初我的碰撞检测设置如下:

-(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;
    }

    if (firstBody.categoryBitMask==crabCategory && secondBody.categoryBitMask == leftWallCategory) {
        NSLog(@"Crab Hit Left Wall");
        [crab stop];
        [crab moveRight];
    } else if (firstBody.categoryBitMask == crabCategory && secondBody.categoryBitMask == rightWallCategory) {
        NSLog(@"Crab Hit Right Wall");
        [crab stop];
        [crab moveLeft];
    }
}

但是,在地图上有多个物体后,当原始物体与墙壁碰撞时,它会开始出现故障并停止移动。这会导致堆积,从而产生新的对象。 我还尝试使用更新的CurrentTime方法来查看碰撞检测是否会改善,但是如预测的那样,一次只有一个对象会移动,而其余对象将保持静止。

-(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;
    }

    if (firstBody.categoryBitMask==crabCategory && secondBody.categoryBitMask == leftWallCategory) {
        NSLog(@"Crab Hit Left Wall");
        self.crabTouchLeftWall = YES;
    } else if (firstBody.categoryBitMask == crabCategory && secondBody.categoryBitMask == rightWallCategory) {
        NSLog(@"Crab Hit Right Wall");
        self.crabTouchRightWall = YES;
    }
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
    for (HHCrab *crabNode in crabs){
        if (self.crabTouchLeftWall){
           [crabNode stop];
            [crabNode moveRight];
            self.crabTouchLeftWall = NO;
        }
        if (self.crabTouchRightWall){
            [crabNode stop];
            [crabNode moveLeft];
            self.crabTouchRightWall = NO;
        }
    }
}

如何解决这个问题,以便当一个物体与墙壁碰撞时,它不会影响其他物体的移动,只会影响它自身?

2 个答案:

答案 0 :(得分:0)

我有几点建议。

当你创建多​​个螃蟹实例并将它们添加到螃蟹数组中时,我建议你给每个螃蟹一个独特的名称属性。你可以通过运行int计数器来做到这一点。例如:

@implementation GameScene {
    int nextObjectID;
}

然后当你创建一个蟹实例时:

nextObjectID++;
[crab setName:[NSString stringWithFormat:@"crab-%i",nextObjectID]];

我个人更喜欢这样编码我的didBeginContact:

- (void)didBeginContact:(SKPhysicsContact *)contact {
    uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);

    if (collision == (crabCategory | leftWallCategory)) {
        // figure out which crab in the array made the contact...
        for(HHCrab *object in crabs) {
            if(([object.name isEqualToString:contact.bodyB.node.name]) || ([object.name isEqualToString:contact.bodyA.node.name])) {
                NSLog("I am %@",object.name);
            }
        }
    }
}

您的代码self.crabTouchLeftWall = YES;不是正确的方法。您应该创建一个crab类属性并将其设置为YES。原因是每个蟹实例都需要具有这些属性。您目前拥有它的方式,所有蟹实例都共享相同的BOOL self.crabTouchLeftWallself.crabTouchRightWall

您正在检查更新方法中的BOOL值联系人,然后运行[crabNode stop];。当联系人在didBeginContact方法中注册时,您可以直接执行此操作。

我之前建议你为每个crab实例使用唯一名称的原因是因为当需要从数组中删除它们时,你需要能够指出需要删除哪个特定的crab实例。拥有一个独特的名称只会让事情变得更容易。

答案 1 :(得分:0)

请注意,像这样手动移动节点会产生奇怪的结果。在Spritekit中使用物理引擎时,你必须让它自己进行计算,这意味着你必须使用force来移动对象。这不适用于接触检测,如果您只需要接触检测,则可以移动节点。但如果您需要的不仅仅是接触检测,例如。为了模拟碰撞并限制节点相互穿透,我建议你适当地使用力(applyImpulse:applyForce:是合适的方法)。

您可以通过将代码从update:method移动到didSimulatePhysics方法来解决您的问题,但这不是解决方案......因为即使您在模拟完成后手动移动所有内容,您也可以错误,例如通过将节点定位为彼此重叠来手动启动冲突。因此,建议是,如果您正在使用phyisics,请使用它,或者根本不使用它并手动或使用动作移动节点。如果您决定不使用物理引擎,则可以使用类似方法处理冲突CGRectIntersectsRect和类似的。

再次指出,这与您的问题无关,但它可能很有用:即使您移动节点,也可以使用物理引擎进行接触检测,例如通过SKAction方法。阅读更多here