游戏引擎学习第253天:重新启用更多调试界面

运行游戏,尝试调试系统,并为今天的工作设定方向。

今天我们将继续完成调试编辑代码的收尾工作。虽然昨天已经让它运行起来了,但目前还在使用旧的GUID系统,以及调试系统里早期用于探索阶段的一些旧式实现。因此,我们需要做一些清理工作,确保例如高亮显示等功能能正常运作。

此外,我们还希望重新启用对其他类型值的编辑功能,例如 real32 类型的值,我们之前确实为它们实现过编辑,但现在可能被禁用了,所以打算回顾一下这些部分。我们计划移除不再需要的旧调试 ID 系统,改为统一使用GUID系统来实现这一部分。

目前运行调试时,一切都运作正常,我们已经可以编辑调试值。这些值具体控制什么可能记不太清了,但其中有一些是在特定条件下才会起作用,比如当主角进入新区域时才更新状态。虽然这些代码并不是用于游戏主逻辑,只是用来测试的调试值,但目前看上去这些功能都正常了。

我们还保留了之前做的一个模式,用于测试,目前里面有很多内容,而且运行效果也都还可以。所以接下来应该进入整理阶段,例如:

  • 恢复调试值的高亮显示功能;
  • 因为决定让调试用的GUID标签保留完整字符串表示,因此需要在输出时对其进行裁剪,只保留有意义的末尾部分,不再打印全部内容;
  • 整理之前的调试系统残留代码。

另外,之前我们还实现过一个查看实体的调试功能,现在还可以看到它。还有查看位图的功能,好像没有重新启用。比如选中一个实体时,目前没有看到任何位图渲染,这意味着可能在之前的修改中引入了 bug,这一点我们也需要进一步检查。

总体来说,当前进展还不错,主要问题集中在清理和完善上。目前我们还没有弄清楚一些细节,比如为什么调试中会出现某个"gayatri"的信息,看起来还需要进一步理清上下文。总之,系统已经趋于稳定,但还有很多待完善的小细节。

为调试 ID(DebugIDsα)表达歉意。

我们现在回顾并梳理了一下整个调试系统中关于 debug id 的使用逻辑,目前的观察如下:

我们曾一度认为 debug id 是多余的,打算移除它,但回顾代码后发现它的设计其实相当合理,而且在系统中的用途仍然明确,因此决定保留。其主要的作用是用来在渲染调试元素时进行识别和交互检测,这个逻辑依旧成立。


当前系统状态与分析要点:

  1. debug id 的生成机制:

    当前 debug id 是通过变量组中的链接(link)生成的,这是变量唯一性的一个依据,从逻辑上看没有问题。

  2. DEBUGDrawElement 的使用:

    调用绘制调试元素的时候都会传入 debug id,用于在交互逻辑中判断是否命中元素。这种做法本质上就是通过 debug id 来匹配用户当前操作的对象。

  3. 交互匹配问题:

    我们发现的问题是:交互没有正确生效,也就是说 InteractionsIsHot 并没有返回 true,导致看起来点击操作没触发。

    • 怀疑的点在于:虽然 debug id 是通过 link 构造的,但在某些路径中是否被篡改或未保持一致还有待验证。
    • debug_event_to_text 的输出也确认我们确实成功地构造出了带有 debug id 的事件,只是未与交互命中系统对接成功。
  4. 逻辑流程追踪:

    当前的交互逻辑是从 DEBUGDrawElement() 开始的,传入 debug id,然后内部再进入处理判断是否为当前热点区域(hot),最终决定是否产生点击等动作。但目前看来,这部分的链条中某个细节可能未对齐,导致虽然 debug id 存在,但命中失败。


结论与下一步打算:

  • 决定保留 debug id,因为它的作用仍然清晰、实用,没必要舍弃。
  • 下一步我们将继续追踪调试元素生成和交互系统连接的代码路径,重点查看 debug id 的构造是否一致、是否在某些地方遗漏或构造方式不同。
  • 还需要进一步核查在遍历 debug element 时是否有遗漏导致 InteractionsIsHot 判断失败。

这个问题虽然表面看起来是一个小 bug,但实际上暴露出调试系统中标识符逻辑的一些细节问题,修复这一点可能会让整个系统的稳定性更好。我们继续深挖下去。

再次运行游戏,确认调试 ID 功能可以用于切换状态,并调查高亮为何无法生效。

我们目前在调试系统中遇到一个问题:虽然 debug id 在逻辑上是构造正确的,并且点击行为已经能够生效,但调试元素在鼠标悬停时没有正确高亮显示,导致用户无法直观地感知当前交互区域。这是不符合预期的行为,因此我们开始对整个高亮判断机制进行分析。


当前已知现象与分析:

  1. debug id 是有效的

    已验证:当点击元素时能够正确触发切换操作,因此可以确认 debug id 在某些交互路径中确实被正确传入并生效。

  2. 高亮逻辑依赖 interactionishot

    高亮显示是通过判断当前交互是否处于 hot 状态(即鼠标是否在该元素上)来控制颜色渲染的,判断逻辑集中在 InteractionsIsHot() 函数中。

  3. 元素绘制调用链无误

    调用了 DEBUGDrawElement(),同时传入了 debug_id。理论上这应该能够注册交互区域、接收鼠标事件、并触发 is_hot 状态。

  4. 关键路径检查

    查看 InteractionsIsHot() 函数,它会比较传入的两个 interaction 对象是否相同。比较的包括:

    • 两个 debug_id 是否相等
    • 类型是否相等
    • 泛型数据(generic)是否相等

    这一切看起来都是合理的判断逻辑。


疑点定位:

  1. 是否正确设置了 hot interaction

    在调用 default_interaction() 过程中,若鼠标确实位于该元素区域内,应该将 NextHotInteraction 设置为当前元素。但我们怀疑的是,这里可能没有被触发,或者被覆盖了。

  2. 是否存在元素层级遮挡?

    如果后续的元素覆盖了前面注册的 hot interaction,那可能造成预期之外的高亮被"顶掉"。

  3. generic 字段的匹配是否严格过头?

    如果在比较 InteractionsIsHot() 时,泛型部分数据稍有不同,也会导致匹配失败,哪怕 debug_id 是一致的。

  4. 元素矩形区域判断是否正确?

    如果 BeginElementRectangle() 的区域判断存在误差(比如尺寸太小或坐标不一致),可能压根就没有正确注册鼠标悬停事件。


下一步计划:

  • 验证 default_interaction() 是否确实设置了 NextHotInteraction

    打印调试信息确认该值是否在悬停时被设置为当前元素。

  • 查看是否存在多个元素竞争 hot 状态:

    调试中逐步确认后续元素是否覆盖了之前注册的 hot 区域。

  • 简化调试路径进行逐步验证:

    创建一个最小可复现的调试元素,仅包含 debug_id、交互判断和高亮逻辑,剥离其他干扰因素。

  • 临时放宽 InteractionsIsHot() 的比较标准以做实验:

    先只比较 debug_id,忽略 generic 部分,看是否能触发高亮,以判断问题是否出在这一层。


初步结论:

系统逻辑整体是合理的,但存在某个局部细节没有对齐,最可能的原因是在元素注册或交互比较中某些值不一致或未被正确设置。我们将继续逐步排查,首先验证 hot 状态是否真的在交互中失效。

game_debug.cpp 中,用 debug_element 替换 debug_stored_event

我们当前定位的元素是"当前所在的元素",基于此我们可以直接判断某个交互是否处于该元素内部。接下来,我们需要检查所有执行绘制事件(draw event)的地方,确认这些位置是否需要更新交互对象。

我们需要确保在绘制时所用的交互类型是 element interaction,并且当函数被调用时,需要传入实际关联的元素。这意味着 event interaction 应该不再是通用存在。虽然目前仍有地方调用 event interaction,例如某些临时图像(tear-off bitmap)的处理逻辑中,但从整体来看,我们认为更合理的做法是统一使用 element interaction

于是我们计划将 event interaction 完全废弃,只保留 element interaction,因为在当前架构下,所有交互都与具体的元素绑定。只有以元素为单位来组织交互,才能确保正确识别"hot"状态,否则很容易误判,尤其是在多个重叠元素或层级复杂的 UI 中。

我们继续修改代码,让交互操作统一绑定到具体元素,例如将传递交互的部分改成统一传入 element。同时,在判断某个交互是否为"hot"时,交互对象必须是元素类型。

随后,我们检查热交互存储的机制。记得这个机制比较复杂,因为系统需要追踪各种交互状态、事件来源、UI 层级等信息。我们注意到一个关键点是 most recent event 的存储机制。

当执行某些操作(比如 toggle value 切换布尔值)时,通常默认采用最近一次事件作为参考。这种做法在大多数情况下是合理的,因为用户的最后一个输入就是我们感兴趣的上下文。不过也存在例外场景,需要根据具体的 UI 构造进一步评估。

我们同样在其他地方(例如类似 toggle 操作的位置)进行同步修改,确保他们也使用最近事件的元素交互对象。统一之后,逻辑会更加清晰、稳定,有助于避免意外错误或交互状态错乱。

运行游戏,触发访问冲突。

理论上来说,我们觉得已经完成了所有需要的调整。不过接下来发现一个潜在的问题,就是 event 并不总是一定具备我们预期的那种交互对象结构。比如说,这个 debug interaction 在某些情况下可能并不包含一个标准的 element interaction

换句话说,当前逻辑中默认假设了 event 总是会携带有效的元素交互信息,但实际运行中可能存在某些事件没有附带任何交互对象。我们之前的修改是在这个假设前提下进行的,因此需要重新考虑这种特殊情况的处理。

这一点可能导致在某些路径下 debug interaction 为空或结构不完整,而我们又没有进行足够的判断处理,从而引发潜在的空引用或逻辑错误。因此接下来必须补充保护逻辑,避免在交互不存在的情况下进行访问或比较操作。

这一发现提醒我们,在后续的设计中需要更加严谨地处理交互对象的初始化和传递,确保所有依赖它的模块都能在缺失时做出合理处理,而不是直接崩溃或无反应。进一步来说,可能还需要重新梳理整个事件交互机制的约定和默认行为,明确在无交互时系统应该如何表现。

DEBUGInteract 中有条件地设置事件(Event)。

我们需要增加一个判断条件:只有在目标对象确实存在的情况下,才执行相关操作。否则,在没有交互对象或事件数据的情况下贸然去访问,会导致错误。

具体来说,当我们尝试从事件中获取交互对象时,必须先确认它是否真的存在。如果这个交互数据并没有附带在事件中,那我们就不应该试图从中提取交互信息。我们当前的逻辑默认交互总是存在,这种假设是不安全的。

为了解决这个问题,我们需要在访问之前加一层保护逻辑,比如判断这个对象是否非空、是否包含所需字段,确保在没有有效交互的场景下不会继续执行后续逻辑。否则,可能会访问未定义的属性,导致程序异常或功能失效。

这也意味着系统内部应该明确哪些路径会产生带交互的数据,哪些不会,不能再依赖隐式假设来处理交互状态。今后在处理类似事件数据或交互结构时,必须保持清晰的判断流程和完整性验证,才能保证系统运行稳定。

重新运行游戏,确认高亮功能现在正常工作。

目前来看,已有的交互功能现在基本可以正常工作了,这是一个积极的进展。不过接下来发现另一个问题:拖拽操作似乎没有生效,也就是拖动行为无法触发。

本来我们认为已经实现了相关的拖拽逻辑,因为印象中之前就加过这方面的处理。例如代码中确实存在一些与拖拽相关的模块或方法,也包括用于检测拖拽状态的结构。但现在看来这些逻辑可能没有被正确调用,或者存在某些前提条件没有满足,导致拖拽行为无法响应。

当前的问题很可能是拖拽起始点或者拖拽状态没有被正确识别。例如可能没有正确设置初始点击点、没有追踪鼠标移动过程,或者交互状态根本没有进入"dragging"状态。还有一种可能是事件传递顺序有误,在拖拽开始之前某些数据已经被重置或覆盖。

接下来我们需要重点检查几个部分:

  1. 拖拽相关的逻辑是否被调用;
  2. 拖拽状态是否有被正确初始化并更新;
  3. 是否有其他交互行为干扰了拖拽逻辑,比如提前中断事件处理;
  4. 鼠标位置变化是否被正确追踪,并触发了拖拽事件。

一旦理清了这些逻辑,应该可以恢复拖拽功能。同时也需要确保拖拽逻辑与现有的交互状态系统兼容,例如不能和当前的"hot"状态或"active"状态冲突,确保状态转换清晰明确。

DEBUGInteract 中调用 DEBUGMarkEditedEvent

我们认为,当前的处理方式需要更系统化。之前已经修复过某些功能,也能看到它在局部确实有效,但目前的逻辑仍然比较分散,像 edit eventsevent guid 等相关操作,分布在不同位置,显得混乱冗余。

为了避免类似代码重复出现在各处,我们打算统一整理这些事件编辑相关的操作。我们的目标是创建一个更清晰、更具抽象性的接口,类似于统一的函数,比如 debug add event grants,这个方法的作用是集中处理事件的编辑和记录操作。

通过这种封装方式,任何地方只要调用这个函数,就能完成原本复杂的事件编辑流程。我们可以在触发某个具体行为之后(例如 toggle value 之后),自动调用这个通用函数完成后续的事件标记、状态更新等逻辑。

甚至我们可以将这个方法命名为 DEBUGMarkEditedEvent,让其语义更清晰,表明这是对某个事件进行编辑状态标记的操作。这种做法不仅能提高代码可维护性,还能减少不同模块间的耦合,提高一致性。

总之,这一思路的核心是将目前分散的事件编辑逻辑集中处理,使后续调用更加统一简洁,提升系统稳定性和扩展性。

game_debug.cpp 中实现 DEBUGMarkEditedEvent

首先,计划将新的方法 DEBUGMarkEditedEvent 引入到代码中。这个方法的作用是标记已经编辑的事件。它只需要传递事件本身作为参数,除此之外不需要其他复杂的逻辑。这看起来非常简洁,基本就这样了。

接下来,注意到调试状态下缩进不正常的问题,虽然不知道具体原因,但目前似乎并不影响大部分功能,因此可以先不关注这个问题。

另外,在实现拖拽时,可能需要对拖拽的值进行编辑。如果传入的参数为空(null),则不执行任何操作,这样可以避免不必要的错误或者空操作,确保代码的健壮性。

在进行这些操作时,还考虑到某些情况下需要根据原始guid(original guid)的剩余空间进行一些处理,避免出现"找不到目标"之类的错误。比如,可能是因为卡片的自由区域(free deck)没有被正确设置,导致事件无法正确处理。

为了确保事件的编辑操作正确进行,我们在处理时标记了值,并且在某些状态下(比如拖拽结束后)触发相关事件,最终确保整个流程顺畅执行。

运行游戏,编辑 DebugDistance

现在,我认为当启用调试相机后,应该能够正常进行缩放操作了,看起来一切都很好,功能也在正常运行中。编辑这些值也变得更加流畅,因此这个部分已经没有问题。

接下来,考虑到其他可能启用的功能,想检查一下是否能开启其他一些选项,尽管对于某些操作,我总是忘记点击时会触发某些意外的效果------比如点击时会出现某些不期望的行为,这是我没有预料到的。

game_debug.cpp 中重新启用 tearing(拆分操作)。

首先,现在的目标是确保"tear value"功能正常工作,并查看其当前状态。首先,我们需要理解 tear value 相关的操作和交互是如何实现的。通过调试代码,发现目前这些操作可能还没有完全启用,因此需要查看它们为何被禁用,以及如何激活它们。

在尝试进行一些修改时,注意到目前交互的方式并不完全符合预期,特别是"tear off"操作,并未按照预期返回正确的值或信息。因此,考虑到这一点,我们决定重新组织这部分代码,并确保在正确的地方使用正确的交互方法。

例如,目前在处理调试交互时,传递的是 debug element,但实际上我们希望的是 link interaction。因此,需要调整代码,使得当进行"tear off"操作时,可以正确地将链接交互(link interaction)传递下来,而不是当前的调试元素。

为了让这一过程更加顺畅,我们打算在处理交互的地方设置一个变量,确保当右键点击某个链接时,能够将交互状态正确传递到后续的逻辑处理。为了避免重复的交互逻辑,我们决定将交互的处理部分移到上层,而不是在每个细节中都重新处理。这意味着需要在处理交互前,首先设置好交互状态,然后在调试时进行操作。

此外,在"tear value"相关功能中,如果要正确处理交互状态,可能需要创建一个新的变量组(variable group)。这将确保我们可以正确地将值与相应的交互状态绑定在一起,以便后续的操作能够按照预期进行。

整体来说,当前的工作流程就是整理交互逻辑,确保每个交互步骤(如点击、拖拽等)都能够正确处理,并确保 tear value 操作能够在适当的时机启用。通过改进交互流程和调整逻辑,最终使功能能够顺利运行。

game_debug.cpp 中,在 TearValue 分支中调用 CreateVariableGroup

首先,创建了一个变量组,并使用 debug variable group 来存储相关的数据。这是合理的,因为调试状态需要一个专门的组来管理相关的变量。接下来,思考了如何给这个变量组命名。为了简化,暂时将名称设为一个临时值,并根据需要稍后再进行调整。

在完成命名后,开始将元素添加到变量组中。这部分的逻辑首先是通过 add element to group 操作来实现。接下来决定使用 clone 操作来克隆这个变量组,而不是直接操作原始变量组,这样做可以更好地管理和跟踪变量的状态。

克隆变量组之后,会将相关的数据(例如树结构)添加到新创建的变量组中。然后,考虑到是否需要传递鼠标参数,决定暂时将其传递到相关函数中,以确保操作正确。通过传递鼠标参数,可以更精确地控制交互的细节。

接下来,处理了与移动操作相关的逻辑。在执行这些操作时,检查到"创建变量组"时出现了一个错误,提示"变量标识符未找到"。这可能是由于某些步骤没有正确执行,导致相关信息丢失或没有正确传递。为了解决这个问题,决定逐步检查并确保每个步骤的正确性,尤其是创建和处理变量组的部分。

game_debug.h 中引入 CloneVariableGroup

目前的目标是实现变量组的克隆功能。为此,需要将相关函数引入,包括 create_variable_groupclone_variable_group。克隆函数的基本结构已经初步设定,第一个参数是要克隆的对象,暂时只关注基础的克隆逻辑,不处理更多细节。

接着决定暂时搁置克隆相关的处理逻辑,先执行程序运行以确认当前改动不会造成其他问题。同时也需要构建一个机制来验证克隆是否生效,因此下一步的重点是测试整个交互过程。

基本设想是:当在界面上右键点击某个元素时,应该触发一个克隆操作,然后将克隆得到的变量组移动到一个新的位置。这个交互方式意味着:不仅要能正确地克隆,还需要在视觉和数据结构上表现出变量组的位置变化。为此需要验证以下几点:

  1. 右键点击事件是否被正确捕获;
  2. 触发的变量是否是预期的调试元素;
  3. 克隆函数是否正确生成了一个新的变量组;
  4. 移动逻辑是否能将克隆结果正确呈现在界面上;
  5. 所有状态是否在 debug_state 中被正确注册。

目前阶段仍处于基础验证,主要是确保右键触发可以启动克隆流程,后续将进一步完善克隆操作的细节与可视反馈。

game_debug.cpp 中实现 AltUI 模式下的 TearValue 操作。

在进行 debug_draw_events 绘制事件时,重点是处理交互逻辑的切换。当前交互行为是"展开切换(toggle expansion)",但希望在某些条件下,比如使用替代 UI 时,将交互行为改为"tear value",即拆分或抽取某个值的操作。

为了实现这一目标,采取的做法是:在执行交互之前判断当前是否处于替代 UI 模式,如果是,则不再使用默认的 item_interaction,而是覆盖为一个新的交互类型 tear_value。尽管这个名称理应更准确地称作类似"tech_tree"的东西,但暂时保持原命名以便继续推进逻辑。

为了支持这个新的交互方式,需要将 item_interaction 设置为 debug_link_interaction,传入的参数是当前对应的链接对象。这一步的目的是确保当我们执行 tear_value 操作时,能够获取到准确的变量链接信息。

为此,在设定 debug_id_interaction 时,使用 debug_variable_link 作为实际的参数。这一变量链接将作为 tear_value 的参数传入,从而让后续操作知道要针对哪个变量组执行克隆或者移动操作。

尽管目前还未真正执行克隆操作,但关键是先完成交互结构的搭建,确保交互入口能够正确识别并传递所需的信息。一旦这一流程打通,就能在后续逻辑中顺利加入克隆或重组的处理。目标是确保即使现在还未实现克隆功能,也可以通过传入的变量链接完成基础的交互动作,逐步构建功能体系。

进入调试器,确认调试系统行为与原本一致。

现在开始检查 tear_value 的执行路径,并设置断点以确认是否真正触发了该操作。结果显示,并未成功执行"tear"操作。接着检查交互路径,发现确实成功将当前交互设置为 tear_value,并尝试设为默认交互。

进一步调查发现,问题并不在于交互设置本身,而是由于之前并未实现任何形式的"替代"或"强制交互"逻辑。因此,当右键按下、左键点击时,流程才会生效,这与此前的交互逻辑一致,说明旧的 UI 行为机制仍在正常工作。

确认之后,现在可以通过拖动方式生成新的变量分支,这是预期行为的体现。下一步是实现变量树的克隆功能,一旦完成,就能实现最初的设计目标:基于交互从原树中拆出一棵新树。

当前尚不确定克隆出的变量树是否应该自动显示名称,按理说应该显示,但可能由于这些节点最初并未命名,或者命名逻辑未生效,导致未显示名称。需要进一步确认命名机制是否在克隆后被正确触发,进而决定是否需要对克隆出的变量树补充命名逻辑或显示更新。

总体上,交互路径和触发机制已经打通,拖拽和"tear"功能基本生效,接下来只需完善克隆逻辑和名称显示细节。整个结构正在逐步清晰、完善。

game_debug.cpp 中实现 CloneVariableLink

现在的目标是实现一个完整的变量组克隆功能,基于现有的 clone_variable_groupcreate_variable_group 机制进行处理。为了明确逻辑结构,首先重新理解了 debug_variable_groupdebug_variable_link 的关系:一个变量组是一个标题加一个链表,而变量链接则是链表中的一个节点。

我们需要处理的情况是------当传入一个变量链接(而不是一个完整的变量组)时,是否应仅克隆该单个变量,还是整个变量组。当前倾向于每次都克隆整个组,因为组表示的是树结构的子部分,而不仅是单一节点。

克隆逻辑构思如下:

  1. 传入变量链接作为源对象

    判断其是否属于某个组,如果是,就克隆整个组。

  2. 构建克隆函数

    创建 clone_variable_link() 函数,该函数接收一个链接和目标组,将该链接内容复制到目标组中,并递归处理其子项。

  3. 判断源对象是否包含子结构

    如果这个链接表示的是一个组(即它的 children 字段非空),则:

    • 创建一个新的子组结构;
    • 将其 name 和原组的 children.name 一致;
    • 将所有子链接通过链表方式一个个添加进去。
  4. 链表遍历逻辑

    使用哨兵节点 (sentinel) 和 next 指针遍历子节点链表;

    • 初始化 child = sentinel.next
    • 每次循环中检查 child != sentinel
    • 在循环内递归克隆 child 并添加至新组;
    • 最后继续 child = child.next
  5. 最终结构组织
    clone_variable_group() 函数中创建新的组,调用 clone_variable_link() 将源结构逐层复制进去。形成新的结构树,保留原有组织层级、变量名、子节点等。

这一设计能确保:每次用户执行"tear"操作并试图复制一段变量树时,都能完整复刻其结构与数据内容,包括嵌套子结构,方便后续编辑与分析。

接下来将实现该克隆函数,并进行测试,验证"tear"操作是否在功能上完成了树状结构的复制。整个思路逻辑清晰,遍历、递归和组织方式也已梳理完毕。

我们之所以进行克隆,是因为我们关心的是子节点的存在和结构的完整性。

在这个逻辑中,当我们克隆一个变量链接时,会返回这个链接对象。原因是------虽然我们最终的目的是克隆整个结构,但每次克隆都要以某个具体的子节点为起点,而不能只是创建一个"悬空"的变量链接对象。所以这个返回值是有用的。

虽然整个克隆逻辑看上去略显繁琐,并且这可能不是我们最理想的实现方式,但当前的处理方案是合理的,也没有必要为了"理想形式"而大幅更改已有结构。

结构上这样实现有以下几点理由和效果:

  1. 克隆返回子节点

    每个克隆操作返回的是新建的变量链接(link),这样我们可以方便地将其插入到新的组或树结构中。

  2. 变量链接是独立的节点

    由于变量链接在数据结构中是可独立存在的,因此必须先创建它,再根据需要将其挂载到某个组上。

  3. 维持数据结构一致性

    当前的逻辑虽然不是完全理想,但它确保了变量克隆时的结构一致性,不会出现悬空节点或无法访问的子树。

  4. 暂时保留当前方式

    鉴于该方案能正常工作,也能完成"tear"操作的目的,没有立即更改的必要,因此选择先保留当前写法。

总结来说,这部分的设计重点在于实用性和结构的完整性,即使不是最完美的设计,也能高效完成目标,后续如果有更好的优化方向,可以再逐步调整。

game_debug.cpp 中实现 CloneVariableGroup

我们需要实现的是对变量链接(variable link)以及变量组(variable group)的克隆操作。

首先,对于克隆变量链接,这个操作会复制一个已有的变量链接,并返回新的链接对象。这样我们就可以将克隆出来的链接插入到新的变量组或结构中。

接下来,对于克隆变量组的操作,我们的处理思路如下:

  1. 创建新变量组

    要克隆变量组,首先需要创建一个新的组对象。由于我们当前不知道新的组名具体是什么,暂时命名为 "cloned"。这是一个临时名称,后续可以更改。

  2. 处理字符串持久化

    出于支持热重载的目的,我们不希望这个名字是一个栈上或临时字符串,因为那样在DLL热重载时字符串可能会失效。因此需要将该字符串推入某个字符串池(例如调试状态或调试上下文所管理的),以确保它在热重载时仍然有效。具体做法是将该字符串保存在 debug statesdebug arena 中。

  3. 将变量链接克隆进新组

    创建完变量组后,使用 clone_variable_link 函数将原始链接克隆并添加到这个新组中。

  4. 参数传递与交互处理

    克隆操作需要传入当前的调试状态(debug state)以及需要克隆的链接对象。这些信息一般会作为交互事件的参数传递进来,因此在交互触发时,要确保我们能从中提取出这两个必要的参数。

  5. 链接与元素的绑定关系

    克隆变量链接时,也需要知道其所对应的元素(debug element),否则无法正确地复制其内容结构。这种绑定关系需要在克隆逻辑中保持一致,确保克隆后的链接仍然与正确的数据元素关联。

总结:

  • 克隆变量组的逻辑是:新建组、确保名字字符串持久化、将原始链接克隆添加进去。
  • 克隆变量链接的逻辑是:复制链接结构、返回结果、插入到指定组中。
  • 所有操作需考虑热重载下的资源稳定性,特别是字符串资源的生命周期。
  • 参数来源必须清晰,调试状态与交互传参应正确对应。

整体流程逻辑清晰,重点在于确保克隆出的对象结构一致、内存安全,并兼容系统的热重载机制。

运行游戏,触发访问冲突。

当前的目标是完成对变量组的克隆功能,但在调试过程中发现克隆后的结果无法正常工作,具体问题和分析如下:


问题现象

克隆变量组之后,操作崩溃或出现非法内存读取错误,调试信息提示在访问 element 时出错,显示读取位置无效。这说明克隆后的结构在某处存在指针错误或未正确初始化的数据。


分析过程

  1. 原始判断:以为数据没被复制

    最初认为出错的原因是我们在 add_element_to_group 的时候没有复制变量数据本身。但很快意识到这其实不是问题,因为:

    • debug_variable_link 本身只是一个包含指向 debug_variable_element 的指针的结构。
    • 也就是说,克隆时只需要拷贝链接(link)即可,不需要拷贝 element,除非 element 是临时的或者未正确初始化。
  2. 假设:可能是 element 指针无效

    鉴于 link 本身只是一个包装,真正出问题的可能是它内部指向的 element 指针在克隆过程中变成了悬空指针(野指针)或根本就没有被设置。

  3. 验证:检查指针是否为合法引用

    需要确认在 tear_value 或其他交互执行时,传入的 debug_variable_linkelement 字段是否有效,即该内存位置是否还处于有效生命周期,且未被释放或被覆盖。

  4. 进一步检查:交互处理流程

    查看 interaction_tear_value 时,确实是通过热交互传入的 debug_variable_link,因此应该有 element 指针。检查时确认其确实不是 variable_link 类型,而是 debug_link 或其他派生结构,可能存在结构不匹配的问题。


后续行动

  1. 确认 link 与 element 的创建流程

    仔细确认:

    • link 是不是由合法的 element 构造而来。
    • element 在克隆后是否仍存在于有效内存。
    • 是否在 debug state 或 arena 中正确分配并管理。
  2. 验证 clone_variable_link 返回值的合法性

    具体检查:

    • 克隆出的 link 是否真实地持有指向 element 的有效指针。
    • 如果 element 是某种子结构,是否已提前创建好。
  3. 验证交互参数的准确性
    hot_interaction 是否正确传入了指向的 link,而不是结构错误或未初始化的对象。

  4. 去除不必要的字符串传递

    注意到在过程中涉及到临时字符串的处理,比如在克隆中对组名使用 "cloned"。为了避免热重载中字符串被释放的问题,这部分已妥善使用 debug_state 或 debug_arena 处理,现在可移除临时调试输出。


总结

  • 问题本质:克隆后变量链接中的指针在运行时无效,可能为悬空指针或未初始化。
  • 关键检查点:变量链接是否来自合法 element、element 是否在生命周期内、交互参数是否准确。
  • 后续优化:严格验证指针引用和数据结构一致性,确保热交互中的参数类型正确。

通过以上验证和修复,可确保克隆功能在热交互场景中正确运行且不会触发内存非法访问问题。

在调试器中进入 CloneVariableGroup,检查结果对象(Result)。

当前的调试过程聚焦在变量组(variable group)克隆的实现上,特别是在递归克隆子节点结构时出现的问题。主要内容和分析如下:


克隆流程回顾

  1. 创建变量组 :调用了 create_variable_group,并为其命名。
  2. 克隆变量链接 :通过 clone_variable_link 来递归地将原始组中的每个子项复制到新组中。
  3. 子结构遍历 :从源组的 children 链表中遍历每一个子节点,通过 add_element_to_group 添加到新的组中。

遇到的问题

  1. 克隆后结构看起来完整:组名、哨兵(sentinel)、子链表结构等都存在。
  2. 但子节点的 element 总是为零
    • 初看觉得克隆过程中元素都为空,感觉不合理。
    • 检查后发现遍历过程中看到的是哨兵节点(sentinel),本身就没有元素,这是误解。
    • 实际子节点的 next 指针指向的对象才是真正的子项,它们应当包含有效的 element 指针。

根因分析

最终定位问题出在子组结构没有被正确设置。具体分析如下:

  • 在克隆过程中,对于每一个变量链接(link),如果其代表的是一个组(group),会创建一个子组来承接其内容。
  • 然后对原始组的子节点进行遍历并逐一克隆加入新子组。
  • 关键问题在于:虽然正确创建了子组并遍历克隆了子元素,但没有把这个子组设置为当前节点的 element 成员,也就是说结构被创建但未绑定。

当前状态分析

  • 变量组本身是成功创建的。
  • 哨兵节点是有效的,子节点列表存在。
  • 子节点中的 element 值未设置的根因是代码中没有将新创建的子组赋值到其所在的父节点 element 字段。
  • 因此,从外部看似组结构完整,但实际上丢失了关联,逻辑无法正常工作。

后续处理建议

  1. 修复子组绑定问题

    在创建子组之后,必须将其设置到当前节点的 element 字段,确保结构完整。例如:

    c 复制代码
    current_link->element = newly_created_subgroup;
  2. 验证结构完整性

    • 每一个 variable_linkelement 应指向合法的 element 或者子组。
    • 克隆完成后,打印或调试查看各节点的 element 指针是否有效。
  3. 确保内存生命周期一致

    新创建的子组和其包含的数据结构应该注册在 debug_statearena 中,确保在热重载等动态场景下不会失效。


总结

  • 问题本质:克隆过程中创建了结构但未正确连接子组,导致逻辑结构不完整。
  • 关键修复点:确保每个子组在创建后被设置为其上层节点的 element
  • 当前结构基本正确,只需要补齐连接逻辑即可恢复预期功能。

我们在执行 clone_variable_link 时,意识到一直以来遗漏了一个关键步骤 ------ 正确设置克隆结果中的子组结构。


当前逻辑回顾

  1. 克隆变量链接clone_variable_link):

    • 遍历源组的子节点;
    • 使用 add_element_to_group 将每个子节点添加到新建的变量组中;
    • 如果源节点本身是一个子组,也会递归地创建对应的子组。
  2. 遗漏关键逻辑点

    • 克隆后的结构虽然通过链表形式添加了子节点;
    • 但是没有把新创建的子组指派到克隆后节点的 children 字段上。

正确做法

  • 在处理某个节点为组(group)的情况下,应当:

    c 复制代码
    def->children = subgroup;
  • def 是克隆过程中当前创建的目标节点;

  • subgroup 是针对源节点的 children 克隆生成的新子组;

  • 如果不设置这个字段,虽然子节点结构存在,但与父节点之间的结构连接断裂,外部代码无法正确遍历该层级。


调整结果

  • 通过将 subgroup 设置为 def->children,可以保证结构的完整性和可遍历性;
  • 修复后,整个变量组树状结构能够被正确重构和显示;
  • 这也是一直以来逻辑不对、变量组显示不完整的根本原因。

总结

我们在克隆变量链接过程中,必须正确地将新建的子组绑定到目标节点的 children 字段上。此前没有这么做,导致结构看似构建成功,实则信息丢失。现在通过修复这个绑定逻辑,克隆功能将能够完整而正确地重建整个变量组结构。

运行游戏并进行元素"撕裂"操作。

目前克隆功能基本已经调通,现阶段的系统状态良好,我们完成了如下关键点,并明确了接下来的优化方向:


当前进展与功能状态

  1. 克隆与抽取子结构能力基本稳定

    已经能够从主结构中独立地抽出子结构,包括单个元素与部分子树,并保持其独立性与可操作性。

  2. UI 状态隔离良好

    每个克隆出来的子结构都能维持自身的 UI 状态,而不会干扰或共享其他结构的状态。

    • 如果某个变量组未被交互或查看,就不会占用任何 UI 状态内存;
    • 真正实现了"按需分配"与"懒加载"的机制。
  3. 交互反馈准确

    • 拖拽实体或变量组到新的位置时,系统能准确识别并渲染;
    • 高亮反馈也准确无误,能正确响应当前用户操作的是哪个元素;
    • 即使 UI 样式上略有瑕疵(例如部分小图标不可见),功能逻辑上并无问题。

可视化方面的待办事项

  1. 渲染优化

    • 目前图标、控件的可见性存在瑕疵,某些小图标虽然存在但视觉上难以辨认;
    • 需要进一步优化渲染逻辑,增强视觉反馈,让用户能更直观感知系统状态。
  2. 位图显示问题

    • 位图渲染未生效,图像资源未正确加载或绘制;
    • 需要检查位图加载流程与绘制层级,确保图像元素可见且布局合理。
  3. 信息固定机制优化

    • 已实现将关键值"固定"在界面上的能力,例如某些重要值可以"钉住"在视图内,不必频繁翻找;
    • 接下来可进一步改进这一机制,使得其更易用并可支持更多种类的数据项固定。

整体评估

  • 当前系统的功能结构与行为表现已趋于稳定,尤其是在变量组的克隆、UI 状态隔离、交互反馈这几方面;
  • 接下来的重点是优化视觉呈现,解决渲染细节问题,使得整体验更加直观和流畅;
  • 同时,为后续更复杂交互逻辑和调试机制打下了坚实的基础。

小结

功能部分已进入收尾阶段,当前重点转向渲染与用户体验优化。基础架构上,克隆机制与状态管理已具备良好可扩展性,后续只需局部修整即可上线更多场景支持。整体系统逻辑清晰,性能高效,状态管理自洽,是一次成功的系统构建阶段性成果。

这个要改成debug_variable_group *RootGroup = CloneVariableGroup(DebugState, DebugState->HotInteraction.Link);

Q&A

如果你尝试修改一个动态变量,比如玩家的位置,会发生什么?

当前遇到的问题是动态变量(例如玩家位置)修改时,系统缺乏合适的编辑器来处理特定类型的数据,比如向量。以下是处理情况的总结:

  1. 修改动态变量的影响:

    • 如果尝试修改像玩家位置这样的动态变量,系统会直接覆盖其当前值。对于位置这类变量,系统没有特殊的处理逻辑,只是简单地进行赋值操作。
    • 如果在游戏中尝试修改玩家的"朝向"变量,实际操作效果能够直接看到,意味着系统允许修改并应用这些值。但目前没有专门的编辑器界面来管理这类向量类型的数据。
  2. 缺乏向量编辑器:

    • 系统目前没有针对向量(例如玩家位置或朝向)类型变量的编辑器。这意味着,虽然系统能够接受这些值的修改,用户界面并没有提供直观的方式来编辑这些变量。
    • 这主要是因为缺少相应的编辑工具,例如向量编辑器。通过这种编辑器,用户应该能直接对坐标、方向等向量数据进行修改,而不需要通过其他复杂的操作。
  3. 现有的功能:

    • 虽然没有专门的编辑器,用户仍然可以手动更改这些变量并看到实际效果。系统能够正确地接受修改并更新玩家位置或朝向等值。
    • 但是,由于缺少向量编辑器,这种操作在使用体验上存在不便。当前的解决方案更多是通过手动输入或其它方式修改变量,而不是通过专门的UI进行直观操作。
  4. 改进方向:

    • 未来需要开发向量编辑器,允许用户以更友好的方式编辑这些动态变量,尤其是像玩家位置、朝向等重要变量。
    • 这个编辑器应该能够让用户更直观地操作数据,而不是依赖于简单的数值输入或调整,提升交互体验和效率。

总之,当前系统能够处理动态变量的修改,但缺乏合适的工具来管理向量类型的数据,导致用户体验不够理想。

CloneVariableGroup() 中使用了 sizeof(Name) - 1,这应该是字符串长度而不是指针大小减一吧?

在"clone variable group"中,关于"nameless one"的大小问题,需要确认是否应该使用字符串的长度,而不是指针大小减去一。具体来说,问题是是否应基于字符串的实际字符长度来计算,而不是仅仅依据指针的大小(即指针的内存占用)。如果是字符串,计算长度时应考虑字符串的实际字符数,而不是指针所占的内存空间。这种计算方式可以避免一些潜在的错误,尤其是在处理不同类型的数据时,指针大小可能与实际数据长度不匹配。因此,这里需要确认的是,是否应该使用字符串的字符数长度,而不是指针的大小减一来表示字符串的实际长度。

CloneVariableGroup 中传入正确的长度计算值给 CreateVariableGroup

在之前的代码中,确实没有修复一些问题,特别是在获取字符串长度方面。我们发现有办法获取字符串推送的长度,但遗憾的是没有做到这一点。因为一开始就已经知道字符串的长度,所以再次调用相同的操作就显得有些浪费。现在的问题是纹理下载的问题,纹理在运行时并没有正确下载。希望通过改变为单线程处理,能够解决这个问题,因为在视频处理中,多个线程并没有得到优化,未来会继续研究这个问题。

目前看不出任何改动的效果,因为我们并没有真正使用这个名字。如果使用了这个名字,问题应该能够得到解决,但目前因为没有在代码中应用,所以无法观察到变化。

相关推荐
爱吃涮毛肚的肥肥(暂时吃不了版)1 小时前
仿腾讯会议——注册登录UI
开发语言·c++·面试·职场和发展·腾讯会议
Dante7981 小时前
【多源01BFS】Codeforce:Three States
c++·算法·bfs
s_little_monster1 小时前
【Linux】线程池和线程补充内容
linux·运维·服务器·c++·笔记·学习·学习方法
Timmer丿2 小时前
kafka学习笔记(四、生产者(客户端)深入研究(二)——消费者协调器与_consumer_offsets剖析)
笔记·学习·kafka
李匠20243 小时前
C++负载均衡远程调用学习之Reactor事件触发机制
c++·学习
冰茶_3 小时前
WPF之Image控件详解
学习·microsoft·微软·c#·wpf·wpf控件
legend_jz3 小时前
算法--模拟题目
数据结构·c++·算法
子豪-中国机器人3 小时前
第 12 届蓝桥杯 C++ 青少组中 / 高级组省赛 2021 年 4 月 24 日真题
c++
搏博3 小时前
结构模式识别理论与方法
人工智能·深度学习·学习·算法·机器学习
z35026037064 小时前
K8S学习笔记01
笔记·学习·kubernetes