基于XNA Tile的游戏:使用2D阵列内的网格位置检查2个对象之间的碰撞

时间:2016-04-08 17:16:32

标签: c# multidimensional-array collision-detection xna-4.0

我想知道是否有人可以帮我弄清楚如何检查二维阵列中两个物体之间的碰撞。

我正在努力制作一款简单的2D自上而下游戏。地图由20x20 2D正方形阵列组成。玩家和敌人都是正方形,每个占据网格内的一个方格。网格由多个不同类型的正方形组成。到目前为止,我有地板方块(玩家和敌人将被允许移动),墙方块(玩家和敌人不能移动或过去),然后是敌人和玩家方块。以下是它的截图:

enter image description here

我在游戏类中创建并初始化2D数组。使用0-3中的数字我可以选择每个方格将包含的内容: 0-地方广场1-墙广场2-敌人广场3-球员广场。

首先,我让玩家通过简单地使用嵌套for循环并检查播放器的上方,下方,左侧和右侧的网格位置来与墙壁方块发生碰撞,以查看2D数组中的值是否为1。那不是那意味着它不是一个墙,这意味着允许玩家移动。

但是在检查玩家和敌人之间的碰撞时我不能使用这种方法,因为我只使用2D数组(2和3)中的值来选择最初绘制敌人和玩家方格的位置。这意味着它们当前都在的网格位置包含值0。

我以为我会通过存储每个玩家和敌人对象的“网格位置”来实现它。这样我就可以检查玩家周围的每个网格块,看它是否等于敌人的网格位置。如果它是相等的,那么我将设置我用来将该特定方向上的玩家移动到(0,0)的运动矢量,以防止它们朝那个方向移动。当敌人不再在玩家的一个街区内时,运动矢量将返回其原始值。

我已经取得了一些成功,但它似乎只适用于一个敌人物体。对于一个敌人,我无法从任何角度通过它,但与其他人一起,我可以直接通过它们。

注意:看起来屏幕下方最远的敌人是玩家广场可以碰撞的唯一敌人。

我已经使用了断点,我可以看到,当我接近我可以通过的任何敌人时,它实际上会运行检查以查看玩家旁边的方块是否是敌人但是它没有实际上是阻止玩家穿过敌人。

所以我的问题是,我是否正在做错误的碰撞,有更简单的方法吗?或者,如果我的代码中可能存在错误,导致其无法正常工作?

以下是我的一些代码:

这第一个样本是针对玩家/敌人的碰撞。这包含在敌人类中。正如我之前所说,我使用玩家和敌人的网格位置来检查它们是否在彼此的一个方格内,如果是,那么我将玩家的移动向量设置为0,0。当它们不再在一个方格内时,我将运动矢量重置回原始值。

public void CheckCollision(Player playerObject)
    {
        //Check above player
        if (playerObject.PlayerGridPosition.Y - 1 == gridPosition.Y && playerObject.PlayerGridPosition.X == gridPosition.X)
        {
            //north is a vector2 variable that I use to add to the players position in order to move them about the screen. I use it to update both the players position
            //and the grid position. North, South, East and West are all similar except the values contained in each are for a specific direction
            playerObject.north.X = 0;
            playerObject.north.Y = 0;
            //This bool is used to check for when an enemy is beside and no longer beside the player.
            besidePlayer = true;
        }
        //Check below player
        if (playerObject.PlayerGridPosition.Y + 1 == gridPosition.Y && playerObject.PlayerGridPosition.X == gridPosition.X)
        {
            playerObject.south.X = 0;
            playerObject.south.Y = 0;
            besidePlayer = true;
        }
        //Check to right of player
        if (playerObject.PlayerGridPosition.Y == gridPosition.Y && playerObject.PlayerGridPosition.X + 1 == gridPosition.X)
        {
            playerObject.east.X = 0;
            playerObject.east.Y = 0;
            besidePlayer = true;
        }
        //This if statement just checks to see if any of the enemies are within a squares space of the player, if they are not then the besidePlayer bool is set to false
        else if (playerObject.PlayerGridPosition.Y != gridPosition.Y && playerObject.PlayerGridPosition.X + 1 != gridPosition.X && playerObject.PlayerGridPosition.Y - 1 != gridPosition.Y && playerObject.PlayerGridPosition.X != gridPosition.X && playerObject.PlayerGridPosition.Y + 1 != gridPosition.Y && playerObject.PlayerGridPosition.X != gridPosition.X)
        {
            besidePlayer = false;
        }
        //When an enemy is no longer beside the player then we can reset all the North, South, East and West vector velues back to their original values.
        if (besidePlayer == false)
        {
            playerObject.north.X = 0;
            playerObject.north.Y = -1;

            playerObject.south.X = 0;
            playerObject.south.Y = 1;

            playerObject.east.X = 1;
            playerObject.east.Y = 0;
        }
    }

下一段代码用于我在2D数组中设置值并创建关卡的布局。它也是我创建敌人对象并设置玩家对象位置和网格位置的地方,使用网格上特定“数字”的行,col来选择绘制它们的位置。

注意:行和列都会被翻转,否则水平会被向侧面绘制。

public void LoadLevels(int level)
    {
        //More levels will be added in later.
        if(level == 1)
        {
            //Here I set the values inside the 2D array "grid". It is a 20x20 array.
            //0 = Floor Square, 1 = Wall square, 2 = Enemy Square, 3 = Player Square
            grid = new int[maxRows, maxCols]
            {
                {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
                {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
                {1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1},
                {1,0,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,1},
                {1,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,0,1},
                {1,0,1,2,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,1},
                {1,0,1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,1,1,1},
                {1,0,1,0,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,1},
                {1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1},
                {1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1},
                {1,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,0,1},
                {1,1,1,1,1,1,1,1,0,0,0,3,0,0,0,2,0,0,0,1},
                {1,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,0,1},
                {1,2,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
                {1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
                {1,0,1,2,1,1,0,1,0,1,0,1,0,1,0,0,0,0,0,1},
                {1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
                {1,0,1,0,1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1},
                {1,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,1,1,0,1},
                {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
            };
            //Cycle through the array with a nested if
            for (int i = 0; i < maxCols; i++)
            {
                for (int j = 0; j < maxRows; j++)
                {
                    //If the the value at grid[i, j] is a 2 then create an enemy at that position
                    if (grid[i, j] == 2)
                    { 
                        enemyList.Add(new Enemies(Content.Load<Texture2D>("redSquare"), new Vector2(j * squareWidth, i * squareHeight), new Vector2(j, i)));
                        //Reset the value at this position to 0 so that when the player/enemy moves from this spot, a floor tile will be drawn instead of a blank space.
                        grid[i, j] = 0;  
                    }
                    //If the value is 3 then set the players position and grid position to the value based of [i, j] values.
                    if (grid[i, j] == 3)
                    {
                        playerObject.PlayerPosition = new Vector2(i * squareWidth, j * squareHeight);
                        playerObject.PlayerGridPosition = new Vector2(i, j);
                        grid[i, j] = 0;
                    }
                }
            }

        }
        if (level == 2)
        {
        }
    }

3 个答案:

答案 0 :(得分:2)

如果没有看到完整的代码,那么很难找到真正的错误。直接调试它,但我觉得你的方法太复杂了。

就个人而言,我将遵循以下逻辑/伪代码:

loading () {
    load level to array, no need to perform additional modification
}
game_update () {
    for each tile within the array {
        if ( tile is player ) {
            read user's input
            temp <- compute the next position of the player
            if ( temp is floor ) then { current player position <- temp } // move allowed, update its position
            else { the player must stay on its current position } // move rejected
        }
        if ( tile is enemy ) {
            temp <- compute the next position of this enemy
            if ( temp is floor ) then { this enemy position <- temp } // move allowed, update its position
            else { this enemy must stay on its current position } // move rejected
        }
    }
}
game_draw () {
    for each tile within the array {
        draw the current tile
    }
}

这样,任何非法移动都会在发生之前被阻止。

答案 1 :(得分:2)

  

但是在检查之间的碰撞时我无法使用此方法   玩家和敌人因为我只使用2D数组中的值(2和3)   选择最初吸引敌人和玩家方格的地方。

也许这暴露了你的设计中的一个缺陷,你应该修复而不是解决。我没有足够的代码来准确地说出来,但似乎你在Player类或其他网格中复制信息。根据您的描述,每个方块包含值0-1-2-3的2D网格完全描述了游戏的状态。为什么不将这个网格用作single source of truth的所有内容?

答案 2 :(得分:1)

旁注:您似乎缺少西方的代码。如果这是无意的,那应该是一个简单的解决方法。

我认为可能发生的事情是,只要一个敌人不在玩家旁边,就可以让玩家再次向所有方向行走,无论早期怪物施加什么限制。

如果我是正确的,那么它会在屏幕下降位置检查每个敌人并在需要时阻止玩家。但是,如果任何剩余的敌人不在玩家旁边,则在执行对该敌人的检查并允许玩家再次行走时撤消更改。对于最后一个敌人来说,这不会发生,因为他们不再是不相邻的敌人来撤消其阻挡。

尝试更改它,以便重置每帧开始时允许的移动,而不是每次发现不在玩家旁边的怪物时。