二维iPhone游戏中基于平铺的碰撞检测问题

时间:2009-10-01 04:07:55

标签: iphone opengl-es collision-detection tiles

SETUP:
我正在为iPhone开发基于2-D平铺的游戏(鸟瞰图)。该应用程序读取tile-tile(.tbx)tilemap文件,其tile为'blocked'属性,为true或false,表示英雄是否可以在tile中移动。我遍历地图中的每个图块并创建一个表示图块行和列的二维C数组,以保存每个图块的阻止属性(true / false)。当我将英雄移动到整个棋盘时,我用阵列检查英雄的位置,看看他移动的牌是否被阻挡。如果被阻挡,英雄的位置会被提前反转。

PROB:
问题是,当英雄踩到被阻挡的瓷砖时,他无法离开它。平铺位置是正确的,因为阻挡的瓷砖被检测到应该在哪里,但英雄仍然被卡住。英雄逐像素地前进而不是“按瓦片”前进。这就是全部。剩下的就是显示代码:(英雄是28像素乘36像素大小)

//Code from GameScreen.m





-(void)generateCollisionMap
{




for(int layer=0; layer < 2; layer++) {
            for(int yy=0; yy < _screenTilesHeight; yy++) {

                NSLog(@"Row %i", yy);

                for(int xx=0; xx < _screenTilesWide; xx++) {
                    int _globalTileID = [[[tileMap layers] objectAtIndex:layer] getGlobalTileIDAtX:xx y:yy];
                    NSString *_value = [tileMap getTilePropertyForGlobalTileID:_globalTileID key:@"blocked" defaultValue:@"false"];
                    if([_value isEqualToString:@"true"]) {

                        _blocked[xx][yy] = YES;
                        NSLog(@"Cell %i = YES", xx);

                    }else{

                        if(_blocked[xx][yy] == YES){
                        NSLog(@"Leaving Cell %i as = YES", xx);
                            //Leave As Is

                        }else{

                         _blocked[xx][yy] = NO;
                        NSLog(@"Cell %i = NO", xx);

                        }

                    }
                }
            }
            }
}





//Code from Hero.m


-(void)moveHero
{

            // Up

            if(moveDirection == 1 && !doesNeedShiftWorld) {
                heroY += _playerSpeed;
                [self checkBlocked:1];
                _currentAnimation = _upAnimation;
                _moving = YES;
            }

            // Down
            if(moveDirection == 2 && !doesNeedShiftWorld) {
                heroY -= _playerSpeed;
                [self checkBlocked:2];
                _currentAnimation = _downAnimation;
                _moving = YES;
            }

            // Left
            if(moveDirection == 3 && !doesNeedShiftWorld) {
                heroX -= _playerSpeed;
                [self checkBlocked:3];
                _currentAnimation = _leftAnimation;
                _moving = YES;
            }

            // Right
            if(moveDirection == 4 && !doesNeedShiftWorld) {
                heroX += _playerSpeed;
                [self checkBlocked:4];
               _currentAnimation = _rightAnimation;
                _moving = YES;
            }



}   



//  ... 


- (void) checkBlocked:(int)checkDirection
{

    float xx = (heroX+160.0f+_tileWidth) / _tileWidth;
    float yy = 11-((heroY+300.0f+_tileHeight) / _tileHeight); 


    switch (checkDirection) {

        case 1:

            yy -= 1;

            if([_scene isBlocked:xx y:yy] ||
               [_scene isBlocked:(heroX+160.0f) y:yy]) {
                NSLog(@"Scene Blocked at %i, %i!", (int)xx, (int)yy);
                heroY -= _playerSpeed;

            }else{

                NSLog(@"Clear at %i, %i!", (int)xx, (int)yy);

            }

            break;

        case 2:


            if([_scene isBlocked:xx y:yy] ||
               [_scene isBlocked:xx y:(heroY+300.0f)] ||
               [_scene isBlocked:(heroX+160.0f) y:(heroY+300.0f)]) {
                NSLog(@"Scene Blocked at %i, %i!", (int)xx, (int)yy);
                heroY += _playerSpeed;

            }else{

                NSLog(@"Clear at %i, %i!", (int)xx, (int)yy);

            }


            break;

        case 3:

            xx += 1;

            if([_scene isBlocked:xx y:yy] ||
               [_scene isBlocked:(heroX+160.0f) y:yy] || 
               [_scene isBlocked:(heroX+160.0f) y:(heroY+300.0f)]) {
                NSLog(@"Scene Blocked at %i, %i!", (int)xx, (int)yy);
                heroX += _playerSpeed;

            }else{

                NSLog(@"Clear at %i, %i!", (int)xx, (int)yy);

            }


            break;

        case 4:


            if([_scene isBlocked:xx y:yy] || 
               [_scene isBlocked:xx y:(heroY+300.0f)]) {
                NSLog(@"Scene Blocked at %i, %i!", (int)xx, (int)yy);
                heroX -= _playerSpeed;

            }else{

                NSLog(@"Clear at %i, %i!", (int)xx, (int)yy);

            }


            break;

    }

}

2 个答案:

答案 0 :(得分:4)

你的问题是你正在移动播放器,然后检查他移动的空间是否被阻止。相反,你想要找出他想要移动的位置,看看它是否被阻挡,然后只有在没有被阻挡的情况下才移动他。此外,您始终可以添加一个子句来排除其当前空间。即只要你的角色没有改变网格空间,他就可以总是移动,但是一旦他即将改变网格空间,你应该检查他是否会碰到某些东西。

以下是您的代码:

if(moveDirection == 1 && !doesNeedShiftWorld) {
    heroY += _playerSpeed;
    [self checkBlocked:1];
    _currentAnimation = _upAnimation;
    _moving = YES;
}

它应该是这样的:

if(moveDirection == 1 && !doesNeedShiftWorld)
{
    //Figure out if the player is changing grid spaces.
    BOOL isChangingSpaces = ((int)((heroY + _playerSpeed) / myGridSizeVariable) != (int)(heroY / myGridSizeVariable));

    //The player should be able to move either if he isn't
    //changing grid spaces or if his destination space is free.
    if ( !isChangingSpaces || (spaceIsOpenAtX:heroX andY:heroY+_playerSpeed) )
    {
        heroY += _playerSpeed;
        _currentAnimation = _upAnimation;
        _moving = YES;
    }
}

你真的应该尝试使你的代码更多更加面向对象。在这一点上,它似乎是完全程序性的,所有变量都是全局变量,这绝对不是一个好方法。 “checkBlocked”应该被重写为“spaceIsOpenAtX:andY:”,因为这样你就可以将任何X和Y坐标放在你想要的位置,以便查看该位置是否被阻止。正如你现在所做的那样,你的代码太抽象了(传递整数而没有表明他们意味着什么除了你的实际if语句检查之外)并且它不能适用于任何情况。除了你一次性使用它。

真的,你应该有player.speed和player.animation以及[迷宫spaceIsOpen]等等。你使用的是Objective-C,而不仅仅是C.即使你使用的是C,这样做也是一个更好的主意。它以OO的方式。

答案 1 :(得分:0)

我认为问题在于你允许玩家移动到被阻挡的瓷砖上并且卡住了。

在尝试将玩家移动到那里之前,我会检查玩家移动的方向是否被阻挡。