【问题标题】:Collision Detection with an NSMutable array of Objects使用 NSMutable 对象数组进行碰撞检测
【发布时间】:2015-08-27 10:36:44
【问题描述】:

我正在尝试制作一个游戏,其中的对象在屏幕的左右壁之间弹跳直到它们到达底部。

我在这些方法中创建对象,这些方法将对象存储在 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];
    }
}

但是,在地图上出现多个对象后,当原始对象与墙壁碰撞时,它开始出现故障并停止移动。这会导致生成新对象的错误堆积。 我还尝试使用 update 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;
        }
    }
}

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

【问题讨论】:

  • 尝试使用 SKSpriteNodes 数组
  • WraithSeeker,我相信这就是我已经在做的事情。
  • @HansHartle 你是靠力移动螃蟹还是手动设置它们的位置属性?
  • @Whirlwind 我将它们向左/向右递增并永远运行该动作,当它们撞到墙上时,我删除动作并翻转方向。我确实使用重力来移动螃蟹,将它们拉到屏幕上。

标签: sprite-kit nsmutablearray collision-detection


【解决方案1】:

我有几个建议。

当您创建多个螃蟹实例并将它们添加到螃蟹数组中时,我建议您给每个螃蟹一个唯一的名称属性。您可以通过运行 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; 不是正确的方法。您应该创建一个螃蟹类属性并将其设置为 YES。原因是每个螃蟹实例都需要具有这些属性。按照您目前的方式,所有螃蟹实例都共享相同的 BOOL self.crabTouchLeftWallself.crabTouchRightWall

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

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

【讨论】:

  • 我已经添加了所有三个建议(顺便说一下,我真的很喜欢你做 didBeginContact 的方式。非常紧凑,而且更简单。)但仍然以相同的结果结束。我假设是因为我没有使用唯一名称属性来移动它们,但我不知道该怎么做。
  • 解决了问题!我感谢您的所有帮助! :)
  • @HansHartle - 很高兴为您提供帮助。一旦不再需要它,请记住从螃蟹数组中删除螃蟹对象。这是正确的内存管理,有助于提高游戏的 FPS。如果您对此感到困惑,请告诉我。
  • 在此之前的上一个问题中(请参阅link),一旦螃蟹到达屏幕底部,我在重置螃蟹的位置时遇到了问题。我们通过添加布尔值解决了这个问题,但是我在使用唯一名称来重置对象的位置时遇到了问题。我应该创建一个新问题吗?
  • @HansHartle - 可能最好这样做。否则这个问题会让其他可能有相同初始问题的读者感到困惑。
【解决方案2】:

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

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

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

【讨论】:

  • 我一直在我的游戏的某些方面使用 applyImpulse。但我会将所有内容都切换到它,看看它是否能让游戏愉快:)。但是,我仍然会遇到无法确定向哪只螃蟹施加力的问题。我为每只螃蟹创建了一个唯一的名称属性,但不知道如何判断是哪只螃蟹导致了碰撞以及如何专门调用该螃蟹的方法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多