针对多个正方形的基于图块的高效碰撞检测?


9

目前,我正在自己开发一款基于图块的游戏(想想Terraria,但不要太幻想了(我认为这是一个词?如果不是,抱歉)。

无论如何,我目前有碰撞检测功能(甚至适用于极端情况!),这对我来说是一大进步。看到一个精灵没有穿过一个块,这让我非常高兴。但是后来我有了基准测试的想法。馊主意。

1000平方,没问题。10,000平方,对于3个字符来说有点滞后。100,000平方(真是巨大的地图),无法显示3个字符。

我遇到的问题是,我什至不想考虑与玩家,角色,物品等距离太远的块,但我不想不断地将这些块装入内存。

到目前为止,这是我的算法,请随时批评。

foreach (Block in level)
{
    if (distance from block to player > a specified amount)
        ignore this block;
    else
    {
        get the intersection depth between the two bounding boxes
        if (depth of intersection != Zero-vector)
        {
            check y size vs x size
            resolve on smallest axis
        }
    }
}

您将注意到,当级别大小变大时,该算法的阶数将增加N个块。我什至不考虑玩家附近的障碍物。

我在想也许使用(0,0)到(mapWidth,mapHeight)块的双数组而不是列表,根据人的位置计算危险区域,例如,如果玩家的位置在(10,20)它将从(0,10)到(20,30),依此类推。

任何想法和考虑都很棒,谢谢。


1
欢迎来到stackexchange!:-)如果您不了解整个质量检查和声誉系统的工作原理,请不要忘记阅读FAQ。
David Gouveia

当然,这些图块大于16 x 16像素,在1920 x 1080时为8,100个图块。当然,您知道可移动实体的位置,并且只能检查可能在范围内的网格上的图块(如果一个图块为160 * 160并且中心在图块(12,12)中,则只需检查图块之间(6 ,6)和(18,18),总共约150个可能的图块。)。当然,重力作用下的瓷砖只会掉落,因此您只需要寻找其下方的下一块瓷砖即可。
DampeS8N 2011年

您认为16x16太小了吗?对于我来说,更改磁贴的大小并不难,因为任何引用tilewidth / height的内容都是静态常量。我要做的就是在Paint.NET中将它们放大,这很好,因为它增加了更多细节。
罗斯,

您介意分享您的碰撞代码吗?:/
ashes999

Answers:


7

是的,您的想法正确。您应该使用2D瓷砖阵列,因为它允许您按位置索引瓷砖。

Block[,] level = new Block[width, height];

而且由于玩家只能与周围的瓷砖碰撞,因此您需要进行的碰撞检查数量很少。当然,这取决于播放器的大小。在一些成熟的样品确实是这样的:

int leftTile = (int)Math.Floor((float)characterBounds.Left / tileWidth);
int rightTile = (int)Math.Ceiling(((float)characterBounds.Right / tileWidth)) - 1;
int topTile = (int)Math.Floor((float)characterBounds.Top / tileHeight);
int bottomTile = (int)Math.Ceiling(((float)characterBounds.Bottom / tileHeight)) - 1;

for (int y = topTile; y <= bottomTile; ++y)
{
    for (int x = leftTile; x <= rightTile; ++x)
    {
        // Handle collisions with the tile level[x,y] just like you were doing!
    }
}

如果您仍然有任何问题,请检查样本。


这是一个非常不错的小算法,我什至没有听说过Platformer示例(我应该有,但我声称无知)。谢谢!
罗斯,

@罗斯真的吗?您会对解决方案与样本的相似程度感到惊讶。:-)减去列表部分,其他所有内容几乎都是相同的(相交边界框,获得相交深度,在最小轴上解析)。
David Gouveia

1
天哪,我只是看着它。>。<希望我两天前知道这一点!!好吧,我是XNA的新手,但是我非常喜欢2D图形(只是OpenGL,不是很多游戏编程)。我想我应该先检查更多的资源,然后再进行编码。
罗斯

1

我想我的答案就是你的答案!;-)

如果您具有玩家的位置(和大小),则可以计算周围瓷砖的索引(这是需要详细检查的唯一索引)。这样,地图的大小就无关紧要了,它仅取决于播放器的实际大小,因此会产生更多需要检查的图块。

如果您还没有的话,也许可以在riemers.net上查看有关碰撞的教程


我听说过里默(Riemer),但是从没去找,谢谢!
罗斯,

1

处理大量冲突时,通常需要采用更高级的结构,例如Quadtree或Hashmap来检查这些冲突。

由于图块是静态的,因此建议您使用四叉树。四叉树由四叉树组成。每个四边形由四个矩形组成,每个矩形都是四边形。递归地继续直到指定的大小。每个四边形可以包含一个位于屏幕该区域的图块列表。这样,当您检查碰撞时,您可以

  1. 将支票限制在附近地区
  2. 将检查限制为仅移动中的对象

现在,如果您甚至不想看屏幕外的瓷砖,则可以执行以下操作

public bool CheckCollision(myPosition) {
    if(quadNodes.Count > 0) {
        // This is not a leaf, keep checking
        foreach(Quad node in quadNodes) {
            if(node.Position is insideViewport && nearPlayer)
                // Continue recursion
            }
        }
    }
    else {
        // This is a leaf, do checks
        foreach(Tile tile in tileList) {
            if(collision)
                return true;
        }
        return false;
    }
}

嗯,我听说过3D碰撞检测中的Octree,但从未见过将高级数据结构用于2D碰撞检测。非常感谢你!
罗斯,

1
由于他的游戏(假设Terraria)是由均匀分布的瓷砖组成,因此使用网格比四叉树既容易又快捷。四叉树更适合于更复杂的世界,在这些世界中,网格将难以安装,并且所有内容都具有任意大小。
David Gouveia

1
如果它是纯粹基于网格的游戏,甚至可以缩小到角色大小,那您都是对的。我知道,在Terraria中,他们也有不容易适应网格格式的怪物。我当时的假设是,基本世界是由砖块组成的,但其他对象可能会有所不同,它们可以将它们存储在相似的结构中,以避免构建另一个对象。我猜想他们可以对砖使用网格,然后对其他任意对象使用不同的结构(如果需要)。
Mike Cluck

1
这就是我要建议的:)网格应用于处理与地形的碰撞,而四叉树可用于处理对象间的碰撞。
David Gouveia

真正。现在,每个边界框都有2 ^次幂尺寸。这使我更容易发现碰撞检测。网格现在可以满足我的需求。
罗斯,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.