仓库: https://gitee.com/mrxiao_com/2d_game

回顾

我们正在进行碰撞检测的工作,昨天我们几乎完成了一部分代码。由于一些原因,昨天的直播结束时未能完成所有内容。今天我们将继续进行,首先回顾一下之前的进展。我们需要让角色能够正确地与墙壁发生碰撞。之前在墙上添加了一些标记,我们想要修复现有的碰撞检测问题,使角色能够准确地与墙壁碰撞。

我们写了一些代码,目标是完成基本的碰撞检测。碰撞检测分成一个个小步骤,而我们已经在处理这些小步骤。例如,我们处理了一个移动点与水平或垂直墙的碰撞,通过编写相应的方程式来解决问题。尽管如此,我们并没有完成整个碰撞检测系统的编写,特别是碰撞检测的循环部分,这将是今天的重点。

接下来,我们要实现碰撞检测的循环,并确保至少可以处理一些基本的碰撞问题。除此之外,我们还计划优化碰撞检测的性能,使其更精确。但首先,这个基础步骤是必须完成的,只有在这一部分工作完成之后,才能继续改进系统。

目前,尽管碰撞检测的效果已经初步完成,但它依然存在许多问题,尤其是在引擎的碰撞应用方面。碰撞处理的效果远不理想,甚至可以说是非常糟糕。因此,在接下来的工作中,首先需要解决这些基础问题,再考虑如何进一步提升碰撞检测的效果。

在这里插入图片描述

继续昨天关于碰撞检测的工作

在进行碰撞检测时,首先要明确玩家的位置以及他在没有障碍的情况下会到达的目标位置。我们通过计算矩形区域来确定会涉及到的所有瓦片。如果这些瓦片上有墙壁,并且这些墙壁被标记为固体,那么我们就需要进行碰撞检测。检测的方式是,检查玩家是否会与这些墙壁发生碰撞,并找出最早的碰撞点,进而确定玩家的停止位置。

在实现时,我们首先初始化最小的碰撞时间,这个时间表示玩家会碰到墙壁的时刻。然后,我们通过循环检查每个瓦片与墙壁的碰撞情况,并每次更新最早的碰撞时间,直到找到最合适的碰撞点。如果玩家能够到达目标位置而不碰撞到任何墙壁,那么我们就不再考虑这个问题。

碰撞检测的核心是通过比较玩家与各个墙壁之间的时间,来确定玩家是否会停下。在实际操作中,我们将碰撞点和玩家的位置进行比较,计算出时间点,并确保碰撞的结果不会被其他因素影响。通过这种方式,所有的碰撞检查都会在一个时间点内完成。

接下来,我们通过检查每个瓦片的中心位置,计算出玩家相对于瓦片中心的位置。如果玩家没有受到阻碍,就可以顺利完成这次移动。然后,我们就能够更新碰撞检测函数,确保每面墙都能正确检测到碰撞,从而避免重复代码。

最后,为了提高代码的可读性和复用性,我们将相同的碰撞检测逻辑提取成一个函数,避免在每次检测四面墙时重复编写相同的代码。

在这里插入图片描述

编写墙壁交点检测

在处理玩家与墙壁的碰撞时,首先计算出与每个墙壁相交的时间。通过在玩家的运动方向(即玩家的矢量)上寻找与墙的交点,来确定玩家是否会与墙碰撞。此时,我们的关键任务是计算出在玩家移动过程中,与每面墙的相交时间,并确保这些碰撞检测在玩家的路径范围内。

计算的核心是一个时间变量(t),这个时间变量表示玩家移动到达某一位置的时间。通过比较玩家当前位置与墙的相对位置,得到碰撞的时间点。如果玩家在时间 t 时刻与墙相交,我们进一步检查 t 是否符合要求,确保这个碰撞发生在墙的有效范围内,即墙的边界之内。

如果计算得出碰撞点的 y 坐标在墙的有效范围内(即在墙的最小和最大 y 值之间),则认为碰撞有效,可以继续计算碰撞的时间。如果 t 小于当前的最小碰撞时间,则更新碰撞时间,表明这是一个新的、更接近的碰撞。

需要注意的是,墙是有限的,而非无限的,因此我们在进行碰撞检测时,必须确保计算的碰撞点在墙的边界之内。若碰撞点的 y 值在墙的 y 范围内,则认为碰撞是有效的,并进行后续的碰撞处理。

这种方法通过动态更新碰撞检测的时间点,选择最接近玩家的碰撞,以此来确定玩家与墙的交互。对于每次碰撞,都会重新计算最短的时间 t,确保我们选择的是最早的有效碰撞。

此外,还需要处理一些可能的错误,如错误的变量或数据引入,确保代码的正确性。

在整个过程中,最关键的步骤是如何计算和更新玩家的相对位置,以及如何判断与墙的碰撞是否有效。这一系列操作能够确保玩家在游戏世界中的移动是符合物理规则的。

在这里插入图片描述

修复向后移动

当我们进行碰撞检测计算时,特别是针对时间 t 的计算时,我们实际上是在计算玩家沿着路径行进时与墙的交点。这个交点决定了玩家与墙壁发生碰撞的时刻。但我们关心的是玩家从当前的位置向前移动时与墙的碰撞,而不是回头撞到墙。因此,我们需要确保碰撞发生的时间 t 大于零,因为如果 t 小于等于零,意味着玩家已经从墙的另一边开始移动,或是走回去,这种情况我们是不需要关注的。

因此,我们对碰撞发生的时间 t 结果要求是在 0 和 1 之间。这里,0 表示玩家刚好开始接触墙壁,而 1 则表示玩家到达了碰撞的位置。如果 t 小于 0,那么我们就不考虑这次碰撞,因为玩家是向后走并撞到墙,这不符合我们的检测需求。

接下来,如果我们发现 t 在合理的范围内(即 0 和 1 之间),那么我们就会考虑这次碰撞并进行处理。如果碰撞发生在不合理的时刻(比如 t 小于 0),我们就跳过不处理,因为它不符合我们需要的“正向碰撞”。

最后,关于墙体的具体测试,我们通常会从最大角(如 MaxCorner.X)开始进行碰撞测试,检查玩家是否会与墙相交。若当前的测试条件没有通过,表示我们需要继续测试其他墙。

这个过程不仅仅是一个简单的碰撞检测,而是需要逐步排除那些不符合条件的碰撞,最终确保只检测和处理那些真实有效的碰撞。

在这里插入图片描述

修复除以零错误

在进行计算时,可能会遇到除以零或非常小数的情况,这会导致灾难性的结果。如果除数为零,计算会变得无效。如果除以一个非常小的数,结果可能变得极大,虽然这不会立即导致问题,但我们还是要小心,避免这种情况发生。

为了解决这个问题,需要确保计算过程中不会出现除零的情况。如果某个值为零,计算结果将会无效,所以我们需要做一个检查,确保计算中的 x 值不等于零。这样可以避免错误的发生。

如果我们能保证 t 结果总是大于零,就能避免问题。即使除数为零,这个检查也会使计算结果无效,从而避免问题。虽然这种情况在理论上是安全的,但为了确保稳妥,我们不打算将这个问题留给运气,而是通过代码来强制确保除零问题不会出现。

理论上,计算现在应该能顺利进行,能够对单个墙壁进行碰撞测试,并且可以检测与墙壁的交点。然而,在实际操作中,可能需要启用这个特定的案例,以确保它能够顺利运行。
在这里插入图片描述

开始测试
在这里插入图片描述

实现移动玩家

在进行更新时,发现我们没有正确地处理某些运动逻辑。特别是在移动玩家时,我们忘记了将其移动到正确的位置,因此出现了一些状态错误。更新的过程中,本应设置的碰撞检测(如是否发生了碰撞)并没有正确执行,导致玩家没有按照预期的方式移动。

问题的根本在于没有正确处理玩家的偏移量和速度。我们需要确保计算玩家的移动时,能够稳定计算出正确的 delta(变化量),并根据这一结果来调整速度,避免出现不必要的错误。速度和碰撞的更新需要依赖于正确的计算过程,确保玩家的移动不受到异常值的影响。

另外,当前的代码中处理方式存在不规范的地方,比如处理速度和方向的顺序不对,或者某些状态更新漏掉。为了修复这个问题,需要重新组织代码,确保所有的变量和状态都能按照正确的顺序更新。

解决方案是先计算出玩家的 delta,然后在更新时使用该 delta 来决定玩家的实际移动位置。如果玩家没有碰到墙壁,则继续按预定路径移动。如果碰到墙壁,则调整路径,以确保玩家不会越过墙壁。

在修复过程中,发现有些地方的逻辑不太清晰,尤其是在处理速度和碰撞的边界条件时。为了避免这种情况,应该对每个逻辑步骤进行详细检查,确保每个更新都符合预期。最终目的是让玩家能够顺利穿越环境,同时避免出现卡住或异常行为。

总结来说,主要问题在于玩家的运动更新逻辑没有正确处理,导致玩家行为不符合预期。修正方案包括确保玩家的位置和速度更新正确,处理好碰撞检测和路径调整。

在这里插入图片描述

描述部分墙壁穿透

我们先要展示一个关键点,以确保大家都清楚目前的状况。可以注意到,现在出现了角色与墙体相互渗透的现象,这是因为系统仅检查了角色的一个基点,而基点本质上是一个点。

然而,如果从另一个角度来看,直线可以被视为一个点的运动轨迹,而墙体则是一个静止的对象。仅依赖单一基点进行检测,会导致角色与墙发生相互渗透。

接下来需要采取的措施是,将角色的检测从一个点扩展为一个区域形状,以便更准确地处理与墙的碰撞。但在实现这一点之前,明确现象产生的原因非常重要,这样每个人都能理解。

目前的系统还存在一些需要修复的问题,比如未完成的墙体可能会带来不良后果。下一步将修复这些问题,优化整体表现,避免类似问题继续发生。

最后强调,不应在修复完成前尝试进行其他操作,否则可能导致系统进一步受阻。我们将继续优化这些机制,以确保一切能够顺利运行。

添加剩余的墙壁测试

目前的状况是,角色经常会卡在不同位置。接下来的工作主要是调整代码逻辑,将现有的处理方式扩展为针对所有墙体进行统一测试。为此,需要将现有功能模块化,并将其定义为一个独立的函数,便于多次调用。

我们将函数命名为“测试墙”,它主要用于检测角色与墙的交互情况。为了实现这一目标,需要将墙体的相关坐标、角色的位移数据以及其他关键参数传入该函数。比如,墙的坐标可以是xy,而角色的位移数据则包括delta xdelta y。这些参数会在函数内进行处理,以测试角色与墙之间的可能碰撞。

函数的核心在于传递并处理这些参数,例如实际的xy坐标、角色的位移以及墙的边界信息。同时,需要确保这些参数能够适应不同方向的测试,比如更宽的区域检测或者与墙体交点的检测。通过这种方式,可以保证墙体检测逻辑的通用性。

接着,将测试函数集成到整体逻辑中。需要分别对墙体的每个边界点(如最大角点和最小角点)进行测试。为了支持对不同坐标方向(如xy)的检测,代码会动态调整传递的参数并重复调用检测函数。例如,通过翻转xy的输入,可以切换检测方向。

在测试的过程中,发现了一些错误。例如,有些墙体的边界未被正确检测到,可能是由于代码中参数传递时的拼写错误或其他逻辑问题。进一步检查发现,可能与max corner y相关参数的设置错误有关。为此,需要重新检查这些边界值在测试中的使用,并修复拼写错误或参数定义。

目前的主要问题在于部分墙体的碰撞检测尚未完全生效,这可能与代码的实现逻辑或尚未完成的部分相关。下一步需要进一步调试这些功能,确保每个墙体的检测逻辑都能够正确处理。

总结来看,目标是通过模块化的函数和灵活的参数传递,构建一个通用的墙体检测系统,以解决角色与墙之间的碰撞问题,并进一步完善整体逻辑。

在这里插入图片描述

这个地方有一个问题没有对旧位置规范化不然有问题的
在这里插入图片描述

新问题:由于浮点精度损失,角色走过顶部墙壁

我们已经成功完成了对所有墙体的测试,但接下来我们需要处理第二个问题。在当前的碰撞检测中,当角色与墙体发生碰撞时,计算可能会出现精度问题,导致角色并没有完全接触到墙体。

具体来说,当角色的计算位置精确地与墙体相接时,由于浮点数的精度问题,角色可能实际上稍微偏离墙体一点点。这会导致角色“穿透”墙体,未能触发正确的碰撞检测。也就是说,角色可能永远不会正确地碰到墙体,即使他实际上已经非常接近墙体。

我们依赖的碰撞检测公式通常会期望某个值大于或等于零,以判断角色是否与墙体发生碰撞。但当角色位于墙体的精确位置时,由于浮点数的最小值误差,测试可能无法成功,导致碰撞检测失败。这种精度问题会导致很小的误差,从而让角色穿过墙体。

为了处理这个问题,我们需要确保角色在测试时有一个合理的误差范围。最简单的解决方案是,在角色与墙体碰撞时,稍微将角色的位置调整一点,确保角色与墙体之间有一个小的间隔。通过这种方式,即使浮点数精度导致角色位置略微偏离墙体,也能够确保碰撞检测能够正常工作。

虽然这个方法简单且有效,但它并不完美,可能不是最理想的解决方案。我们可以通过更复杂的碰撞检测方法来避免这种问题,比如使用区域形状等更加健壮的技术。当我们逐步实现更复杂的碰撞检测时,可以重新评估这个问题,看看是否能找到更清洁、更有效的处理方式。

目前,最简单的解决方法是确保角色与墙体之间始终保持一点小的间隔,这样就能避免浮点数误差带来的问题。

实现临时修复

当前的目标是确保角色在与墙体碰撞时能够适当反应,避免由于精度问题导致角色穿透墙体。在进行碰撞检测时,发现即使角色撞到墙体,由于浮点数的精度限制,角色可能会由于位置计算的细微误差而略微偏离墙体,这使得碰撞检测无法正常工作。

为了解决这个问题,提出了一个简单的解决方案:每当角色与墙体发生碰撞时,稍微调整角色的位置,使其略微偏离墙体。通过这种方式,可以确保碰撞检测会正常触发,即使角色的实际位置因为浮点数的误差而与墙体不完全对齐。

这个调整通过引入一个小的“epsilon”值来实现,epsilon代表一个很小的容差,用来应对浮点数计算带来的误差。具体来说,当角色与墙体发生碰撞时,系统会确保角色的位置至少比墙体的边界远一点,避免因为精度问题导致的穿透现象。

然而,这个处理方法并不完美。尽管它能解决大多数问题,但并不是最理想的方案,因为它依赖于一个硬编码的误差值。在实际应用中,这个epsilon值的设定可能会影响碰撞检测的准确性,特别是在碰撞较为复杂的场景下。

通过测试,发现这个解决方案能够使角色与墙体正常接触,但由于调整的间隔较大,角色与墙体之间可能仍然保持较大的距离,这并不是期望的效果。因此,仍需要进一步调整epsilon值,使其更加精确,以便达到更好的碰撞检测效果。

目前,我们已经能够确定角色与墙体发生碰撞的时刻,并且能够通过调整位置来避免精度问题导致的穿透现象。但仍需进一步优化碰撞检测算法,确保它能够在不同情况下都能够正常工作,特别是在处理浮点数和精度问题时。
在这里插入图片描述

寻找另一个导致角色过早停止的错误

在分析中,我们发现一个错误:输入的值使用了最小值(minimum)而非最大值(maximum)。这是一个低级错误,但导致了逻辑上的重大问题。
当尝试将值限制在零以上时,代码中选择了取最小值的逻辑(Minimum)。结果,系统总是返回零,导致了我们无法实现向前推进的操作。经过发现这一点后,我们意识到这是一个非常愚蠢的错误,并迅速进行了修正。
修正后,逻辑开始正常工作,效果明显改善。对比之前的表现,现在的功能正常运作,问题得到了有效解决。
在这里插入图片描述

不过现在有时候会卡到边界里面后面应该会处理

调试断言失败

目前已经恢复到预期的逻辑状态,但仍存在一些问题需要进一步探讨。例如,在某些情况下,相关连接逻辑可能没有正确处理,导致结果出现偏差。

检查过程中发现,瓦片的相对位置值非常大,这表明逻辑存在某种错误。进一步分析瓦片的边长和米制瓦片地图的映射值,结果显示这些值虽然接近预期,但并不完全匹配,可能需要放宽某些约束或增加一定的灵活性来应对误差。

在偏移计算和后续的除法操作中,也产生了疑问。结果未能达到预期,可能是某些中间步骤需要调整。例如,考虑是否可以允许一定的槽位容差,以减少边界情况引起的异常。

针对代码中的 epsilon 引入,通常 epsilon 被用作临时解决方案来规避浮点运算的精度问题。尽管它在实际运行中可以避免部分错误,但从代码长期维护的角度来看,引入 epsilon 也可能是一种妥协。因此,优化代码逻辑以减少对 epsilon 的依赖,将是一个重要方向。

此外,在某些情况下,角色的移动范围超出了预期,或者操作显得不够平滑。这可能是由于处理逻辑中存在微小的不一致,或边界条件未充分覆盖所致。尝试复现这些问题,将有助于更清楚地识别其发生的条件并采取对应修正措施。

尽管目前实现的结果接近目标,但代码中仍有一些地方需要优化。例如,边界逻辑的进一步严谨性、误差处理的改进,以及对代码中临时解决方案的替换。这些优化将有助于提升整体代码的可靠性和可维护性。
在这里插入图片描述

更多碰撞错误;是时候编写更好的碰撞检测系统了!

在分析这个问题时,我们发现可能有些处理不当的地方。角色的移动方式不应该让他能够穿过某些指定区域或洞口,这通常是由于碰撞检测不够精准造成的。现有的碰撞检测机制可能存在问题,导致角色以不正确的方式进入这些区域。

为了解决这个问题,我们可以考虑升级到更高一层的碰撞检测系统。这种方法能够更精确地检测角色的边界,并确保角色只能在合法的区域内移动,从而避免出现现有碰撞的情况。这些情况包括角色意外穿过壁障或其他不合理的移动行为。这种方法的实施有可能消除大多数目前看到的问题。

因此,从当前的碰撞检测提升到更高一级,可能是解决此类问题的最简单而有效的策略。

观察当前碰撞检测系统的问题

通过观察这些问题,我们意识到必须处理好一个极小值(epsilon),以防止角色穿过墙壁。即便如此,即使引入了这个epsilon,有时仍然会出现角色在墙体之间“穿越”的情况。这种问题主要源于数值的不稳定性。

我们可以通过更仔细地调整碰撞检测机制来解决这些问题。比如,当角色接近墙的边缘时,数值的不稳定性会更明显,使得角色能够进入本不应允许的区域。因此,问题的核心在于数值精度不够,导致角色意外进入这些指定的区域。

这类问题的示例通常出现在碰撞检测的边缘情况,这些边缘可能导致角色轻易通过。解决这些问题的办法之一是进一步优化碰撞检测算法,使其更加稳定和准确,从而防止角色出现不符合物理规则的移动情况。这些改进可能需要进一步调整数值和边界条件,使得角色只能在合法的区域内移动。

在这里插入图片描述

新系统的需求

我们讨论了两个主要的问题。首先,是碰撞检测机制的问题,目前我们只是测试碰撞点,而不是实体的实际体积。这种检测方式容易导致角色能够穿越“无限薄”的墙体,因为数值的不稳定性会让角色从一个边界移动到另一个边界。我们希望能够引入实体的厚度,使得碰撞检测更加准确,避免角色穿越这些“薄墙”。

其次,我们提到角色不仅应该是一个点,还应该具有体积感。目前,角色在碰撞检测时是作为一个点来处理的,导致他能够在物体之间自由插入。我们认为应该使用多边形、圆形或矩形等形状来进行碰撞检测,使得角色更像一个实体而非一个点。

因此,我们的目标是升级碰撞检测系统,使其能够更好地处理实体的体积,并从线性的碰撞检测方式转向区域和体积的碰撞检测。这将提高碰撞检测的稳定性和准确性,同时使角色的移动更加真实。

在这里插入图片描述

你是否计划通过微积分来改进碰撞检测?

我们讨论了使用微积分来改善碰撞检测的问题。虽然微积分可能有助于提高碰撞检测的精度,但在实际应用中并不总是需要它。我们倾向于采用更加简单直观的方法,就像在游戏中处理角色的碰撞检测一样,不需要复杂的全体会议或高级数学运算。而是通过简单的代码段,来实现更好的碰撞检测效果,这种方式更为实际和易于操作。这样能够使得角色的碰撞检测在游戏中更为流畅,不会由于复杂的数学计算而影响游戏体验。

你认为是否需要在其他语言中原型化一些功能?

我们讨论了是否有必要在游戏中使用不同的方法进行碰撞检测。对于这个问题,很多人倾向于认为不需要使用不同的方法。我们更习惯于在游戏的运行环境中使用相同的语言来处理所有方面的碰撞检测,而不是采用其他语言或复杂的方法来处理不同的部分。这样做可以简化开发过程,提高代码的可维护性和性能,不会因为使用不同的方法而导致游戏的体验不一致。

如果我们改变游戏的分辨率,会改变物体的大小吗?

我们讨论了如果改变了解决方案,瓦片和位图如何绘制会发生变化的问题。我们目前并没有任何地图缩放代码,因此位图总是按照像素来绘制,所以如果分辨率改变,位图的大小也会相应改变。一个例子是,我们可能希望能够处理这样一种情况,使得在最后的渲染阶段,位图可以被扩展到更大的尺寸,以便它不再依赖于分辨率。这样,位图的显示效果不再受到屏幕分辨率的限制,能够更灵活地适应不同的画面尺寸。

你能基于位图和像素的 alpha 值来进行碰撞检测吗?

我们讨论了当在位图中存在透明度时,进行碰撞检测的问题。如果一个像素有 alpha 值,我们不能在整个位图上持续不断地进行有效率的碰撞检测。这样做会导致计算量过大和效率低下。相反,我们可以通过只检查具有透明度的区域的外部边缘来简化检测过程,从而减少需要扫描的区域。通过使用多边形来逼近对象,我们可以更高效地检查碰撞,而不是每个像素都进行逐一检查。这种方法避免了逐像素的检测,避免了计算上的隧道效应,从而显著提高了碰撞检测的效率。

是否有兴趣做一个关于浮点计算的教程?

对浮点计算的理解和处理是一个有趣且复杂的领域,但它并不是每个人都擅长的事情。即使是一些简单的操作也可能会很棘手。因此,我对浮点特性并不非常了解,尽管我会在需要时做一些工作绕过这些问题,但我不会给出一个很好的教程,因为这不是我擅长的领域。我的理解和操作方法可能只是表面上的,而不是深入的知识。

为什么不使用旧的碰撞系统,而是只有在发现碰撞时才使用新的系统?

我们决定移除旧的碰撞系统是因为我们打算转向一个更灵活、更基于对象的碰撞系统。旧的系统主要依赖于瓷砖地图的基础碰撞,而我们希望能够使用多边形和圆形的碰撞模型来获得更多的灵活性。这种新系统的引入,让我们能够在不同的地图结构和对象类型之间实现更好的碰撞检测,而不再依赖于旧的碰撞体系,这也是为什么我们选择了新的ftc系统作为基础的原因。

关于瓦片(tiles)呢?如果你改变分辨率,它们会保持不变吗?

我们目前的游戏是基于位图的,所以即使我们改变了分辨率,游戏屏幕的外观不会改变。我们可能会运行在原生分辨率下,例如1920x1080,但是我们也考虑支持缩放到一半的分辨率来适应像树莓派这样的硬件。当我们改变分辨率时,我们会使用预处理过的位图,在最初的渲染过程中保持较小的尺寸,然后在最终合成时进行放大。这不仅避免了每个元素都重新调整大小所带来的计算开销,还能确保视觉效果的一致性。这样做的目的是优化性能,同时确保游戏无论分辨率如何都能表现得很好。

Logo

欢迎加入西安开发者社区!我们致力于为西安地区的开发者提供学习、合作和成长的机会。参与我们的活动,与专家分享最新技术趋势,解决挑战,探索创新。加入我们,共同打造技术社区!

更多推荐