如何防止平台游戏角色的角色夹在墙砖上?


9

目前,我有一个带有地形瓷砖的平台游戏(图形是从Cave Story借来的)。该游戏是使用XNA从头开始编写的,因此我没有使用现有的引擎或物理引擎。

瓦片碰撞的描述几乎完全与此答案中描述的一样(对于矩形和圆形使用简单的SAT),并且一切正常。

除非玩家摔倒时撞到墙壁。在这种情况下,他们会抓住瓷砖,开始以为自己撞到了实际上不存在的地板或天花板。

角色捕捉的形象

在此屏幕截图中,播放器向右移动并下降。因此,在移动之后,将检查碰撞-首先,发现玩家角色正在与地板上第3个瓷砖碰撞,并向上推。其次,发现他与旁边的瓷砖发生碰撞,并向侧面推开-最终结果是玩家角色认为自己在地上并且没有跌倒,并且只要碰到瓷砖就“抓住”瓷砖。

我可以通过定义从上到下的瓷砖来解决此问题,这使他可以平稳地跌倒,但是相反的情况发生了,当他向上跳到墙上时,他会撞到一个不存在的天花板。

我应该如何解决这个问题,以使玩家角色可以顺着墙倒下?



@ Byte56问题不一样,但答案可能有所帮助。
doppelgreener 2012年

注意:在过去的几周中,我很少有时间在这里尝试答案。我已经实现了世界碰撞解决方案,它在@ Byte56链接的问题中得到了回答,该问题在高速时具有单独的错误,并且尚未能够尝试解决该问题。我将在可能的情况下尝试使用这些方法,或者在可能的情况下接受答案,或者如果我自己解决该问题,请张贴自己的答案。
doppelgreener 2012年

Answers:


8

最简单,更可靠的方法是不检查隐藏边缘上的碰撞。如果您有两块墙砖,一个在另一个墙的正上方,则不应检查上砖的底部边缘和下砖的顶部边缘是否与播放器发生碰撞。

您可以确定在一次简单的遍历图块地图的过程中哪些边缘有效,将向外的边缘存储为每个图块位置上的标志。您也可以在运行时通过在冲突检测过程中检查相邻的图块来执行此操作。

此技术有更高级的版本,可以更轻松快捷地支持非正方形磁贴。

我建议您阅读以下文章。他们是在现代平台游戏(例如Cave Story)中进行基本物理学和碰撞检测的简单介绍。如果您想体验更古老的Mario感觉,还有更多的技巧需要担心,但其中大多数技巧涉及处理跳跃和动画而不是碰撞。

http://www.metanetsoftware.com/technique/tutorialA.html http://www.metanetsoftware.com/technique/tutorialB.html


您能否阐明在碰撞过程中如何忽略某些边缘?我通常会看到碰撞被检测为两个矩形(玩家边界和图块边界)的交集。当你说不是要检查各个边缘的碰撞,这是否意味着碰撞算法不是一个长方形矩形相交,但其他什么东西?您的描述很合理,但是我不确定实现的样子。
David Gouveia 2012年

是的,只需进行矩形线碰撞即可。仅简化,因为该线始终与轴对齐。您可以分解您的复选框。
肖恩·米德迪奇

5

这是我要执行的命令...

1)保存当前字符的X,Y

2)移动X方向

3)通过获取图块数据来检查字符的所有角,并通过执行以下操作检查其是否牢固:(X和Y是字符位置)

  • 左上角:地板(X / tileWidth),地板(Y / tileHeight);
  • 右上角:floor((X + characterWidth)/ tileWidth),floor(Y / tileHeight);
  • 对于左下角:floor(X + / tileWidth),floor((Y + characterHeight)/ tileHeight);
  • 对于右下角:floor((X + characterWidth)/ tileWidth),floor((Y + characterHeight)/ tileHeight);

...从地图数据中获取正确的图块数据

4)如果任何瓷砖是实心的,则需要通过恢复保存的X和...来将X强制到最佳位置。

  • 如果您向左移动:X =楼层(X / tileWidth)* tileWidth;
  • 如果您向右移动:X =((floor((X + characterWidth)/ tileWidth)+ 1)* tileWidth)-characterWidth;

5)使用Y重复2-4

如果角色比瓷砖高,则需要检查更多瓷砖。为此,您需要循环并将tileHeight添加到characterY位置以获得tile

int characterBlockHeight = 3;

// check all tiles and intermediate tiles down the character body
for (int y = 0; y < characterBlockHeight - 1; y++) {
    tile = getTile(floor(characterX / tileWidth), floor((characterY + (y * tileHeight)) / tileHeight));
    ... check tile OK....
}

// finally check bottom
tile = getTile(floor(characterX / tileWidth), floor((characterY + characterHeight) / tileHeight);
... check tile OK....

如果字符宽于1个块,则执行相同操作,但用characterX,characterWidth等替换characterY,characterHeight,tileHeight等。


2

如果您检查了作为玩家移动方法一部分发生碰撞的可能性,并拒绝了导致玩家滑入另一个物体的移动,该怎么办?这将防止玩家进入“方块”内部,因此他不能在“方块”下方。


1

我在目前正在开发的游戏中遇到几乎完全相同的问题。我解决的方法是在测试碰撞时存储重叠部分的区域,然后根据其优先级处理这些碰撞(首先是最大的区域),然后仔细检查每个碰撞以查看是否已经被较早的碰撞解决了。处理。

在您的示例中,x轴碰撞的面积将大于y轴,因此将首先对其进行处理。然后,在尝试处理y轴碰撞之前,它将检查并看到它在x轴上移动后不再碰撞。:)


1

一个简单但不完美的解决方法是更改​​播放器碰撞盒的形状,使其不为正方形。切掉角落,使他像八角形或类似的东西。这样,当他与墙面的瓷砖碰撞时,他会滑下而不会被抓住。这不是完美的,因为您仍然可以注意到他在弯道上有点儿捉住,但是他不会被卡住,而且非常容易实现。


1

本教程涵盖了一个非常类似的问题:游戏开发简介。视频的相关部分在1:44处,并用图表和代码段进行了解释。

注意14-tilemap.py出现了裁剪问题,但已在15-blocker-sides.py中修复。

我无法确切解释为什么上一个教程示例在您的代码执行时却没有剪辑,但是我认为这与以下内容有关:

  • 基于图块类型(实际处于活动状态的边)的条件碰撞响应
  • 玩家总是在“摔倒”。尽管玩家似乎正躺在瓷砖上,但实际上玩家是反复地跌落在瓷砖上,然后被推回顶部。(self.resting代码中的成员仅用于检查玩家是否可以跳跃。尽管如此,我无法让玩家从墙壁上进行“双”跳跃。但是,从理论上讲,这是可能的)

0

另一种方法(我回答了Byte56所链接的问题)将检查冲突解决方案是否将字符置于空白处。因此,在您遇到的问题中,您会从内部矩形的天花板上碰撞而将其向上移动,这是非法的(因为您仍在与另一个图块发生碰撞)。相反,只有在将其移动到自由空间中时才将其移动(例如,上图块的碰撞将您向左移动的方式),一旦找到该碰撞,就完成了。

我给出的答案有代码,但是变得过于复杂。我会跟踪该时间范围内的所有碰撞,只跟踪导致可用空间的碰撞。但是,如果没有,我想我是将角色的位置重置为最后一个位置,但这确实很丑陋,也许您宁愿尝试实现像原始Mario那样的功能,即他只是朝一个方向移动,或者是在没有自由空间分辨率的情况下进行操作是可能的。您也可以对冲突解决方案列表进行排序,并仅移动到最短距离的自由空间(我认为解决方案是最可取的,尽管我没有为此编写代码)。


0

我只做过3D SAT,所以也许我做错了。如果我了解您的问题,可能是由于无法与接触轴取得联系,导致玩家的行为就像地板一样。一种解决方法可能是改为将圆形用于您的角色,然后在碰到墙时您只会在x轴方向上获得接触轴。

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.