2D 平台游戏两个轴的碰撞问题
我正在使用 C++ 和 SDL 开发一个小型 2D 平台游戏/格斗游戏,但在碰撞检测方面遇到了很多麻烦。
这些关卡由一系列图块组成,我使用 for 循环来遍历每个图块(我知道这可能不是最好的方法,而且我可能也需要这方面的帮助)。对于角色的每一侧,我将其朝该方向移动一个像素并检查是否发生碰撞(我还检查角色是否朝该方向移动)。如果发生碰撞,我将速度设置为 0 并将玩家移动到图块的边缘。
我的问题是,如果我首先检查水平碰撞,并且玩家每帧垂直移动超过一个像素,它会处理水平碰撞并将角色移动到图块的一侧,即使图块位于下方(或上方)角色。如果我首先处理垂直碰撞,它会执行相同的操作,只是它针对水平轴执行此操作。
如何处理两个轴上的碰撞而不出现这些问题?有没有比我这样做更好的处理碰撞的方法?
I'm working on a little 2D platformer/fighting game with C++ and SDL, and I'm having quite a bit of trouble with the collision detection.
The levels are made up of an array of tiles, and I use a for loop to go through each one (I know it may not be the best way to do it, and I may need help with that too). For each side of the character, I move it one pixel in that direction and check for a collision (I also check to see if the character is moving in that direction). If there is a collision, I set the velocity to 0 and move the player to the edge of the tile.
My problem is that if I check for horizontal collisions first, and the player moves vertically at more than one pixel per frame, it handles the horizontal collision and moves the character to the side of the tile even if the tile is below (or above) the character. If I handle vertical collision first, it does the same, except it does it for the horizontal axis.
How can I handle collisions on both axes without having those problems? Is there any better way to handle collision than how I'm doing it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
是的。基于矢量的碰撞比基于图块的碰撞要好得多。将图块的每个边缘定义为线(有捷径,但暂时忽略它们。)现在要查看是否发生了碰撞,找到最近的水平线和垂直线。如果您取 lastPos.x * LineVector.y - lastPos.y * LineVector.x 的符号并将其与 thisTurnsPos.x * LineVector.y - ThisTurnsPos.y * LinePos.x 进行比较。如果这两个值的符号不同,则说明您已越过了该线。但这不会检查您是否已经越过线段的末端。您可以在相同的 lineVector 和 curPosition 之间形成点积(这里有一点错误,但可能足够好),它要么是负数,要么大于线的大小平方,您不在该线段内,并且没有发生碰撞。
现在这非常复杂,你可能可以通过简单的网格检查来看看你是否已经进入了另一个方块的区域。但!使用向量进行此操作的优点是它解决了移动速度快于碰撞框大小的问题,并且(更重要的是),您可以使用非轴对齐线进行碰撞。该系统适用于任何 2D 矢量(稍微按摩一下,也适用于 3D)。它还允许您相当轻松地沿着碰撞框的边缘滑动角色,因为您已经完成了 99% 所需的数学运算。找到碰撞后您应该所在的位置。
我掩盖了一些实现细节,但我可以说我已经在无数商业视频游戏中使用了上述方法,并且它非常有效。祝你好运!
Yes. Vector based collision will be much better than tile based. Define each edge of a tile as lines (there are short cuts, but ignore them for now.) Now to see if a collision has occured, find the closest horizontal and vertical line. if you take the sign of lastPos.x * LineVector.y - lastPos.y * LineVector.x and compare that with thisTurnsPos.x * LineVector.y - ThisTurnsPos.y * LinePos.x. If the signs of those two values differ, you have crossed that line this tic. This doesn't check if you've crossed the end of a line segment though. You can form a dot product between the same lineVector and your curPosition (a little error here, but good enough probably) and it is either negative or greater than the line's magnitude squared, you aren't within that line segment and no collision has occured.
Now this is farily complex and you could probably get away with a simple grid check to see if you've crossed into another square's area. But! The advantage of doing it with vectors is it solves the moving faster than the size of the collision box problem and (more importantly), you can use non axis aligned lines for your collisions. This system works for any 2D vectors (and with a little massaging, 3D as well.) It also allows you slide your character along the edge of the collision box rather easily as well because you've already done 99% of the math needed to find where you are supposed to be after a collision.
I've glossed over a couple of implementation details, but I can tell that I've used the above method in countless commercial video games and it has worked like a charm. Good Luck!
XNA 的 2D 平台游戏示例也使用基于图块的碰撞。他们的处理方式非常简单,可能对您有用。以下是对其中内容的简要说明(删除了特定于演示的内容):
如果您获取代码并希望了解它们具体执行的操作,则位于 HandleCollisions() 函数中的player.cs 中。
XNA's 2D platformer example uses tile-based collision as well. The way they handle it there is pretty simple and may useful for you. Here's a stripped down explanation of what's in there (removing the specific-to-their-demo stuff):
It's in player.cs in the HandleCollisions() function if you grab the code and want to see what they specifically do there.