2D游戏中的表面检测?

4

我正在开发一个2D平台游戏,想知道最好的(从性能上讲)实现Surface(碰撞)检测的方法。

目前,我考虑构建由线列表构成的级别对象列表,并沿着这些线绘制瓷砖。

alt text http://img375.imageshack.us/img375/1704/lines.png

我想每个对象都持有他所行走的表面的ID,以便在上下坡时轻松地操作他的y位置。

类似于这样:

//Player/MovableObject class
MoveLeft()
{
    this.Position.Y = Helper.GetSurfaceById(this.SurfaceId).GetYWhenXIs(this.Position.X)
}

因此,我用来检测“落下/行走在表面”逻辑的方法是一个简单的点(玩家的下腿)-接触线(表面)检查 (带有一些安全近似 - 假设在线的上方1-2个像素)。

这种方法正确吗? 我一直很难找到这个问题的阅读材料,所以请随意提供链接/建议。

8个回答

3

长期从事基于多边形的2D平台游戏开发,我想给你一些建议:

制作一个基于瓷砖的平台游戏。

现在,直接回答你有关碰撞检测的问题:

你需要使世界几何图形“实体”(你可以让玩家对象成为点,但使其成为实体更好)。所谓“实体”,是指你需要检测玩家对象是否与你的世界几何图形相交。

我尝试过“玩家是否越过了这个世界几何图形的边缘”,但实际上并不起作用(即使看起来在纸上似乎可行——浮点精度问题不是你唯一的问题)。

网上有很多关于如何在各种形状之间进行相交测试的说明。如果你刚开始学习,我建议使用轴对齐的包围盒(AABBs)。

制作基于瓷砖的平台游戏要容易得多、得多、得多,比任意几何形状的平台游戏要容易得多。所以从瓷砖开始,用AABB检测相交,然后一旦你做到了这一点,就可以添加其他形状(比如斜坡)。

一旦检测到相交,你就必须执行碰撞响应。再次强调,基于瓷砖的平台游戏是最容易的——只需将玩家移动到与发生碰撞的瓷砖之外即可(你是向上移动还是向侧面移动?这将取决于碰撞——我将把如何做这个作为一个练习留给你)。

(附注:你可以用正方形瓷砖获得出色的效果——例如看看《Knytt Stories》。)


1

看看在XNA的Platformer Starter Kit项目中是如何完成的。基本上,瓷砖有一个枚举来确定瓷砖是否可通过、不可通过等,在你的关卡中,你可以GetBounds瓷砖,然后检查与玩家的交集并确定要做什么。


谢谢Mike,我会开始处理。我很好奇它如何处理旋转的物体。 - CodinRonin

1

我曾经在处理2D碰撞检测时度过了美好的时光。这个看似简单的问题如果没有提前规划,很容易变成噩梦。

在面向对象的意义下,最好的方法是创建一个通用对象,例如classMapObject。它具有位置坐标和斜率。从这里开始,您可以扩展它以包括其他形状等。

从那里开始,让我们来处理与实体对象的碰撞。假设只有一个32x32的方块,你可以从左、右、上和下撞到它。或者,根据你的编码方式,你可以同时从上面和左边撞到它。那么你如何确定角色应该走哪条路呢?例如,如果角色从上面撞到方块,为了站稳,如果编码不正确,你可能会无意中把角色推到一边。

那么,你应该做什么?对于我的2D游戏,我会在决定如何反应碰撞之前查看人物之前的位置。如果角色的Y位置+高度在方块上方并向西移动,那么我会先检查顶部碰撞,然后检查左侧碰撞。但是,如果角色的Y位置+高度低于方块顶部,则会检查左侧碰撞。
现在假设你有一个有斜坡的方块。该方块宽32像素,当x=32时高32像素,当x=0时高0像素。请注意,你必须假设角色只能从顶部碰撞并与此方块发生碰撞才能站立。对于该方块,如果是左/右/底部碰撞,则可以返回FALSE碰撞,但如果是从顶部碰撞,则可以说明,如果角色处于X=0,则返回碰撞点Y=0,如果X=16,则返回Y=16等。

当然,这都是相对的。您将针对多个块进行检查,因此应将所有可能的更改存储到角色方向的临时变量中。因此,如果角色在X方向上与块重叠5个单位,则从该变量中减去5。累积X和Y方向上的所有可能更改,将它们应用于角色的当前位置,并在下一帧将它们重置为0。

祝你好运。我以后可以提供更多示例,但我现在在我的Mac上(我的代码在WinPC上)。这是经典Mega Man游戏中使用的相同类型的碰撞检测,如果我没记错的话。这里还有一个视频:http://www.youtube.com/watch?v=uKQM8vCNUTM


0

你可以尝试使用其中一个物理引擎,例如Box2DChipmunk。它们拥有自己先进的碰撞检测系统和许多不同的奖励。当然,它们不能加速你的游戏,但它们适用于任何现代设备上的大多数游戏。


0

创建自己的碰撞检测算法并不容易。一个简单的困难例子是:如果你的角色以足够高的速度移动,在两个帧之间它将从一条线的一边移动到另一边,那么你的算法就没有时间在中间运行,碰撞将永远不会被检测到。

我同意Tiendil的观点:使用一个库吧!


w00t?你要检查两个对象是否相交,而不是对象边界是否与另一个对象发生“碰撞”。 - Mike
1
如果物体的每帧速度大于它们的宽度,那么对于您的朴素碰撞检测算法来说,就永远不会发生碰撞。 - Jordan Lewis
+1 赞同,做好这个确实很具挑战性。我曾经有一个小项目,需要汽车在高速公路上行驶,即使只需要检查在同一条车道和下一条车道中前面的一辆车和后面的一辆车的位置,并且当汽车想要变道时,也会出现无数难以调试的错误,只有在非常特定的情况下偶尔才会出现。我个人没有查看过这些库,但是如果我要开发一个适当的碰撞检测系统,我绝对会先浏览现有的库。 - dimo414
好的,在这种情况下,你是正确的。抱歉我第一次没有理解你的答案... 另外,关于计算物体移动的向量呢? - Mike
SigTerm - 嗯?这仍然是一个问题。假设你有两个坚固的2x2盒子,它们以每帧10个单位的速度相向而行,并且开始时相距一个单位。那么假设使用最可能的贴纸上的天真实现,即new_position = old_position + velocity,则模拟将无法检测到碰撞,因为不会发生碰撞。如果这还不够难,想象一下在此情况下刚好在顶部或底部互相剪辑的两个复杂形状。这是个棘手的问题。 - Jordan Lewis
显示剩余2条评论

0

我建议使用Farseer Physics。它是一个强大的物理引擎,应该能够满足你的所有需求!


谢谢RCIX,但我认为这对于我想要实现的目标来说有点过了。 - CodinRonin

0

我会这么做:

  1. 严格禁止线条碰撞。仅使用实心形状(盒子和三角形,也许还有球体)。
  2. 2D BSP、2D分区来存储所有级别的形状,或者“扫描和削减”算法。每个方法都非常强大。扫描和削减结合插入排序,可以轻松处理成千上万个潜在碰撞对象(如果不是数十万),而2D空间分区将允许根据需要快速获得所有附近的潜在碰撞形状。

使物体在表面上行走的最简单方法是让它们每一帧下落几个像素,然后获取物体与之碰撞的表面列表,并将物体向表面法线方向移动。在二维中,这是一个垂直方向。这种方法会导致物体在非水平表面上滑动,但您可以通过略微改变法线来解决这个问题。 此外,你需要在一帧内运行多次碰撞检测和“推开物体”的程序,而不仅仅是一次。这是为了处理物体堆积或接触多个表面的情况。


0

我使用了一种有限的碰撞检测方法,它基于非常不同的原理,所以我在这里分享一下,以防有所帮助:

一个黑白的次要图像。不可通过的像素是白色的。构建一个角色的掩码,其中包含任何当前设置的像素。要评估潜在的移动,请从次要图像中读取该掩码的像素,并查看是否返回白色。

要检测与其他对象的碰撞,请使用相同类型的方法,但不要使用布尔值,而要使用足够深度来覆盖所有可能的对象。将每个对象完全绘制到次要对象中,其“颜色”为其对象编号。当您读取掩码并获得非零像素时,“颜色”就是您撞到的对象编号。

这解决了所有可能的碰撞,时间复杂度为O(n),而不是计算交互作用的O(n^2)。


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