游戏引擎学习第73天

回顾并计划今天的任务

昨天的工作基本完成,目前没有特别需要处理的 Bug 或未完成的任务。当前的目标是实现楼梯上下移动的功能,这有助于进一步理解 Z 轴的具体处理方式,并探索其实现方案。

目前的问题在于游戏中尚未有明确的地面概念,因此对于楼梯所在位置的上下移动区域缺乏定义。因此,需要引入一个可以代表楼梯的对象,作为实验的基础。虽然没有专门为楼梯绘制的美术资源,但可以暂时使用现有资源,例如岩石图像或其他贴图,假设其为楼梯的代表性对象。

计划如下:

  1. 从现有资源中选择一个合适的贴图(如岩石或地面图像),用作楼梯的占位符。
  2. 在场景中定义楼梯的区域,以便测试角色在该区域的上下移动功能。
  3. 如果需要,可以在楼梯前后添加物体,测试角色穿过楼梯的效果,例如角色从楼梯前方走到后方,模拟遮挡效果。
  4. 若现有资源不足以完成测试,可通过工具(如 GIMP)创建测试用资源,用作调试辅助。

当前阶段的工作目标是搭建基础环境,通过实验逐步明确上下楼梯的功能需求和 Z 轴的实现方式。

game.cpp:将 rock02.bmp 加载到集合中

计划是将一个名为 "rock02.bmp" 的贴图加载到素材集中,并将其作为楼梯的占位符进行测试。为了实现这一目标,需要加载该贴图并对其进行分类,以便分别表示"向上楼梯"和"向下楼梯"。

具体步骤如下:

  1. 将 "rock02.bmp" 贴图加载到游戏素材集中,暂时假设它代表楼梯的存在。
  2. 定义两个独立的类别,一个代表"向上楼梯",另一个代表"向下楼梯"。
  3. 分别加载这两个类别的贴图,以便在场景中正确地绘制楼梯。
  4. 检查是否已经为楼梯专门创建了一个实体类型(例如"楼梯实体")。如果尚未定义,需要创建一个新的类型来支持楼梯功能。

接下来的任务是确认现有的实体类型配置,并确保能够正确加载和区分"向上"和"向下"的楼梯表示。通过这些设置,可以开始在场景中测试楼梯相关的功能和逻辑。

game_sim_region.h:将 EntityType_Stairwell 添加到 entity_type

在当前的代码中尚未定义一个具体的楼梯间实体,因此需要创建一个新的楼梯间类型。最初的想法是将楼梯分为"向上楼梯"和"向下楼梯"两种不同的实体,但经过考虑,可以将其简化为一个统一的实体,根据玩家的方向决定其功能:从底部进入则向上,从顶部进入则向下。这种设计更加直观和简洁。

在实现过程中,需要注意以下几点:

  1. 楼梯间可以作为一个图形化的静态元素,玩家接触到楼梯区域时触发上下楼的功能。
  2. 这种楼梯间的设计兼顾功能性和表现力,但可能需要调整其视觉效果,以便让玩家直观地理解其用途。
  3. 当前没有处理过类似的二维游戏逻辑,与通常处理三维游戏楼梯的方式不同,不需要模拟实际的楼梯构造,而是使用逻辑判断实现简化的交互功能。

在代码中,通过恢复或启用原有的楼层上下逻辑,可以测试这一楼梯设计是否有效。这需要结合已有的代码片段或功能,重新构建楼层的概念和关联系统,从而支持多层场景的加载与切换。

game.cpp:重新启用 DoorUp 和 DoorDown

我们之前的实现主要涉及处理上下楼层之间的连接,例如随机生成上下楼层的门。当前的逻辑需要调整,以适应新的实体系统。之前我们在城镇地图中直接写入了楼梯相关的瓦片值(如值为 3 或 4 的瓦片代表楼梯)。现在,我们已经摒弃了直接基于瓦片的城镇地图,而是转而使用实体系统来表示所有内容。

因此,我们需要做的是将以前基于瓦片值的楼梯逻辑,转化为基于实体的楼梯生成逻辑。例如,在处理瓦片值为 3 或 4 的情况时,可以直接在对应位置添加楼梯实体。具体来说:

  1. 瓦片值映射到实体 :以前的瓦片值 3 和 4 对应楼梯上下,现在需要用 EntityType_Stairwell 表示楼梯实体。
  2. 代码逻辑调整:在遍历地图或生成区域时,遇到楼梯瓦片值时,插入楼梯实体,而不是简单地设置瓦片值。
  3. 保留逻辑可读性:可以保留之前的随机生成逻辑,确保代码清晰,但输出结果需要直接生成对应的楼梯实体。

这可以通过复用之前的随机生成逻辑来实现,同时在检测到楼梯相关瓦片值时,调用一个方法创建并插入楼梯实体,从而完成这一转换。

讨论添加楼梯实体

上下楼层的门(DoorUp 和 DoorDown)在当前实体系统中需要重新设计。计划采用单一实体来表示楼梯,而不需要分开定义上下楼梯的门类型。这种方式能够简化逻辑,因为楼梯实体同时负责上下移动,位置则根据其在 Z 轴上的位置决定。

调整的具体思路包括:

  1. 单一实体设计 :通过引入一个楼梯实体 (EntityType_Stairwell),避免单独处理 DoorUp 和 DoorDown。这样一来,不需要为两个方向的楼梯分别创建实体,只需一个实体来表示。

  2. 随机生成逻辑调整 :之前的逻辑中,每次随机生成门时,会标记是否已创建一个上下门的路径(created_z_door 标志)。新的逻辑中,只需在生成楼梯实体时检查标志,以避免重复生成。

  3. 位置调整:楼梯实体的具体位置由 Z 轴位置决定,而不再区分上下门的类型。仅需根据路径所在的高度(Z 值)定位楼梯实体。

  4. 代码实现:判断是否已经创建过路径门,如果已创建,则生成对应的楼梯实体并添加到场景中。无需区分 DoorUp 或 DoorDown,仅需根据其 Z 值决定位置。

通过上述调整,可以简化上下楼层连接的处理逻辑,同时使系统更具扩展性和一致性。

引入 AddStair

当前正在进行楼梯的生成和绘制逻辑设计,具体内容和思路包括:

  1. 楼梯位置逻辑

    • 楼梯被添加到特定的 xyz 位置。
    • 如果是向下的楼梯,其基点位置为 z-1;否则,基点为当前的 z
    • 通过检查路径是否创建了 z 门(created_z_door),来决定是否需要生成楼梯实体。
  2. 移除旧的瓦片值逻辑

    • 不再依赖之前基于瓦片值的逻辑(例如 tile_value),转而使用 should_be_door 的布尔值来决定是否添加楼梯。
    • 原有瓦片值处理代码被重构为简单的布尔判断。
  3. 楼梯尺寸设置

    • 楼梯的高度设置为一个完整瓦片的高度(tile_depth_in_meters),以确保其连接到下一层。
    • 实体的范围(extents)暂时与墙体相同,用于占位。
  4. 碰撞逻辑待定

    • 当前楼梯实体暂时不启用碰撞处理,需在后续开发中进一步思考和完善。
  5. 楼梯绘制

    • 在绘制过程中,楼梯实体使用一个简单的位图表示。
    • 通过工具(如 GIMP)确定位图的偏移量(中心位置),以适配实际绘制的图像。
    • 位图的绘制初步设置在中心位置,后续可能需要进一步调整。
  6. 测试调整

    • 添加楼梯后,发现实体生成逻辑可能会干扰现有对象(例如怪物的初始位置)。
    • 将怪物或楼梯位置调整到不冲突的位置,以便更好地测试楼梯功能。

通过上述步骤,可以在场景中加入基本的楼梯功能,为多层逻辑的测试和开发奠定基础。同时,后续需完善碰撞逻辑及视觉效果,使其更加真实和直观。

运行游戏,查看新楼梯

目前楼梯已经生成并显示在场景中,但还存在以下问题和需要进一步处理的地方:

  1. 实体排序问题

    • 由于当前尚未实现实体的渲染排序逻辑,因此实体显示的顺序是随机的,导致视觉上不一致。
    • 实体排序将在后续渲染功能完善时处理,目前暂不优先解决。
  2. 视觉干扰

    • 当前存在某些实体(例如跟随的对象)会在视觉上对开发测试产生干扰。
    • 需要调整或暂时移除这些干扰,以便更专注于楼梯的功能测试和完善。
  3. 后续工作计划

    • 完善渲染逻辑,包括实体的层级排序,使得楼梯和其他对象能够按照合理顺序显示。
    • 调整场景中可能干扰开发的对象位置或行为,确保测试环境清晰和直观。
    • 在后续开发中逐步解决已知问题,优化整体体验和功能表现。

通过这些改进,可以使场景更加有序,为后续开发和测试提供更良好的基础条件。

考虑如何实现楼梯

目前已经将楼梯实体添加至场景中,现在需要进一步实现玩家与楼梯的交互逻辑,使玩家能够通过楼梯上下移动。以下是实现目标的详细思路和现阶段面临的问题:

  1. 楼梯交互逻辑设计

    • 设定当玩家走向楼梯时,可以向下移动(即下降到下一层楼)。
    • 当玩家从楼梯返回时,可以向上移动(即回到上一层楼)。
    • 考虑为玩家提供明确的方向指引,例如通过前进进入楼梯触发下降,后退从楼梯触发上升。
  2. 玩家交互的实现需求

    • 当玩家角色移动至楼梯实体顶部时,楼梯需要能够检测到这种交互并触发楼层切换逻辑。
    • 当前的碰撞检测逻辑较为基础,仅能检测到是否与某物体接触,需要进一步扩展支持楼梯的交互功能。
  3. 可能的技术实现

    • 为楼梯实体增加一种特殊属性,检测当玩家位于楼梯顶部区域时触发事件。
    • 实现一种逻辑,当玩家角色沿特定方向移动到楼梯顶部时,能够根据方向计算玩家应移动至的目标楼层(向上或向下)。
    • 考虑将楼梯功能与当前的"碰撞检测"功能整合,形成统一的交互检测系统。
  4. 面临的挑战与下一步计划

    • 当前的碰撞检测功能尚不完善,可能需要对其进行扩展,支持更多复杂交互,例如楼梯上下移动的事件触发。
    • 在实现过程中,需要同步考虑玩家体验,例如给出清晰的反馈以提示玩家楼层切换(如视觉效果或提示文字)。
    • 下一步的重点是分析如何将现有的碰撞逻辑与交互检测相结合,以支持楼梯功能的完整实现。

通过上述实现,可以使玩家与楼梯实体的交互更加直观,并为后续开发更复杂的交互场景提供基础。

黑板:跨越楼梯

想要实现的是,在三维空间中创建一个楼梯或坡道的交互系统,使玩家能够在上下楼层之间进行流畅的移动。具体实现思路如下:

  1. 三维空间中的上下移动

    • 设想楼梯的移动平面为一个水平区域,并且有另一个平面在其下方。玩家可以在这两个平面之间上下移动。
    • 当玩家向前走时,应该向下移动;当玩家向后走时,应该向上移动。
  2. 通过区域来控制Z坐标

    • 创建一个区域,当玩家进入该区域时,玩家的Z坐标应该根据他们在该区域中的位置逐步变化。
    • 当玩家站在楼梯的起点时,Z坐标应该为楼梯的最大值;当他们走到终点时,Z坐标为最小值。移动过程中,Z坐标应该随着玩家的位置变化。
  3. 出口区域控制

    • 设计一个区域,玩家只有在特定的位置才能退出楼梯。如果玩家位于楼梯的下方三分之一区域,他们可以离开并进入最底层区域;如果位于上方三分之一区域,他们可以退出并到达楼梯的顶部区域。
    • 如果玩家位于楼梯的中间区域,不能直接退出楼梯,应该限制玩家只能在特定区域内退出。
    • 这个限制可以是可配置的,玩家可以选择是否启用这个出口控制。如果不使用这个限制,玩家可以自由离开楼梯并自动跳到最接近的一侧。
  4. 灵活性与易实现性

    • 希望这个实现方式能够灵活,能够根据需要选择是否启用出口控制功能,而不是一种难以实现的复杂设计。理想情况下,应该很容易实现这个功能,并且可以根据需要开启或关闭。

黑板:室内/室外

目前计划引入一个"内外区域"概念,以改进碰撞检测系统,并实现更可靠的游戏逻辑和碰撞检测处理。具体内容如下:

  1. 内外区域的概念

    • 需要判断玩家是否处于某个特定区域内或区域外。这将有两个主要用途:
      • 游戏逻辑:当玩家进入楼梯区域时,需要使其行为发生特定变化,如开始上下楼梯。
      • 碰撞系统的健壮性:目前碰撞系统存在一个问题,浮点数精度可能导致玩家被错误地放置在墙体内部,从而无法正确处理玩家与墙体的碰撞。通过判断玩家是否在某个区域内,可以在玩家进入墙体后,将其移出墙体,避免玩家被困在墙内。
  2. 解决碰撞精度问题

    • 由于浮点数精度问题,碰撞检测可能会出错,使得玩家被放置在墙体内部。为了避免这种情况,计划使用内外区域概念来检测玩家是否在墙体内。如果玩家被错误地放置在墙内,碰撞系统将把玩家重新放置到墙体外。
  3. 实现步骤

    • 首先实现内外区域的功能,用于游戏逻辑和碰撞检测。
    • 之后确保这项功能能够处理所有相关的碰撞问题,以提高系统的鲁棒性。
    • 此外,提到了一些细节问题,如定时器应用中的小 bug,需要修复并确保开发过程的顺利进行。

game_sim_region.cpp:引入玩家在空间区域中的概念

目前的目标是进一步完善碰撞检测系统,特别是在判断玩家是否进入特定区域时的处理。以下是详细的实现思路和计划:

  1. 碰撞检测与区域检测

    • 现有的碰撞检测主要集中在判断玩家与其他实体是否重叠,类似于"是否在矩形内"的检测。计划在现有基础上,扩展检测范围,检查玩家是否处于一个特定的区域内。
    • 对于每个实体,将其位置和尺寸表示为矩形,判断这些矩形是否相交,从而确定玩家是否进入了该区域。
  2. 空间分区的需求

    • 当前的做法仍然依赖于逐个实体的检查,这导致了效率问题。为了提高效率,需要使用空间分区技术(例如四叉树或网格分区)来减少无关实体的检查。这是未来需要实现的优化方案。
  3. 碰撞逻辑实现

    • 使用"是否碰撞"的标志来确定是否需要进行区域内检测。通过检测矩形是否重叠,来判断玩家是否与实体发生接触。
    • 对于每个实体,首先计算其矩形区域,然后与玩家的矩形区域进行比较,判断是否存在重叠。
  4. 楼梯区域的处理

    • 虽然尚未完全确定如何实现楼梯区域的行为,但目标是通过检测玩家是否进入该区域,根据玩家的位置改变其"z坐标",以实现上下楼梯的效果。
    • 需要进一步分析如何在楼梯区域的不同位置设置不同的行为规则,例如只允许在特定位置离开楼梯。
  5. 后续步骤

    • 完成当前区域检测的初步实现,确认玩家是否处于指定区域内。
    • 优化碰撞检测系统,尤其是通过空间分区提高效率。
    • 根据具体需求,调整楼梯区域的行为,使其符合游戏设计逻辑。

黑板:指定楼梯的可穿越区域

目前在设计楼梯区域的碰撞检测时,有几个重要考虑:

  1. 玩家与楼梯区域的交互

    • 目前的想法是避免单纯的重叠检测,特别是当玩家站在楼梯外部时,不希望玩家直接进入楼梯区域。相反,应该设计一个较窄的区域,玩家需要走入该区域才能上下楼梯。这意味着,只有当玩家在楼梯区域内时,才允许上下楼梯。
  2. 楼梯碰撞区域的设计

    • 楼梯的碰撞区域将会设计成较窄,这样玩家只有在区域内时才会被允许走下楼梯。设计上,楼梯的宽度与实际行走的区域宽度有所不同,后者应该比楼梯区域稍大,以确保玩家可以平稳地通过。
  3. 玩家进入楼梯区域的处理

    • 另外,考虑到如果玩家进入了楼梯区域,可以让玩家被"吸入"该区域,这需要更精细的碰撞与移动处理。比如,可以通过某种方式在玩家接触到楼梯区域时自动吸引其进入楼梯区域,从而实现顺畅的过渡。
  4. 暂时的设计方案

    • 目前暂定的方案是统一进行碰撞检测测试,然后逐步改进如何更智能地处理楼梯区域的行为。这个过程需要更多思考,尤其是在如何使玩家的移动更自然、更符合预期方面。

总结来说,楼梯区域的碰撞设计目前计划通过窄区域限制玩家的进出,之后再考虑更精细的玩家吸入和移动行为。

game_sim_region.cpp:继续编写包含检查

在处理实体碰撞检测时,设计的逻辑包括了几个步骤:

  1. 碰撞测试与记录

    • 首先检测实体是否相交,假设它们确实相交,记录这一事实并将其标记为"重叠"状态。为了避免处理过多的重叠实体,设计了一个限制,只处理一定数量的重叠实体,避免无限制的计算带来性能问题。
  2. 限制重叠实体的数量

    • 在检测到实体相交时,如果当前的重叠实体数量低于预设的最大值(如3个),则将其添加到重叠实体列表中。这样做是为了防止引入过多的重叠测试,避免不必要的性能开销。
  3. 重叠实体的处理

    • 每次碰撞检测时,会检查该实体是否已经在重叠实体列表中。如果是,那么就从列表中移除它。如果不是,则将其添加到列表中。这种处理方式确保了只处理当前确实与其他实体发生碰撞的情况。
  4. 重叠实体的更新

    • 在更新重叠实体列表时,如果当前实体仍在重叠中,会根据需要将其从列表中移除,或者如果它是新的重叠实体,则将其添加进去。移除时,采取将列表末尾的元素移到被删除元素的位置的方式,保持列表的高效更新。
  5. 碰撞规则处理

    • 当检测到碰撞时,如果设定了碰撞后停止状态(即不允许穿越某些实体),就会阻止玩家进入该实体区域。如果不需要停止玩家的运动,则可以继续让玩家进入或离开重叠区域。
  6. 优化与简化代码

    • 在处理过程中,通过把不同的碰撞逻辑拆解成单独的函数(例如矩形相交测试),实现了代码的模块化和简化。这使得代码更清晰,同时也便于后续扩展和维护。

总之,整体思路是通过限制重叠实体的数量、检测碰撞是否进入或退出区域、以及根据碰撞规则来调整玩家状态,优化碰撞检测和实体交互的处理。

黑板:检查两个矩形是否相交

在判断两个矩形是否相交时,可以通过检查每个轴上的重叠情况来简化问题。我们可以逐一检查矩形在每个轴上的最小值(min)和最大值(max),来判断它们是否重叠。

关键思路:

  1. 重叠的条件

    • 两个矩形要相交,必须至少有一个公共点。如果在某个维度上没有重叠,那么两个矩形就不可能相交。
  2. 逐轴检查

    • 对于每个轴(X轴、Y轴、Z轴),我们通过检查每个矩形的最小值和最大值来判断它们是否有重叠。
  3. 检查方法

    • 对于每个轴:
      • 如果矩形A的最大值小于矩形B的最小值,说明它们在这一轴上没有重叠。
      • 同样地,如果矩形B的最大值小于矩形A的最小值,它们也没有重叠。
      • 如果在某个轴上没有重叠,则可以立刻判断它们不相交。
      • 如果三个轴上都没有出现这种情况,说明两个矩形在所有维度上都有重叠,进而可以确定它们相交。
  4. 注意旋转情况

    • 这种方法假设矩形没有旋转。如果矩形发生旋转,情况会变得复杂,因为旋转会导致矩形的边不再与坐标轴对齐。此时,需要采用更复杂的算法,比如分离轴定理(SAT)来检测旋转矩形的碰撞。
  5. 实现

    • 对于每个轴上的检查,首先获取矩形在该轴上的最小值和最大值。
    • 然后进行比较,确保两个矩形在每个轴上都存在重叠,只有当三个轴上都存在重叠时,矩形才会相交。

通过这种方式,只需要逐一检查每个轴上的最小值和最大值即可高效判断矩形是否相交,特别适用于没有旋转的矩形。

game_sim_region.cpp:编写 RectanglesIntersect

在检查两个矩形是否相交时,核心的思路是基于每个坐标轴的最小值和最大值来判断它们是否重叠。具体步骤如下:

  1. 基本概念:我们需要检查每个轴(X轴、Y轴、Z轴)上,两个矩形是否有重叠。如果在某个轴上没有重叠,矩形就不会相交。

  2. 检查每个轴的最小值和最大值

    • 对于每个坐标轴(X、Y、Z),我们有两个矩形的最小值(min)和最大值(max)。例如,矩形A的X轴范围是A.Min.X到A.Max.X,矩形B的X轴范围是B.Min.X到B.Max.X。
    • 如果矩形B的最大值在矩形A的最小值的左侧,或者矩形B的最小值在矩形A的最大值的右侧,那么这两个矩形在该轴上就没有重叠。
  3. 条件判断

    • 如果矩形B的最大值小于矩形A的最小值,或者矩形B的最小值大于矩形A的最大值,则表示两个矩形在该轴上没有重叠。
    • 只要有一个轴没有重叠,矩形就不可能相交。
  4. 综合判断

    • 如果在所有轴上都有重叠,那么矩形相交。如果任一轴没有重叠,那么矩形就不会相交。
  5. 总结

    • 对于每个轴,判断矩形是否重叠,所有轴上的判断结果合并起来决定两个矩形是否相交。
    • 如果任一轴没有重叠,就立即返回"没有相交"。否则,如果所有轴都有重叠,返回"相交"。
  6. 优化

    • 这只需要对每个轴进行简单的判断,可以高效地确定两个矩形是否相交。

最终,这种方法适用于没有旋转的矩形,若矩形有旋转,则需要更复杂的算法,例如使用分离轴定理(SAT)进行判断。

这样做的好处是通过分轴检查,可以在不需要复杂数学计算的情况下迅速得出结果。

处理英雄与楼梯之间的碰撞

在处理碰撞时,关键是根据不同的实体类型来决定是否需要停止碰撞。具体做法如下:

  1. 碰撞规则的应用 :当处理两个实体之间的碰撞时,需要根据实体类型判断碰撞的行为。例如,如果实体A是"英雄"(hero),而实体B是"楼梯"(stairwell),则不需要阻止碰撞,让英雄可以穿过楼梯。为此,可以设置一个标志,比如"停止碰撞"stopOnCollision,如果条件符合,就将其设置为false,允许穿过。

  2. 碰撞规则的改进 :目前的碰撞规则处理似乎放置在不适合的位置,可能在handleCollision函数的内部,这个位置应当进行调整。特别是在像"剑"这样的具体物品碰撞时,碰撞规则的处理应当更高层次一些,而不是在单一的碰撞处理函数中进行。

  3. 代码优化:目前的实现逻辑应该更清晰地拆分,尤其是涉及不同类型实体之间的行为规则时。当前的处理方式对于"剑"等特定实体的碰撞处理应当移到更合适的位置,确保规则的清晰和扩展性。

  4. 下一步:将碰撞规则放到一个更合适的层级,在碰撞处理函数内对碰撞类型进行更精细的判断,同时改善代码结构,使得以后对其他类型的碰撞处理更加简洁和可扩展。

尝试通过楼梯

在检查碰撞处理时,首先确认剑的碰撞规则是正常工作的,这部分看起来是没有问题的。然而,在尝试让角色穿过楼梯时,出现了错误,因此需要进一步排查为何楼梯的穿透行为没有按预期工作。

排查步骤:

  1. 确认碰撞条件 :需要确保在判断角色和楼梯的碰撞时,正确地将"停止碰撞"标志设置为false,使角色能够穿过楼梯。如果碰撞标志设置不正确,可能导致角色被阻挡。

  2. 检查碰撞规则 :确保在handleCollision函数中,当角色与楼梯碰撞时,碰撞规则已经被正确地应用。如果在此部分没有适当地处理楼梯的特殊规则,那么角色就会被错误地停止。

  3. 代码路径检查:确认是否有其他地方的代码影响了碰撞检测的结果,可能是某个条件或逻辑没有覆盖到,导致楼梯的穿透没有生效。

  4. 错误信息和调试:查看错误信息,并使用调试工具检查程序的执行流程,特别是在碰撞检测时如何判断角色与楼梯的交互,是否有异常条件被遗漏。

解决这个问题的关键是确保楼梯的碰撞处理规则被正确执行,并且能够允许角色穿过。

发现一个问题玩家变的很慢

调试:进入 HandleCollision

目前正在调试一个问题,目的是让某些碰撞处理逻辑能够正常工作。通过查看 handle_collision 函数以及碰撞检查逻辑,发现问题可能与临时碰撞列表的处理有关。以下是分析和改进方法的总结:

问题定位

  1. 碰撞迭代中的重复处理

    当前的 should_collide 函数没有检查临时碰撞列表。即使某些碰撞已经在临时列表中处理过,它们仍然可能在后续的碰撞迭代中被重新处理。

  2. 临时列表与碰撞规则的独立性

    临时列表被用来存储当前帧中处理的碰撞,但这种方式需要额外维护一个独立的堆栈,增加了复杂性。

  3. 重复计算问题

    当前的 should_collide 函数已经使用哈希表(可能是哈希桶)进行规则检查,但未能与临时列表的逻辑统一,导致逻辑重复。

改进方法

  • 将碰撞规则整合到现有哈希表中

    可以直接利用现有的碰撞规则哈希表来替代临时列表的功能。在处理碰撞时,将需要忽略的碰撞规则动态添加到哈希表中,而在当前帧结束后再移除这些规则。

  • 动态添加和移除规则

    碰撞处理过程中,可以动态向哈希表中添加规则,标记某些实体之间的碰撞需要被忽略。帧结束后,再统一移除这些规则以保持哈希表的清洁。

  • 减少逻辑重复

    通过让 should_collide 直接使用动态更新的哈希表,避免独立管理一个额外的临时列表,从而降低代码复杂度。

改进思路的优势

  1. 统一性

    逻辑更为清晰,所有的碰撞规则处理都集中在哈希表中,减少了代码分散管理的可能性。

  2. 灵活性

    动态规则的添加与移除使得碰撞处理更灵活,可以适应不同场景的需求。

  3. 性能优化

    减少了多余的堆栈操作和临时存储开销,提高了运行效率。

改进方向总结

在当前帧的碰撞处理逻辑中,采用动态规则管理机制,直接利用哈希表而非独立临时列表。这样既能解决当前帧中重复处理的问题,也能减少代码复杂度,并为后续的功能扩展提供更灵活的基础设施。这种方法更符合代码设计的一致性和高效性原则。

game.cpp:让 AddCollisionRule 返回一个布尔值,指示实体是否已经添加

目前的改进目标是优化碰撞规则的添加和移除机制,以下是具体的总结和优化步骤:

优化策略

  1. 利用现有哈希表存储规则

    将动态的碰撞规则直接存储在一个集中式的哈希表(CollisionRuleHash)中,无需额外维护单独的临时表或列表。

  2. 添加规则时返回状态

    在调用 AddCollisionRule 时,函数返回一个布尔值,表示规则是否已存在。如果规则不存在,则将其添加到哈希表中,并返回 false;如果已存在,则直接返回 true

  3. 简化逻辑

    通过统一使用哈希表进行规则管理,避免代码中出现多处临时数据结构,同时减少重复逻辑。

实现方式

  • 修改 add_collision_rule 函数

    调整函数逻辑,使其在添加规则的同时返回规则是否已存在:

    • 如果规则不存在,则将其添加到哈希表中并标记为有效。
    • 如果规则已存在,则直接返回状态,无需重复添加。
  • 规则的添加与移除

    在需要忽略碰撞的情况下动态添加规则,碰撞处理完成后再移除这些规则,保持哈希表的整洁。

  • 统一管理

    所有规则的添加、移除和查找均通过哈希表完成,实现逻辑的统一性。

game_sim_region.cpp:记录已添加的碰撞规则

新的碰撞规则添加逻辑

  1. 目标

    动态添加碰撞规则,并在逻辑处理后移除,以支持实体间特定情况下的临时碰撞逻辑。

  2. 规则添加逻辑

    每次循环中,判断是否需要添加新的碰撞规则:

    • 调用 AddCollisionRule 方法。
    • 传入两个实体的存储索引(storage_index),指定规则禁止这两个实体碰撞。
    • 如果规则是新添加的,则记录下来以便后续移除。
  3. 记录规则

    如果发现当前碰撞规则是新添加的,使用一个临时数据结构(如列表)将该规则存储起来,便于后续管理和移除。

  4. 规则移除逻辑

    在处理逻辑完成后:

    • 遍历之前记录的规则。
    • 调用 RemoveCollisionRule 移除对应的规则。
  5. 移除限制

    在移除规则时避免调用 ClearCollisionRules,以免清除整个哈希表中的其他规则。

game.cpp:引入 RemoveCollisionRule

我们需要实现移除单个碰撞规则的方法,所以需要添加一个 RemoveCollisionRule 的功能。现在,碰撞规则移除的逻辑还没有实现,待补充。

在移除碰撞规则时,实际上我们并不需要精确地追踪每一个规则,因为可以采用不同的方案。比如,可以通过将碰撞规则标记为"临时"的方式来存储,并在适当的时候将其移除。这样,我们就可以在需要时找到它并进行移除。

有多个方法可以实现这一点,可能还需要调整当前的方法。虽然目前还没有完全决定最合适的方案,但我们可以先考虑通过这样的方式实现"临时"规则,然后在以后根据需要进行清理。

另外,对于 AddCollisionRule,或许我们不再需要返回值,而是可以直接传递一个额外的参数,来标记碰撞规则是否是临时的。这样做可以使代码结构更清晰,避免不必要的返回值。

明天会继续处理这些问题,整理思路并做具体实现。

game.h:引入 pairwise_collision_rule_flag

计划改进当前的碰撞规则实现,考虑使用一个"配对碰撞标志"(pairwise_collision_rule_flag)。这个标志将存储两个重要信息:

  1. 是否应该发生碰撞,即是否需要覆盖默认碰撞行为。
  2. 是否是临时的碰撞规则。

这样,碰撞规则会存储这些标志,而不是单一的 ShouldCollision 变量。然后可以根据需要传递这些标志,并在适当的时机清除临时规则。

这种方案的优势是,临时规则和永久规则可以共存于同一个碰撞规则表中,使得管理更为灵活,避免了不必要的复杂操作。可以在需要时添加临时规则,完成碰撞检测后再清除它们。

这个方案感觉不错,计划明天进一步实现并验证是否可行。

TODO:临时碰撞规则!基于标志清除

计划是将碰撞规则分为"临时"和"非临时"两类。临时规则将在运动过程中使用并在完成后移除,这样可以避免生命周期管理的问题。临时规则用于在特定情况下允许穿越等特殊操作,一旦不再需要,就可以清理掉,而不必担心何时销毁这些规则。

通过这种方式,可以简化处理逻辑,避免因无法确定何时删除临时规则而导致的复杂性。临时规则不需要像持久规则那样长期存在,并且能够在每次移动开始时重新生成,确保每次都检查重叠。

这种方案解决了之前担心的生命周期管理问题,即无法确定何时应该删除某些规则。临时规则的引入使得管理变得更加简单和清晰,并且能够统一处理不同类型的规则,无论是长期有效的规则还是临时规则,系统可以自动处理它们的生命周期。

这种思路是非常令人兴奋的,因为它不仅简化了规则的管理,还避免了生命周期管理方面的问题,最终使得整个系统更加高效和灵活。

问:很高兴看到 A-A B-B 碰撞检测代码的出现。我花了很长时间才弄明白这一点

在进行AABB碰撞检测的过程中,理解重叠测试(overlap test)对于复杂形状的处理比较困难。使用分离轴定理(Separating Axis Theorem,简称SAT)来测试两个形状是否重叠是一个常见的思路。根据分离轴定理,如果两个形状不重叠,必定存在一个轴可以将它们分开。因此,重叠测试的核心就是找到这些可能的分离轴。

对于轴对齐包围盒(AABB)来说,重叠测试较为简单,因为AABB本身是一个与坐标系对齐的凸形物体。其可能的分离轴就只是坐标系的轴:X、Y和Z。因此,只需要检查这三个轴,判断它们是否能将两个物体分开。如果所有三个轴上都没有分离,则说明两个物体重叠。

然而,对于其他复杂形状来说,分离轴定理变得更加复杂,因为需要考虑所有可能的分离平面,这通常涉及到组合不同形状的元素。

在处理任意形状的碰撞时,使用GJK(Gilbert-Johnson-Keerthi)算法是一种常见的做法,因为它能处理更复杂的形状。对于简单形状(如球体),分离轴定理的实现会相对简单,因为球体在任何方向上都是对称的。如果两个球体接触,它们的分离轴就是连接它们中心的线,而这一点非常容易计算和理解。

总的来说,对于AABB和球体等简单形状,分离轴定理的应用相对简单,而对于任意形状,则需要更加复杂的算法和技巧来找到适合的分离轴。

问:你昨天提到过两种方式来解决大实体与模拟区域重叠但不在选定区块中的问题。我觉得你可以同时使用两种解决方案。如果实体大于最大实体尺寸,仍然允许它并使用两次添加的方法。这是否能让你获得两者的所有优点,而没有任何缺点?

在处理大实体重叠同一区域但不在选定区块的问题时,提出了一种新的解决方案:对于超过最大实体尺寸的实体,可以使用双重添加方法,将其添加到空间分区中两次。这样做可以同时享受两种解决方案的优势,避免两者的缺点。

这种方法得到了认可,虽然最初避免双重添加的想法是出于效率考虑,但这种做法确实可以在某些情况下提高效率。通过明确规定实体边界的设置时机,可以确保在任何时候都知道该使用哪种方式来处理该实体。总的来说,这是一个不错的想法,值得进一步尝试。在实际应用中,可能会选择一种双重系统,即支持通过多次添加大于最大半径的实体来管理它们。

问:把每侧都加上三分之一长度的墙来处理楼梯的碰撞,这样做是否合理?

考虑在楼梯两侧放置一个长度为三分之一的墙来处理碰撞问题,这是一个可行的方案。虽然这种方法是合理的,但由于需要处理走下楼梯的逻辑,因此可能会发现最终这种方案会变得多余,尤其是在已经能确定在楼梯上的位置时。总的来说,这个方案是可行的,如果遇到特别棘手的问题,仍然可以考虑使用这种墙壁方法。

问:平台层中的 TODO 列表是你在发布前需要做的更全面的更改列表吗?还是还有其他需要注意的事情?

这个问题涉及平台层的处理,是否在提交之前有一个全面的待办清单。虽然已经列出了大部分当时想到的事项,但并不确定是否是详尽无遗的,可能还会有其他需要注意的内容。

问:昨天你提到,当角色跳到上方区块时,角色会跳过地面 Z=0 然后下落。然而,由于跳跃代码让 Z 位置从负值变为 0,角色在到达上区块低侧时不就会被推到 0 吗?这样做是否会在尝试让角色上楼梯时造成问题?

昨天提到的问题是,当角色跳到上方的一个区块时,角色会跳过地面(z=0),然后再下来。然而,由于角色的 z 位置会被设为负数时推回到 0,这可能导致角色在到达上层区块的低边时被推回到 0,从而引发上楼梯时的问题。

确实,问题在于代码中将 z 位置强制设置为 0,这会导致问题。因此,需要完全删除这段代码,因为它在处理跳跃时并不正确。解决方法是,必须处理角色的运动方式,特别是在楼梯的情况下,应该仔细处理 z 轴的运动,而不是将 z 位置与 0 进行比较。

问:所有这些算法都指定了凸形状。是否有可以用于凹形状的东西,还是通常最好将凹形状分割成多个凸形状?

对于凹形状,通常的做法是将其拆分为多个凸形状。虽然并不是说凹形状就完全无法处理,但目前没有广泛使用的、直接适用于凹形状的碰撞检测算法。所有接触检测算法中,通常会首先将凹形状拆解成凸形状,因为凹形状总是可以被分解为凸形状。实际上,几乎所有实现碰撞检测的算法都会先进行这一步骤,先把凹形状拆解成多个凸形状,然后再进行碰撞检测。

问:关于 TODO 列表,你之前提到过你想编写某种缩放功能,让你能看到模拟区域外的实体

关于之前提到的待办事项,确实计划通过缩放来查看同一地区之外的实体。目前渲染部分还没有处理缩放的功能,但希望在渲染支持缩放后能够实现这一点。

问:能否做一期关于元编程的节目?

目前不会在直播中教授元编程,因为这个内容与当前的编程进度相差较远。目标是先集中精力掌握常规编程,等到项目进展顺利后,再考虑是否引入元编程。此外,关于多个模拟区域的内容,目前有简单的缩放视图,主要用于测试区域的查看。

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

相关推荐
Chef_Chen2 小时前
从0开始学习R语言--Day18--分类变量关联性检验
学习
键盘敲没电2 小时前
【IOS】GCD学习
学习·ios·objective-c·xcode
海的诗篇_3 小时前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
AgilityBaby3 小时前
UE5 2D角色PaperZD插件动画状态机学习笔记
笔记·学习·ue5
AgilityBaby3 小时前
UE5 创建2D角色帧动画学习笔记
笔记·学习·ue5
武昌库里写JAVA4 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
一弓虽5 小时前
git 学习
git·学习
Moonnnn.7 小时前
【单片机期末】串行口循环缓冲区发送
笔记·单片机·嵌入式硬件·学习
viperrrrrrrrrr78 小时前
大数据学习(131)-Hive数据分析函数总结
大数据·hive·学习
fen_fen8 小时前
学习笔记(26):线性代数-张量的降维求和,简单示例
笔记·学习·算法