游戏引擎学习第196天

发布于:2025-04-01 ⋅ 阅读:(23) ⋅ 点赞:(0)

总结并为今天的内容做铺垫

这里我们正在进行实时编写完整游戏的展示。目前我们正在编写一些调试界面的代码,今天我想谈谈的内容是,我们的游戏中并没有做太多的操作,实际上,基于游戏的性质,我们大部分时间可能不会在游戏中做太多的交互,因为它涉及到焦点或UI交互的概念。

之所以这样,是因为这是一款动作游戏,我希望游戏能提供非常直接的控制体验。我不希望游戏中有很多复杂的模式或UI设计方面的元素。对于我来说,游戏中的交互应该是直接的,让玩家直接与角色进行控制并开始游戏。

因此,我们的游戏大概不会涉及到太多UI设计上的思考,因为这与游戏的核心玩法无关。然而,在调试系统中,我想借此机会引入一些UI设计中的思考,因为调试系统本身具有某些操作模式,我们可以利用这些模式来讨论一些UI编程中相关的问题。

我特别指的是像鼠标移动、点击某个东西、松开鼠标按钮来切换一个布尔值(比如一个单选按钮或复选框)。如果我按住那个按钮并拖动它,可能我会把它从一个地方移到另一个地方。这个交互模型的关键在于,点击操作并不只是执行一个单一的操作,它可能会引发一系列的后续动作,用户的后续操作决定了实际会发生什么。

在游戏中,尤其是动作游戏里,这种互动通常会在时间的推移中发生,通过交互的对象触发一系列事件。例如,我击打一个桶,它爆炸并将某个物品弹飞。这些事件的触发是游戏代码的一部分,而不是用户直接输入的结果。而在UI编程中,交互通常是在用户的输入模型内,意味着系统要根据用户的操作来理解他们实际想要做什么。

我想借此机会简要介绍一下这些思考,虽然这并不会在游戏本身或以后的开发中深入探讨,但它是一个重要的编程话题,我觉得有必要和大家分享。因此,我先来简单回顾一下当前的情况,展示我们目前的进展。

如果运行当前的代码,大家可以看到我们已经开始制作一些调试输出功能,其中一些东西可以像这样展开或折叠。虽然目前做得并不复杂,但这是我们当前的状态。某些调试项甚至有不同类型的值,例如一个可能会有值范围的变量,而我们目前还没有实现与这些值的交互方式。

目前,点击操作只能触发布尔值的切换或展开树形菜单项,但如果是其他类型的变量,比如带有数值范围的变量,我们没有提供任何交互功能。因此,问题的根源在于我们没有讨论如何处理用户交互或类似的功能。我们仅仅是能做一个简单的点击操作。

接下来,我打算谈一谈结构方面的内容,并开始实现一些功能,使得调试系统能够更好地处理交互。尽管我们不会在游戏中深入使用这些功能,但这是一个很好的教育机会,能够让大家了解这些概念。希望大家对这些内容能有一个合理的理解。

Blackboard:UI 输入处理

主要讨论的是UI输入处理。大家之前看到我做过一些非常基础的输入处理,这种处理方式通常与IMGUI风格相关。很多人提到IMGUI时,会将其与“单次执行模型”相关联,而我指的“单次执行模型”是指,例如,假设我们有一个复选框,并且复选框旁边有一个功能。如果我点击复选框,它就会被选中,再次点击时就取消选中,这种交互模型很常见。

在IMGUI中,通常的做法是直接调用复选框的处理函数,里面会检查鼠标是否点击到该区域,如果点击了,就切换复选框的状态。这是实现这一交互的最基本方式。看起来非常直接和简单。

然而,这种方式有一个问题,就是它将交互与屏幕上的具体元素绑定在了一起,导致了灵活性问题。例如,如果我有一个选择工具,我希望能够选择屏幕上的一个对象,而当我切换到复选框工具时,我希望能够点击复选框并勾选它。如果这种操作需要依赖于具体的工具,那就意味着每一个可能的工具或行为都需要嵌入到复选框的处理代码中。这就导致了一个问题:实现复选框处理的代码必须知道所有可能发生的事情。

简单来说,所有的交互都必须放在这个复选框的处理函数内部,而不能独立处理,这样就让交互的管理变得很难。如果我想要实现一个不同的行为,像是选择或取消选择复选框,所有的处理逻辑都需要在复选框的代码内部实现,不能将其外部化。这种做法显得不够灵活,因为每次要实现新的交互时,都需要重新修改复选框的处理函数。

我想再次指出的是,这种实现方式并不代表IMGUI,它只是一种实现交互处理的简单方式,与是否是IMGUI并无直接关系。IMGUI的“即时模式”本质上只是在于代码是否能直接声明需要展示的内容,而不必保存任何指针或者调用专门的创建函数来生成某个UI元素。代码只是被动地宣布它要显示的东西,而如何处理这些内容则完全可以通过独立的代码进行定义。

总结来说,IMGUI的即时模式并不要求一定要将所有交互逻辑绑定在每个UI元素的处理函数中。UI元素的处理与具体的交互工具是可以解耦的。大多数人对IMGUI的理解可能有偏差,他们会把它理解为所有交互都必须在UI元素的处理代码内部完成,但其实这并不是IMGUI的核心思想。

game_debug.cpp:解释 IMGUI 代码的结构

在这段时间里,重点是展示如何在调试界面(debug UI)中开发代码,并通过合适的代码结构来避免将所有操作与界面元素绑定在一起。通过这种方式,可以更清晰地组织代码,使其更加灵活。

举个例子,当前调试界面有一些“调试配置文件”的输出信息显示在屏幕上。如果点击鼠标左键,它会识别你点击的是哪个配置文件并对其进行相应的操作。在实现这一功能时,代码结构是“即时模式”(immediate mode),意味着它会遍历所有的计数器并对它们进行操作,但是它并不会在进行这些操作时立即处理UI交互。相反,它只是记录下当前交互的对象,并等到最后才决定执行什么样的UI操作。最终,UI操作将会根据之前记录的交互信息来决定执行的目标。

这个设计的关键在于,它将处理UI元素的逻辑与UI元素本身的绘制逻辑解耦。换句话说,实际的UI交互逻辑和绘制显示UI元素的代码是分开的。这种分离使得系统在处理UI时更加灵活,因为你可以更容易地在不同的UI元素之间实现通用的交互,而不需要每次都在UI元素的处理代码内部实现所有交互的细节。

例如,如果直接在代码中就处理鼠标点击事件,可能会让每个UI元素处理自己的交互和显示内容,但这种做法并不推荐,因为它让交互逻辑变得高度依赖具体的UI元素本身。而通过分离UI绘制和交互处理,你可以在UI元素绘制后,将它的交互逻辑单独处理,并根据需要实现更复杂的交互模式。这样可以更容易扩展系统,增加新的交互方式。

总体而言,这种结构使得代码更具可扩展性和灵活性,避免了交互逻辑与UI元素绑定过紧的情况,能够为未来的UI元素和交互方式提供更多的可能性。

game_debug.h:介绍 *Hot、*InteractingWith 和 *NextHot 调试变量

在继续开发调试界面时,接下来会进一步改进操作系统的结构。首先,目标是引入“操作发生的位置”和“操作本身”这两个概念,从而可以对浮动变量或其他交互元素进行操作。

在实现过程中,将保持之前的方法:假设DrawDebugMainMenu函数会提供一个信息,该信息指出当前鼠标悬停的位置。虽然这不一定每次都使用,但它是一个重要的信息。当前,代码中已经有了一个hot variable(热点变量),它会通过DrawDebugMainMenu被设置。在这里,当检测到鼠标点击时,就会对hot variable进行相应的操作。接下来,目标是将系统从单纯的热点变量扩展到包括互动变量。

将引入两个变量:hot variableinteracting variable,这两个变量将成为UI系统的基础。hot variableDrawDebugMainMenu提供,负责标记当前鼠标悬停的元素,而interacting variable则用于标记当前正在与之交互的元素。这两个变量将有助于界面交互的进一步处理。

在代码实现中,首先会对这些变量进行初始化和清空,确保每次都能得到准确的信息。然后,通过一系列操作,这些变量会被更新。每个操作都会有机会修改hot variable,这为后续的UI操作提供了依据。最后,代码会根据这些信息决定具体要执行什么操作。

总体来说,新的设计目标是让UI交互系统更加系统化,能够处理更复杂的交互逻辑,同时保持代码的灵活性和可扩展性。这些改进不仅会增强UI的交互性,还能提供更多的操作方式。
在这里插入图片描述

在这里插入图片描述

game_debug.cpp:介绍 DEBUGInteract 并通过这些变量逐步构建

我们在开发调试 UI 的过程中,希望改进现有的交互方式,使其更系统化,并提供更好的控制逻辑。目前的调试 UI 采用了一种即时模式(immediate mode)的方式,其中我们根据鼠标悬停的位置,确定当前交互的变量,并在鼠标点击时对其执行操作。现在,我们希望进一步改进这种方式,引入更加明确的交互状态管理机制。

交互逻辑改进

  1. 交互状态的引入

    • 现有逻辑主要基于 hot variable(当前鼠标悬停的变量),在鼠标点击后直接执行操作。
    • 现在,我们增加一个 interacting variable(正在交互的变量),以明确交互的状态。
    • hot variable 代表鼠标当前悬停的 UI 元素,而 interacting variable 代表正在进行交互的变量。
  2. 状态管理机制

    • 在 UI 逻辑执行的起始阶段,清空 hot variableinteracting variable,确保它们是干净的状态。
    • 通过 draw_debug_menu 确定鼠标悬停的 hot variable
    • 交互逻辑改进如下:
      • 如果没有正在交互的变量,鼠标悬停的变量成为新的 hot variable
      • 如果鼠标在某个变量上按下(went_down),该变量变成 interacting variable,进入交互状态。
      • 如果鼠标松开(went_up),交互结束,interacting variable 归零。
  3. 输入处理

    • 引入 went_downwent_upclicked 三种输入状态:
      • went_down:鼠标按下但未松开,表示开始交互。
      • went_up:鼠标松开,表示交互结束。
      • clicked:鼠标按下和松开都发生在同一帧,表示单次点击操作。

代码结构调整

  1. 抽取交互处理逻辑

    • 现有逻辑分散在多个部分,我们将交互处理逻辑抽取到 debug_interact 函数中,使代码更加清晰、模块化。
    • debug_interact 负责:
      • 处理鼠标点击交互逻辑。
      • 维护 hot variableinteracting variable 的状态。
      • 通过 debug_text_line 输出调试信息,显示当前的交互状态。
  2. 改进交互处理

    • 鼠标悬停时更新 hot variable,但不会立刻影响 interacting variable
    • 只有鼠标按下时,hot variable 才会变成 interacting variable,进入交互状态。
    • 鼠标松开时,interacting variable 归零,表示交互结束。
  3. 提高代码的可扩展性

    • 通过 interacting variable 机制,我们可以更容易地扩展 UI 交互,例如:
      • 拖拽滑块进行调整。
      • 右键菜单交互。
      • 复杂 UI 组件(如文本输入框)交互。

总结

我们正在逐步改进 UI 交互的实现方式,从单纯的 hot variable 处理,转向更加系统化的 interacting variable 机制。这样可以更灵活地管理交互状态,使 UI 代码更加清晰和可扩展,同时提供更丰富的交互方式,例如拖拽、滑动、菜单等功能。
在这里插入图片描述

在这里插入图片描述

运行游戏并查看发生了什么

我们现在测试新的交互逻辑,观察其行为。

  1. 鼠标按下的交互效果

    • 当鼠标按下时,interacting variable 被赋值,表示进入交互状态。
    • 这一变化可以通过调试文本输出观察到,interacting 状态被激活。
  2. 鼠标拖动的行为

    • 在交互状态下,鼠标移动不会影响 hot variable,因为当前已经锁定 interacting variable
    • 这种行为确保了 UI 交互的连续性,不会因鼠标的悬停变更导致交互中断。
  3. 鼠标释放后的状态恢复

    • 当鼠标松开时,interacting variable 归零,交互结束。
    • hot variable 恢复为当前鼠标悬停的 UI 元素,表示可以进行新的交互。
  4. 交互状态的可视化验证

    • 通过调试文本,我们可以实时查看 interacting variable 何时被激活、如何跟随鼠标移动、何时被释放。
    • 这有助于确认交互逻辑是否按照预期工作,并找出潜在的异常情况。

下一步,我们将基于这个交互基础,进一步扩展 UI 交互功能,例如支持滑动调整数值、拖拽操作等,使交互逻辑更加完整和灵活。

game_debug.cpp:确定我们正在与哪个变量进行交互

我们现在对 UI 交互逻辑进行进一步优化,以避免在拖动交互过程中出现错误的高亮显示。

  1. 现有问题:拖动时错误高亮

    • 之前的逻辑中,鼠标拖动时 hot variable 仍然会根据 IsInRectangle 进行更新,导致鼠标经过的其他 UI 元素被错误地高亮。
    • 这种行为会影响用户体验,因为它不应该在拖动过程中高亮其他 UI 元素,而是应该保持当前交互的元素高亮。
  2. 优化方案:解耦 UI 逻辑

    • 交互过程中,hot variable 不再由 IsInRectangle 直接更新,而是完全由 interacting variable 控制。
    • 具体来说,当交互开始时,我们将 hot variable 锁定为 interacting variable,确保交互元素在整个拖动过程中保持高亮。
    • 只有当交互结束(鼠标释放)时,hot variable 才会重新允许更新,以反映鼠标悬停的最新 UI 元素。
  3. 优化后的交互流程

    • 按下鼠标:如果鼠标点击了某个 UI 元素,该元素被锁定为 interacting variable,同时 hot variable 也同步更新。
    • 拖动鼠标:即使鼠标经过其他 UI 元素,hot variable 仍然保持 interacting variable,不会高亮其他 UI 元素。
    • 释放鼠标:交互结束,interacting variable 清空,hot variable 重新回到 IsInRectangle 控制,正常更新鼠标悬停状态。
  4. 优化的好处

    • 交互稳定性提高:不会因为鼠标拖动时经过其他 UI 元素而错误地高亮它们。
    • 代码解耦:将 UI 交互逻辑与绘制逻辑分开,后续更改交互行为时无需修改绘制代码,提高可维护性。
    • 更灵活的 UI 逻辑:未来可以更方便地扩展,如添加拖拽调整数值、交互状态变化等功能。

通过这一优化,我们让 UI 交互变得更加直观、稳定,同时保持代码的灵活性和可扩展性。接下来,我们可以基于这个逻辑,进一步完善其他交互功能,例如数值输入、滑块拖动等。
在这里插入图片描述

game_debug.cpp:介绍 DEBUGBeginInteract 和 DEBUGEndInteract

我们现在进一步优化交互逻辑,特别是处理鼠标点击和交互的细节,使得 UI 交互更加稳定和可控。

  1. 目标

    • 处理在一个帧内完成的完整点击(按下和释放)。
    • 允许处理多个快速点击(例如用户快速点击鼠标的情况)。
    • 处理持续交互的情况(如用户长按交互对象)。
  2. 方法:拆分交互逻辑

    • begin_interact:表示交互的开始,即鼠标按下。
    • end_interact:表示交互的结束,即鼠标释放。
    • process_clicks:处理所有发生的点击,并确保点击事件正确传播。
  3. 处理快速点击

    • 通过 half_transition_count 计算鼠标在当前帧内的按下和释放次数。
    • 如果在一个帧内有完整的按下和释放(即完整点击),就触发 begin_interactend_interact
    • 如果 half_transition_count > 1,意味着用户在一帧内多次点击,此时需要处理所有点击,确保不会遗漏事件。
  4. 优化鼠标输入处理

    • 记录当前帧鼠标状态,并遍历 half_transition_count 处理所有点击。
    • 确保点击事件按照顺序执行,以免遗漏或错误触发。
    • 在鼠标按下时,触发 begin_interact,让 UI 进入交互状态。
    • 在鼠标释放时,触发 end_interact,让 UI 退出交互状态。
  5. 考虑持续交互

    • 如果用户持续按住交互对象,确保 interacting_variable 保持不变,不会因鼠标移动导致状态丢失。
    • 只有在鼠标释放时才会清除 interacting_variable,保证交互完整性。
  6. 优化的好处

    • 更准确的点击检测:不会遗漏用户的快速点击,同时保证点击事件顺序正确。
    • 支持快速点击和持续交互:无论是单击、双击,还是长按,都能正确处理。
    • 提高 UI 交互稳定性:防止因错误的鼠标状态更新导致 UI 交互异常。

通过这些优化,我们的 UI 交互逻辑更加清晰、稳定,并且能够更好地适应不同的用户输入模式,如单击、双击、长按等,提升整体用户体验。
在这里插入图片描述

game_debug.cpp:实现修改调试变量的能力

现在我们进一步完善交互逻辑,特别是鼠标移动(mousemove)时的交互处理,使交互更加直观和可控。

目标

  1. 跟踪鼠标移动:确保每次鼠标移动时都能正确检测并处理交互。
  2. 优化鼠标移动的顺序:决定是先处理鼠标移动还是先处理点击。
  3. 确保交互对象正确:当鼠标拖拽某个对象时,保证只影响该对象的值。
  4. 计算鼠标移动的增量(Delta):根据鼠标位移调整交互变量的值。

实现方案

1. 处理鼠标移动事件
  • 设定 debug_move_interact 处理鼠标移动逻辑。
  • 判断是否有交互对象:如果当前没有交互对象,不做任何处理。
  • 如果有交互对象:根据鼠标移动量调整对象的值。
2. 交互对象的检测
  • 只处理可交互的对象,例如 real32 类型的变量(即浮点数类型)。
  • 提取交互变量:将当前交互变量存入一个更简洁的变量名,方便后续操作。
  • 只修改 real32 类型变量,确保不会影响其他类型的数据。
3. 计算鼠标移动增量
  • 获取当前鼠标位置
  • 计算与上一帧的鼠标位置差值(Delta)
  • 应用增量调整交互变量的值
    • 设定一个缩放系数(比如 0.1),用于调节鼠标移动对变量的影响程度。
    • 计算 鼠标增量 * 缩放系数,然后叠加到交互变量上。
4. 记录鼠标上次位置
  • 存储当前帧鼠标位置,以便下一帧计算增量。
  • 更新 last_mouse_pos 变量,保证后续计算的正确性。

优化后交互逻辑的优势

  1. 更精确的鼠标拖动:鼠标移动时,变量变化平滑,避免跳变或抖动。
  2. 更好的交互体验:拖拽交互对象时,不会意外影响其他元素。
  3. 灵活的事件顺序:可自由选择先处理鼠标移动或鼠标点击,增强可控性。
  4. 扩展性更好:后续可增加其他交互类型(如旋转、缩放)而不会影响当前逻辑。

通过这次优化,鼠标移动时的交互变得更加自然,能够准确反映用户的操作,提高整体交互体验。
在这里插入图片描述

在这里插入图片描述

运行游戏并查看当前进展

在进一步完善交互功能的过程中,需要实现一个关键功能:布尔值处理。这样才能正确地展开并访问交互对象的实际值(real value)。

目标

  1. 恢复布尔值处理逻辑,确保交互对象能正确展开和关闭。
  2. 确保交互状态正确更新,保证点击操作能够正确切换状态。

实现方案

1. 处理布尔值状态
  • 检测当前对象是否为布尔值类型
  • 如果是布尔值
    • 处理它的点击交互,使其可以展开或折叠。
    • 记录状态,确保每次交互时能正确反映当前的开关状态。
  • 如果不是布尔值,继续正常的交互处理,不影响其他对象。
2. 交互逻辑调整
  • 点击布尔值对象
    • 触发展开或折叠。
    • 只有在交互结束时(鼠标释放)才会最终确认展开/折叠状态,防止误触导致频繁切换。
  • 鼠标移动交互
    • 如果交互的是数值对象(如 real32),继续调整数值。
    • 布尔值不受鼠标移动影响,只受点击影响,避免误修改。
3. 状态存储
  • 为布尔值对象存储展开状态,保证在下次交互时能正确读取状态。
  • 记录交互历史,确保展开/折叠行为能正确回溯,不会出现状态错乱。

优化后的优势

  1. 支持布尔值交互:能够正确处理展开/折叠操作,使交互界面更加直观。
  2. 避免误触和状态错乱:确保点击布尔值时只会切换一次,防止因误操作导致界面混乱。
  3. 兼容其他交互逻辑:不影响数值拖拽等其他交互,保证整个系统稳定运行。

通过这次优化,我们成功地实现了布尔值的交互,使系统能够正确展开和折叠对象,进一步增强了交互体验。

game_debug.cpp:重新实现展开/折叠树形菜单的功能

在交互结束时,我们希望执行之前的操作。交互结束时,我们已经点击了某个对象,因此我们需要处理与之相关的变量。

首先,我们检查是否存在交互变量(interacting variable)。如果存在,我们提取该变量,检查其类型,并根据类型执行相应的操作。执行完操作后,我们清除该变量,以确保我们不再与其交互。

当前的实现允许我们打开对象,并且可以看到这一过程的效果。接下来,我们尝试与其中某些对象交互,并希望更改该变量。同时,我们需要修改调试功能 end interact,允许不需要交互的内容正常通过,否则会导致错误。

如果不进行此修改,程序会持续报错。因此,我们需要确保在交互结束时,对应的内容能够正确传递,而不会被阻拦。
在这里插入图片描述

运行游戏并注意到与交互相关的有趣情况

当前的系统中,我们可以修改调试相机的一些变量,调整视角或缩放。但有一个问题是,这些值的更新需要通过编译步骤来完成,每次更新后都必须等待编译器完成工作,这个过程较慢。如果能够让这些值在运行时实时更新,将大大提高效率,不需要每次修改后都进行编译。这是一个有待改进的地方。

此外,在当前的交互系统中存在一些不太合理的地方。例如,当点击某个区域并拖动时,如果鼠标没有与任何可交互的对象接触,就无法进入交互状态。这种情况下,系统会认为鼠标按下的事件没有开始交互,但实际上我们是希望能够继续进行交互操作。更具体地说,当鼠标按下并移动时,系统应能够识别出这个动作并启动交互,而不是仅仅与第一个接触到的物体进行交互,这样的设计不太符合预期的交互流程。

这些问题表明,系统的交互逻辑还需要进一步改进,尤其是在点击和拖动等操作的响应机制上,以提高交互的流畅度和直观性。
在这里插入图片描述

game_debug.h:介绍 debug_interaction

为了实现交互状态的管理,需要引入一个与实际交互对象分开的交互状态的概念。为此,可以使用枚举类型来表示不同的交互状态。这样做是为了确保交互的状态和实际的操作对象是独立的,方便后续的管理和处理。

可以定义一些枚举值来表示不同的交互状态,比如:

  • debug_interaction_drag_value:表示拖拽交互状态。
  • debug_interaction_toggle_value:表示切换交互状态。
  • debug_interaction_none:表示没有交互状态。

这些枚举值用于追踪当前的交互状态,而不直接影响到正在交互的对象。枚举类型在这里有助于清晰地表达不同的交互方式,并且便于管理和扩展。

为了进一步处理交互过程中的各种情况,可以在代码中使用这些枚举值来判断当前的交互状态,进行相应的操作。这种方法让交互的状态变得更加明确,便于调试和优化,避免了将状态和具体的交互对象混淆。
在这里插入图片描述

game_debug.cpp:测试交互状态以确定执行哪种类型的交互

首先,目标是了解当前的代码交互过程,设立了一个调试交互(debug interaction)机制。通过检查交互状态,判断当前是否在进行交互。若是交互中,则执行相应的代码块,否则执行另一套代码。这样,代码逻辑与之前类似,使用了 else 语句来处理不在交互状态时的情况。

在交互开始时,若交互对象不为 None,则执行一系列操作。在这个过程中,代码根据当前交互的对象,使用 switch 语句对不同的交互类型进行分支处理。例如,在拖动某个值时,会进行特定的类型处理。在拖动操作中,目前处理方式是对某一类型进行具体操作,具体实现为 real - it's real for 2q

代码中继续处理其他交互类型,假如交互类型为 none,可以选择改变当前的交互类型,或者重新开始交互。这个过程中,关于如何切换交互类型的逻辑也被考虑进去。

在考虑共享代码时,有一种方案是将 click interaction 部分的处理提取为公共部分,避免在每个交互类型中重复书写。然而,这种方案存在一定的困难和选择难度。最后决定保持原有方式,即保留重复的处理逻辑,以便统一管理交互处理,不至于在每次操作时都需要复制代码。

不要过早做决定

在处理交互时,首先需要明确每个交互的步骤及其对应的代码。例如,针对拖动事件,当发生拖动时,系统会通过特定的代码来处理这一动作。而其他一些交互元素,在此阶段并不参与实际操作,甚至可以认为它们目前没有任何作用。接下来,在完成拖动操作后,系统可能会执行一些后续动作,这些后续动作的定义和执行过程会在代码中体现出来。

整体上,交互的流程设计可以分为几个阶段,其中包括识别当前交互类型、执行具体的操作(如拖动、点击等),以及处理交互结束后的各种后续任务。每个交互模块之间的配合确保了系统的顺畅运行,而一些暂时不涉及的部分则可以被忽略或者预留出来,供以后根据需求调整或填充。

在这里插入图片描述

在这里插入图片描述

game_debug.cpp:根据 DebugVariableType 执行不同的交互

在设计交互逻辑时,首先会检查当前的交互对象和它的类型,以决定应该进行何种类型的交互。每当开始交互时,需要首先查看当前操作对象的类型,然后根据类型选择合适的交互方式。例如,针对不同类型的数据对象(如池类型、实数类型或组类型),每种类型的交互方式可能不同。当前只能对特定类型的数据对象(如池32和实数32)进行交互,而对于其他类型(如组类型)尚未定义交互方式。

在交互开始时,如果调试状态下,交互类型会被明确设置,这样可以确保系统知道正在进行的交互类型。如果交互类型未知,系统会跳过该交互操作。对于不同的交互类型,具体的操作可能不同。例如,对于池类型(pool),执行的操作是切换值(toggle),而对于实数类型(real),执行的是拖动值(drag)。在组类型(group)的情况下,操作类似于池类型,也是一种切换操作。

当交互结束时,系统需要检查交互类型,并根据交互类型执行相应的后续操作。如果是拖动操作,可能没有后续操作需要执行;如果是切换操作(toggle),则需要检查变量类型并进行切换。在这种情况下,交互结束后,还需要将相关的交互状态重置。特别是需要注意的是,在某些情况下,交互对象的状态会被清除,因此在交互结束时需要将其恢复为初始状态(如设置为none)。

总的来说,交互过程包括明确交互对象的类型、选择适当的交互方式、执行相应操作并在交互结束后恢复状态。系统通过调试状态来确保交互过程的正确性,确保每次交互都能顺利进行。
在这里插入图片描述

运行游戏并查看我们的新功能

当前的交互逻辑已经恢复到之前的状态,并且仍然可以执行原先的操作,整个流程仍然保持一致。但是,现在在进行交互时,理论上可以在交互过程中动态调整交互逻辑,即使在执行过程中仍然可以做出修改。然而,目前仍然需要完善一些细节,以确保交互行为的稳定性和一致性。

game_debug.cpp:检查我们是否有一个变量并重新运行游戏

需要检查并确保当前确实存在一个变量,否则交互过程可能会出错。经过调整后,再次运行代码,现在已经能够准确识别正在进行的交互类型。这样,在点击外部区域然后移动到交互对象时,应该能够避免意外的交互触发。可以观察到交互过程的变化,并确保其按照预期进行。

接下来的目标是增加一种能力,使得交互过程更加可控和稳定,确保交互逻辑能够正确处理不同的场景,避免不必要的误触或错误操作。
在这里插入图片描述

game_debug.h:在 debug_interaction 中添加 NOP

需要引入一种特殊的交互类型,用于表示无效或无意义的交互。例如,用户可能只是随意点击了界面上的某个位置,但实际上并没有选中任何交互变量。为了处理这种情况,可以采用几种方法,例如允许交互值为空(如拖动时允许其值为null),但更好的方案是直接定义一个专门的交互类型。

在交互开始时,如果当前并没有选中的变量,则可以将调试状态的交互类型设置为一个特殊的无操作(no-op)状态,表示当前交互不会执行任何实际操作。当进入交互逻辑时,如果检测到该交互类型是无操作类型,就直接跳过所有后续逻辑,不需要执行变量修改,也不需要重写配置文件,从而避免不必要的计算开销。

这样可以确保系统在处理交互时能够区分真正的交互操作和无意义的点击,从而提高程序的健壮性和交互体验。
在这里插入图片描述

在这里插入图片描述

运行游戏并尝试这个新的 NOP 交互类型

现在,当用户执行无效交互时,系统不会执行任何操作,而当用户释放鼠标或触摸点时,交互才会重新开始。这意味着,如果用户在交互区域外进行点击或拖动,不会触发任何有效的交互逻辑,但一旦释放并重新进入交互区域,交互就会恢复正常。

当前的逻辑运行良好,交互行为与预期一致,整个交互流程保持稳定。交互高亮功能也正常工作,确保了用户可以直观地看到当前的交互状态。一切都按照计划运行,没有出现异常情况。

接下来,需要对交互逻辑进行进一步调整,以适应更加复杂的需求,提高交互的灵活性和可控性。

game_debug.h:介绍 debug_variable_hierarchy

希望能够提供多种方式与同一数据进行交互。因此,计划实现一种克隆菜单项的功能,并采用一种相对独特的方法来实现。

首先,需要扩展“组”的概念,使其不仅仅是一个逻辑上的集合,还可以具备具体的位置属性,即可以存在于某个特定的屏幕位置。这样,每个调试变量组不仅仅是一个抽象的集合,还可以在屏幕上的某个位置进行展示。

在这个基础上,需要引入更高级别的组织方式,即“调试变量集合”或“层级结构”,这将作为整个调试变量的根组(root group)。这个根组定义了整个变量层次结构,并且与其在屏幕上的绘制位置相关联。未来,可能还会在这个结构中添加其他相关信息,以便扩展其功能。

为了实现这一点,需要调整当前的数据结构,使其能够支持更大的组织框架。例如,可以创建一个层级结构,其中根组包含所有的调试变量,而其他组则可以独立运作,并与根组进行交互。这样,就可以更灵活地管理和展示调试变量,使其既可以嵌套组织,又可以在不同的位置显示。

在具体实现时,首先需要创建一个“状态层级根组”(state hierarchy root group),并将其与上下文的根组(context root group)关联。同时,增加一个层级结构副本,以确保变量的组织方式能够正确反映到界面上。这部分逻辑可能需要在变量创建之外进行处理,以保证结构的完整性。

此外,仍然可以保留原有的调试变量组概念,并在其基础上增加更高级别的组织方式。例如,根组始终包含所有变量,而其他组则可以独立存在,或者在特定情况下进行组合和交互。这样,可以实现既有全局管理又能局部调整的灵活交互方式。
在这里插入图片描述

在这里插入图片描述

game_debug.cpp:设置层级结构

在创建变量后,需要进一步设置层级结构。最关键的是确保左边距 (left edge) 和 debug state @y 这两个参数正确设置,以便能够正确定位层级结构的位置。

首先,层级结构的 group 直接设置为 root group,而 location 则采用之前计算出的值。在实际绘制时,需要确保绘制逻辑使用新的层级结构,而不是原始的 root group。在 config menu 代码部分进行调整,使其在绘制时采用 hierarchy.group.first_child 作为组,并使用 UI P 来设定 @x@y 坐标。

但在测试过程中,出现了一个问题,即绘制时无法正确显示任何元素。检查初始化流程时,确保 groupUI P 变量已经正确设置,并且 root group 仍然处于正确的初始化状态。代码检查显示,这些值都应该是正确的,因此可能需要进一步调试,逐步跟踪变量的变化,以找出导致绘制失败的具体原因。

接下来,可以通过单步调试来跟踪 label 的设置,确保 group 结构的初始化没有问题,并找出 config menu 变量丢失的具体原因。

调试器:进入 DEBUGDrawMainMenu 查找哪里出了问题

在调试过程中,发现变量没有正确显示,因此需要检查 debug draw 相关的代码,确认是否正确调用了该函数。首先,检查函数是否被正确调用,并设置断点以便跟踪执行流程。

在断点处,查看 @x@y 的值是否合理。初步检查显示,这些坐标值看起来是正常的。同时,检查变量 bargroup,确保它们的状态也是正确的。此外,还需要检查 ground chunks 变量,以确定是否有任何不合理的情况。

在进一步检查时,发现可能存在坐标翻转的问题(flip)。这意味着某些变量可能在计算时被错误地交换了位置,导致最终的绘制结果不正确。接下来的步骤是检查相关代码,确认是否在某处意外交换了 @x@y,并修正坐标计算,以确保变量能够正确显示在预期位置。
在这里插入图片描述

game_debug.cpp:翻转传递给 DebugState->Hierarchy.UIP 的值的顺序

在调试过程中,发现了问题的根本原因,并且很快就确认了错误所在。这个问题的发现过程显得有些反高潮(anticlimactic),但至少成功定位了错误,解释了为什么变量没有正确显示。

现在,接下来的目标是停止当前的错误行为,并进行适当的修正,以确保变量能够正确呈现并在预期的位置显示出来。

运行游戏并发现一切正常

目前的目标是确保可以绘制多个元素。现在,通过调整代码,可以绘制多个字符,并且可以在屏幕上显示多个实例。接下来的计划是,在明天继续实现一个调试功能,该功能允许将菜单项拆分并将其放置到其他位置。这将是一个非常方便的功能,能够帮助管理交互过程,并展示如何管理多个交互元素。

一旦游戏做完,你会进行一次直播全程试玩吗?

在游戏开发的过程中,虽然大部分时间都在进行代码编写,但随着开发的推进,尤其是接近尾声,很多工作将需要通过实际的游戏运行来进行测试和调整。此时,除了少部分如灯光和碰撞检测等技术细节外,大部分工作都涉及到在游戏中实际操作。因此,接下来将不得不进行大量的游戏体验和互动,特别是在进入世界生成阶段时,必须走动在虚拟世界中,进行各种交互和测试。

尽管如此,还是希望尽量减少在直播中长时间进行游戏测试的情况,因为这可能会让直播变得单调无趣。过去,由于大多数代码工作都涉及结构性和架构性的实现,很多时候不需要频繁地运行游戏,所以节省了不少时间。但现在随着开发进入后期,希望通过一些技术,比如实时代码编辑,来提高效率,避免直播内容主要变成一遍又一遍地玩游戏进行测试。希望通过更高效的开发流程,能够平衡好编程和游戏演示之间的比例,让直播更加流畅、有趣。

你认为什么时候会开始做正式的游戏?

目前,游戏开发已经接近尾声,预计很快就会开始正式的游戏开发。之前的估计是大约需要200小时,但实际上可能会稍微超出这个时间,因为还需要做一些灯光调整等工作。接下来,虽然大部分代码工作已经完成,但依然有一些任务需要完成,例如GUI设计和调试系统,这些虽然不复杂,但仍需完成。

此外,尽管粒子系统已经完成,但可能还没有做尽可能多的优化或扩展。而在渲染器方面,虽然大部分任务已经完成,但仍然有一些细节需要处理。当前的开发任务主要集中在一些小的功能调整和优化上,接下来则是进入游戏正式开发阶段。

具体来说,很多后续的工作需要依赖游戏内容的实际互动,因此无法在游戏性内容完成之前完成。比如,保存游戏系统、多个模拟区域的实现等,都需要在游戏中有大量实体互动的基础上才能进行。同时,一些基础的功能,如碰撞检测、路径寻路等,也需要在游戏机制明确之后才可以着手。

总的来说,在完成这些结构性的任务后,将开始进行一些基础的游戏玩法的构建,主要集中在如何实现碰撞检测、Z轴处理等方面。之后,开发将逐渐转向实现游戏中的实际功能和机制,并开始对游戏中的实体进行详细设计。

这是不是意味着一个真正的 RNG 就要来了?

游戏的开发目前仍处于规划和准备阶段,虽然世界生成(world gen)功能的实现已经在考虑中,但在完全实现之前,需要确保一些核心的游戏玩法机制已经做好准备。具体来说,首先会聚焦于设计和实现一些基本的游戏房间、敌人和角色互动的部分,这些部分不涉及太多的随机数生成(RNG),而是更多关注于游戏的基本结构和玩法测试。只有在这些基本的玩法和机制稳定后,才会开始进一步实现世界生成等更加复杂的系统。

接下来的开发可能会涉及到实体系统的改进,可能需要调整之前的一些设计布局,使得系统更加符合实际的需求和玩法设计。关于动画和渲染的顺序,原本计划先做动画再做渲染,但实际上渲染的工作先行完成,说明游戏开发过程中有一些计划上的调整和变化。此外,游戏开发的进度和顺序往往难以准确预测,尤其是在只有一位程序员的情况下,很多时候会根据实际需求灵活调整开发步骤和时间安排。

因此,开发的进展不完全按照预定计划进行,实际开发中会不断根据需求进行调整,但核心目标是尽快完成游戏的基本玩法框架,以便进入更深层次的开发。

你已经讲过 Minkowski 差分了吗?

在流媒体中提到了Minkowski和形状碰撞检测的相关内容,但不太记得何时具体讲过Minkowski差分。讨论时,确实提到了Minkowski和,尤其是如何通过其他形状来“生长”一个形状,这是通过Minkowski和实现的。Minkowski差分通常用于在碰撞检测中找出形状之间的相对位置关系,尤其是在像GJK(Gilbert-Johnson-Keerthi)这样的算法中。Minkowski差分的主要用途是通过将一个形状从另一个形状中“减去”来计算相对位置。这个过程涉及到找到形状间的最短距离或判断它们是否发生碰撞。尽管没有深入讨论差分,通常只有在尝试确定形状的相对位置(如GJK算法中确定原点是否在两个形状之间)时,才会涉及Minkowski差分。

Minkowski和是将两个形状的每个点相加,生成一个新的形状。Minkowski差分则是通过减去一个形状的每个点,来反映另一个形状的相对运动。这两者在形状的碰撞检测和物理模拟中都非常重要,尤其是在处理复杂的形状之间的相互关系时。

能否解释一下 IMGUI 的常见实现方式和你认为应该如何实现的区别?我有点没跟上

在GUI的实现中,有一种常见的方式是将交互代码与绘制代码分开处理。具体来说,绘制的部分仅负责绘制界面元素,而交互部分则通过后续的命中测试(hit testing)来处理,这通常发生在渲染(IM)阶段。通过这种方式,交互的处理被推迟到之后,而不是在绘制的过程中直接进行。这种做法的好处在于,它将绘制与交互分离开来,使得代码更容易组织和解耦,从而提高代码的可维护性和清晰度。

如果将交互代码直接嵌入到绘制代码中,比如在绘制矩形时直接进行交互处理,这样做会导致代码难以组织和管理。因为这样会把不同的逻辑混合在一起,不利于分层和模块化,增加了以后维护和扩展的复杂度。因此,分离绘制与交互处理是一个更好的方法,能够让代码更加清晰,并且便于管理。

总结来说,这种做法的核心思想是通过将绘制和交互逻辑解耦,保持代码的模块化和可维护性。

你会考虑做一次 24 小时,从头到尾创建一个应用或游戏吗?

考虑做一个24小时直播,要求在一个直播中从头到尾完成创建一个应用或游戏,这个想法虽然可以考虑,但并不觉得有太大意义。做这种直播的目的不太清楚,可能唯一的意义就是作为一种宣传活动或者噱头,展示能否在24小时内完成某个项目。不过,除了这种特殊的营销目的之外,感觉做这样的直播并不能带来实际的价值。所以,虽然可能会考虑做,但最终可能会觉得这个点子并不合适。