老游戏如何检测墙壁、地板和天花板的碰撞?

24
我一直在从stackoverflow和其他网站上阅读有关游戏中碰撞检测的文章。很多文章都谈到BSP、边界椭圆、积分等。但是在NES上,他们成功地实现了游戏中的地面和墙壁碰撞检测,我很难相信他们会做出许多计算来检测墙壁碰撞。
我猜我的问题是,在由瓷砖组成的关卡中,像马里奥和洛克人这样的游戏如何检测与墙壁和地板的碰撞,考虑到处理能力很小。
- 他们是否遵循运动路径并确定最接近的连接瓷砖?(搜索)(先验) - 他们是否先确定与地面发生碰撞,然后找出最佳的调整方式?(后验)这在可变时间步长下很冒险,如果你够快,你可能会跳过一个瓷砖。尽管我认为NES游戏的时间步长与电视的刷新率同步。 - 当你在地面上时,重力是否总是影响着你的角色?或者当你确定要踩在一个瓷砖上时,你只是“关闭”它?当你走到悬崖边缘时呢?你需要某种方式来确定你下面的瓷砖。 - 如果你与一个瓷砖碰撞,你只需找到该瓷砖的边缘,并将你的角色移至其侧(取决于行进方向)吗? - 超级大金刚和马里奥中的倾斜瓷砖怎么办? - 对于你可以从底部跳过并落在上面的“平台”,如果你是“后验”的,如何处理这些瓷砖的碰撞?
我编写了一些碰撞代码,基本上是“先验”的,因为它搜索了你以某个方向移动时会碰到的第一个瓷砖。我只是想知道是否有更好的方法。(也许只使用事后碰撞检测)
例如,用于向下移动时检查瓷砖碰撞的代码(我检查垂直然后水平移动):
  def tile_search_down(self, char, level):
        y_off = char.vert_speed
        assert y_off > 0

        # t_ are tile coordintes
        # must be int.. since we're adding to it.
        t_upper_edge_y = int( math.ceil((char.y+char.h) / self.tile_height ) ) #lowest edge
        while (t_upper_edge_y*self.tile_height) < (char.y+char.h+y_off): # lowest edge + offset

            t_upper_edge_x = int( math.floor(char.x/self.tile_width) )
            while (t_upper_edge_x*self.tile_width) < (char.x+char.w):

                t_x = t_upper_edge_x
                t_y = t_upper_edge_y 
                if self.is_tile_top_solid(t_x, t_y, plane):
                    char.y = t_y*self.tile_height - char.h
                    char.vert_speed = 0.0
                    char.on_ground = True
                    return

                t_upper_edge_x += 1
            t_upper_edge_y += 1

        char.y += y_off
4个回答

19

对于你所谈论的NES时代游戏类型,所有的元素都是2D的。这单独就简化了许多事情。

那个时代的某些机器(特别是像Commodore 64这样拥有硬件精灵的机器)具备硬件碰撞检测功能。大多数没有依赖于硬件碰撞检测的游戏都会使用边界框或者碰撞掩码(1位位图的精灵)。

无论哪种方式,碰撞检测通常都是“a posteriori”进行的,除了一些特殊情况,比如世界的边缘。有些游戏实际上会有错误,当你移动得太快撞到东西时,可能会穿过它。(事实上,80年代早期游戏的评论经常会评论碰撞检测的精度)。

对于平台游戏,你通常需要在施加重力之前检查角色是否“接地”。

单向平台的问题并不太难解决,因为你知道精灵的速度向量,所以你可以用它来确定碰撞是否应该被注册。


16

这里有一篇文章,深入介绍了如何编写任天堂娱乐系统(NES)“平台游戏”。

也许我没有搜索对,因为之前没有偶然发现过这篇文章。


14

对于像《超级马里奥世界》(SNES)这样的游戏,该游戏将关卡以易于记忆的格式存储在内存中,因此可以轻松获取马里奥的X/Y位置,将其转换为瓷砖地址,然后检查该地址周围的瓷砖。由于关卡始终是固定宽度的(尽管您可以查看的区域不同),因此地址更容易管理,因为它始终与马里奥的位置有固定的偏移量,例如Address + 1表示马里奥旁边的瓷砖,Address + 0x300 表示他下方的瓷砖等。


我应该指出,在找到瓷砖后,马里奥世界使用了一个函数查找表来处理瓷砖碰撞,这意味着每个瓷砖只执行它想要执行的代码。 - Sukasa

2
在早期游戏中,碰撞检测通常不够完美,为了提高性能而牺牲了准确性。例如,在超级马里奥兄弟游戏中,与敌人的碰撞仅在每两帧中检查一次。与关卡结束旗杆的碰撞仅在27帧中进行一次。还有一个限制,即最多只能检查一定数量的对象进行碰撞,这使得你可以在游戏末尾通过一些 Browser 的攻击而不死亡。另一个例子是 PC Engine 版 Gradius 游戏。它不使用更昂贵的边框命中检测,而是使用瓷砖系统。每个对象都被缩小到一个瓷砖编号,包括四舍五入到8的倍数的 X 和 Y 位置,并连接成一个单一的数字。如果两个对象占据同一个8x8的瓷砖,则认为它们发生了碰撞。虽然不太精确,但往往对玩家更有利,因此是一个可接受和有趣的折衷方案。

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