C# / XNA - 2D 碰撞引擎故障?

3

这里有相当一大段代码,但它相对简单。

这些都是来自不同类的片段,所有引用都正确,但我认为我在某处犯了基于数学的错误,而我找不到它。它总是在 y 轴上比应该早一个像素发现碰撞。我还没有尝试在不同的 X 轴位置上使用它,但它似乎可以很好地通过旁边的块坠落。

结构体 "mapSection" 只包含两个向量2-一个左上角块和底部左块坐标。

tileManager.def_ts 是默认的瓷砖宽度和高度(32)。玩家的大小为 32x64。

toWorldSpace 函数现在什么也没做,只是返回,所以那不是问题所在。

当我说块坐标时,我的意思是块在瓦片数组中的索引(例如,0,0 是第一个块,0,1 是 Y 轴上的第二个块,1,3 是 X 轴上的1块和Y轴上的3块,我并不是指实际像素。)

从 tile engine 类:

    public mapSection toMapMinMax(Vector2 position, Vector2 size)
    {
        position = toWorldSpace(position);

        position.X = (float)Math.Floor(position.X / tileManager.def_ts);
        position.Y = (float)Math.Floor(position.Y / tileManager.def_ts);

        size.X = (float)Math.Floor(size.X / tileManager.def_ts);
        size.Y = (float)Math.Floor(size.Y / tileManager.def_ts);

        return new mapSection(position, position + size);
    }

    public bool collision(Vector2 screenPosition, Vector2 size)
    {
        mapSection mapCollisionPossibilities = toMapMinMax(screenPosition, size);

        for (int y = (int)mapCollisionPossibilities.topLeft.Y; y <= mapCollisionPossibilities.bottomRight.Y; y++)
        {
            for (int x = (int)mapCollisionPossibilities.topLeft.X; x <= mapCollisionPossibilities.bottomRight.X; x++)
            {
                if (x >= 0 && y >= 0 && y < tiles.Count && x < tiles[y].Count)
                {
                    if (tileManager.tileTypes[tiles[y][x]].collideable == true)
                    {
                        return true;
                    }
                }
            }
        }

        return false;
    }

这是来自播放器类的代码:

        if (!tEngine.collision(position + new Vector2(0, 1), new Vector2(32, 64)))
        {
            position.Y += 1;
        }

我添加了“Vector2(0,1)”,因为我想看看是否存在更远处一像素的碰撞;以便他一直下落,直到撞到物体。目前非常基本,但仅用于测试尚未工作的碰撞引擎。
这是一个错误的图片。你可以看到玩家高度多了一个像素。
在图片中,“X:”是X轴上的左上方块坐标,“X2:”是X轴上的右下方块坐标,与“Y:”和“Y2:”相同,除了Y轴。它们是直接从mapSection中读取的。
如果有人能够注意到为什么会发生这种情况,将不胜感激。
谢谢。
如果您无法理解代码的任何部分,请在评论中发布,我很乐意解释,或者如果您认为本文某些地方有点不太明确。

1
问题只是视觉上的,还是数字也有偏差?这可能由许多因素引起,包括图形图像有额外的行、高度计算没有减去1、<= 应该是 <,或者是一个舍入问题。 - Dave Cousineau
1
toMapMinMax可能会提前返回下一个瓷砖像素,这可能是一个四舍五入的问题。您需要在Y值递增时逐步通过toMapMinMax,并确认每次它返回正确的瓷砖坐标。 - Dave Cousineau
1
在 toMapMinMax 方法中,在执行计算之前,您应该先将位置和大小四舍五入,可以通过强制转换为整数或使用 Math.Round 方法实现。 - Dave Cousineau
1
你的碰撞代码和绘制代码必须有不同的舍入方式。即使你的碰撞代码偏移了一个像素,如果你的绘制代码也有偏差,你也不会注意到。要么你的碰撞发生得太早了,要么你的绘制略微偏高。(可能) - Dave Cousineau
1
我只会将位置加1。浮点数并不能保证其准确性。例如,你可能会将18+1相加,但结果可能是18.99999999999。Math.Floor会将其转换为18,而你的绘图代码可能会绘制到19。 - Dave Cousineau
显示剩余8条评论
1个回答

3

编辑:关于瓦片坐标问题,您的toMapMinMax代码应该更像这样:

编辑2:从bottomRight减去(1,1),因为它是我们要添加的大小。

public mapSection toMapMinMax(Vector2 position, Vector2 size)
{
    Vector2 topLeft = position;
    Vector2 bottomRight = position + size - new Vector2(1, 1);

    topLeft.X = (float)Math.Floor(topLeft.X / tileManager.def_ts);
    topLeft.Y = (float)Math.Floor(topLeft.Y / tileManager.def_ts);
    bottomRight.X = (float)Math.Floor(bottomRight.X / tileManager.def_ts);
    bottomRight.Y = (float)Math.Floor(bottomRight.Y / tileManager.def_ts);

    return new mapSection(topLeft, bottomRight);
}

此外,我在之前的评论中犯了错误;在两个for循环中,你确实需要使用<=符号,因为大多数情况下你将会检查6个瓷砖。
关于像素偏移问题:
为了让你看到字符偏移了一些像素,绘制代码和碰撞代码必须不同。如果它们完全相同,例如它们都偏移了15个像素(你会提前15个像素发生碰撞,但你也会提前15个像素绘制),你将看不到任何变化。
1像素的间隙表示绘制坐标计算和碰撞坐标计算之间存在1像素的差异。这1像素的差异很可能是由于舍入方式不同造成的,可能是因为你在碰撞代码中调用了Math.Floor,但在绘制代码中没有将坐标舍入。(我猜你可能只是直接将位置Vector2传递给SpriteBatch.Draw方法)。

但是将其放置在地板上只会使其向上移动1个像素或保持原位? - Ashley Davies
你看到我在 OP 中最近的评论了吗? - Ashley Davies
仍然是同样的问题,似乎给出了与我的原始示例相同的结果。 - Ashley Davies

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接