当前位置: 首页 > news >正文

游戏引擎学习第198天

回顾并为今天的内容设定

今天我们有一些代码需要处理。昨天我们进行了一些调试界面的整合工作,之前我们做了一些临时的、粗糙的操作,将一些东西读进来并放到调试界面中。今天,我们并不打算进行大规模的工作,更多的是对之前的代码进行整理和优化,因为毕竟今天是周五,大家可能不希望深陷在复杂的工作中。

对于接下来的工作,我并不打算过多关注如何优化界面或让配置文件接口运作流畅。下周的重点应该是创建更加清晰的配置文件接口,以便于切换和其他操作。今天的任务主要是对现有的代码做一些整理,尤其是在硬件接口部分。通过整理这些部分,我们可以为将来加入更多的高级功能打下基础,使得整个系统的运行更加平稳流畅。

我个人并不擅长构建非常复杂的配置文件界面,之前也只是做过一些简单的记录和时间跟踪工作。而现在,我们手上有了更多关于线程和数据的详细信息,这让我不得不思考如何将这些信息转化为用户可以轻松理解和操作的形式。对此,接下来的几天或许会花更多时间讨论如何设计这样一个界面,使它既有用又易于理解。

此外,还可以在调试界面中打开性能分析器,查看完整的性能数据,并且调整分析器的大小。虽然在某些时候调试界面可能显示不完,或者超出屏幕,但这并不影响整体的调试能力。你可以同时打开多个界面,进行多重调试操作,所有的界面和操作都可以动态响应,简便而且实用。

接下来,我想做的一件事是实现“拆分”功能。例如,在调试界面中,如果我正在查看某个元素,应该能够把它从主界面中提取出来,变成一个独立的窗口或者面板。这种“拆分”操作在调试过程中非常有用,尤其是当我们希望同时查看多个独立的调试数据时。目前,调试界面只能通过开关来控制显示和隐藏,接下来的目标是让它支持更自由的拆分和拖动操作。

实现这一功能其实并不复杂,我想通过这个简单的例子来演示如何快速制作出一些具有交互能力的界面。通过简单的几行代码,就可以制作出一个非常实用的调试界面,而无需依赖复杂的UI库。这也说明了在游戏开发中,如何快速、有效地加入一些功能,而无需担心UI的复杂性。

总的来说,我们今天的目标是让调试界面能够支持更多的交互功能,特别是拆分功能,让开发者能够根据需要将不同的调试窗口分开处理。
在这里插入图片描述

game_debug.h: 介绍 debug_variable_reference

我们现在已经进入了调试代码的部分。从当前结构来看,我们使用了变量组来管理调试变量,而每个调试变量实际上保存着要编辑的数据。这些调试变量在当前实现中是直接存储数据的,问题在于这种结构让我们很难将一个调试变量放在多个地方进行引用。

理想的做法是通过某种方式间接引用这些变量。我们希望每个调试变量能够在多个地方同时被引用,而不必重复存储其数据。为了实现这一点,我们只需要引入一个间接引用的列表。这种结构我们之前讨论过,但在游戏开发中并不常见,因为链表这种结构在游戏中并不像在编辑器或类似的应用程序中那样常见。然而,在一些特定的情况下,链表结构仍然会派上用场,尤其是在我们处理一些较为复杂的引用关系时。

为了实现这一目标,我们将引入一个新的结构,专门用来存储引用关系。这些引用关系将独立于原始的调试变量存储,并且允许多个地方引用同一个变量。这样,无论有多少个引用需要使用这些变量,它们都可以独立存在,不会相互干扰。

我们只需要修改那些使用调试变量的部分,把原本直接引用变量的地方,改成引用调试变量引用的链表。举个例子,原来一个组可能直接保存了一些调试变量,现在我们希望它保存的是调试变量的引用链,而不是直接保存变量数据。同样地,调试变量的层级结构也需要做类似的调整,让它们存储的是引用而非实际的变量。

不过,考虑到层级结构的实现,我认为可能会有些不同的处理方式。所以,我决定暂时不修改这一部分,先集中精力处理将调试变量改为引用链表的工作。

目前,已经完成了调试变量引用部分的工作。接下来,我会确保这些引用结构能正确地与其他部分进行交互,确保它们能够按预期动态处理。这是我们在调试系统中所需要的一项重要改进,让调试变量能够在多个地方灵活地引用和使用。
在这里插入图片描述

game_debug_variables.h: 介绍 DEBUGAddUnreferencedVariable

在当前的调试过程中,目标是创建一个新的变量,并为该变量添加引用。这一步骤的关键是实现一种引用机制,方便在后续过程中进行变量引用和管理。首先,创建一个名为 debug variable 的变量,这个变量将作为引用的基础。

接下来,需要做的是在创建变量的同时,还要创建一个调试变量引用。这个调试变量引用将与之前创建的变量保持关联。具体来说,调试变量引用将处理与变量直接相关的部分,而不涉及分组或其他复杂的结构。这一步的目的是确保能够通过引用访问变量,而不仅仅依赖变量本身。

在实现时,需要定义一个 DEBUGAddVariable 类型,它将包含指向实际变量的引用。关键的是,引用能够访问变量,而变量本身不能直接访问引用,因此,引用是获取变量的一个有效途径。

总之,当前的目标是通过创建变量和调试引用来构建一个清晰的调试系统,其中引用和变量能够互相配合工作,确保变量的灵活性和可访问性。
在这里插入图片描述

game_debug.h: 为 debug_variable_reference 添加 debug_variable *Var

在这里,调试变量引用的实现只需要一个能够指向实际变量的机制。具体来说,我们需要创建一个 DEBUGAddVariable,它的作用就是指向之前定义的调试变量(debug variable)。这个引用将能够间接访问该变量,从而实现更灵活的操作和管理。

实现这个引用非常简单,只需要确保它能够准确地指向目标变量,并且在需要时能够通过该引用访问变量的值或进行修改。通过这种方式,引用成为了访问变量的有效途径,而不再直接依赖变量本身。

总结来说,目标是确保调试系统中的每个变量都可以通过引用来访问,引用结构的引入能够提高代码的灵活性和可维护性,使得调试过程更加清晰和便捷。
在这里插入图片描述

game_debug_variables.h: 介绍 DEBUGAddVariableReference

在创建调试变量引用时,主要的任务是将调试变量引用推送到内存池(arena)中,并设置引用,使其指向我们给定的调试变量。这意味着,所有原本与变量相关的操作,将改为通过引用进行处理。引用允许我们间接访问变量,因此这里的核心目标是确保所有的操作都通过引用来管理,而不是直接操作变量本身。

接下来,在构建树形结构时,父节点(parent)同样需要使用引用来构建。每个树节点都应当指向其他变量或节点,并且这种关系应通过引用进行管理。通过这种方式,可以实现更灵活的树结构,其中每个节点指向的变量或数据可能随时变化,而引用会确保每个节点都始终指向正确的对象。

在具体操作中,如果树形结构中的父节点引用不为空,我们将通过引用获取父节点对应的变量。这种方法使得树形结构的构建更加灵活和高效,因为树结构的父子关系能够通过引用动态地维护,而不必依赖静态的直接变量指针。

总结来说,重点在于通过引用来管理树形结构和调试变量,以实现更高效的内存管理和更灵活的调试操作。这种方式能够更好地处理复杂的数据结构和变量间的关系,使得代码更加可扩展和易于维护。

在这里插入图片描述

在这里插入图片描述

game_debug_variables.h: 清理编译错误

在处理调试变量引用时,遇到了一些问题,最初的错误是因为忘记在代码中使用指针。具体来说,调试变量的引用未能正确转换,原因是缺少指针,导致编译错误。通过修复这个问题,可以继续进行调试变量的操作。

在处理这一过程时,重点是将调试变量和变量组等结构更改为引用类型,这使得变量和组能够被更灵活地引用和操作。这种更改并不会涉及太复杂的逻辑,只是将原本的直接访问变为间接引用,这样可以方便后续的扩展和调整。

此外,调试变量组和其他结构的返回类型也被修改为引用类型,确保这些对象可以通过引用进行处理和传递。整个过程虽然看起来简单,但需要进行一定的代码更改,以确保数据可以通过引用有效管理。

整个实现过程中并没有涉及太多复杂的设计或算法,主要的变化是将数据结构的引用方式从直接指向改为通过引用间接操作,这为后续的扩展和维护提供了更多灵活性。尽管这些改动相对简单,但它们在实际实现中有助于代码的结构化和优化。

总的来说,这些更改并不涉及复杂的功能或算法,只是对数据访问方式进行了优化,确保变量和数据结构的引用更符合需求,并且便于未来的扩展。

game_debug.cpp: 传播 Ref

在继续处理过程中,目标是将之前的改动进一步传播到更高一级的结构,即根组结构。此时,根组需要使用调试变量引用,而不是直接使用变量。这意味着我们需要修改代码,使得在处理这些结构时,所有的变量操作都通过引用来进行。

为了实现这一点,在编写代码时,根组的操作将改为通过引用访问调试变量。变量不再直接存储数据,而是通过引用指向数据,这样就能确保变量的数据是间接访问的。代码中的 ref 关键字被用来替代原来的直接变量引用。这样做的目的是为了提高代码的灵活性和扩展性,尤其是当涉及到多层次的结构时,引用管理会使得操作更加高效。

具体操作包括修改迭代器,使其迭代的是引用而非变量本身,这样可以避免直接操作数据,而是通过引用来操作数据。这一改动是为了保持数据的一致性和灵活性,尤其是在处理更复杂的数据结构时。

在调整完成后,代码的逻辑并没有发生本质的变化,唯一的区别在于现在操作的是引用而非直接的变量。所有的变量访问都通过引用进行,这确保了数据结构能够以更灵活的方式进行管理和操作。

总的来说,这些修改是为了将数据管理从直接访问改为通过引用操作,增强了数据结构的灵活性和可扩展性,同时保持了原有代码的逻辑和功能。
在这里插入图片描述

在这里插入图片描述

编译并确认仍然可以正常运行

到目前为止,所有的修改看起来都已经完成,并且代码的功能在理论上应该能够正常运行,和之前的操作逻辑类似。尽管我们对代码结构做了一些调整,但系统仍然能够按预期遍历数据和执行操作。整体来看,修改后的代码表现良好,功能正常,迭代过程也没有问题。

这些改动的关键在于如何通过引用来管理数据,而不是直接操作数据本身。通过这种方式,可以确保更高效、更灵活的引用管理,同时不影响原有的迭代和数据操作。总的来说,修改后的代码结构应该能够在不破坏现有功能的基础上,提供更强的扩展性和可维护性。

在进一步的测试中,预计不会遇到太大问题,因为所有的基本操作依然能够按预期执行。
在这里插入图片描述

game_debug_variables.h: 尝试将 UseDebugCamRef 添加到两个不同的组中

首先,想要测试变量引用的功能,目的是确保能够将同一个调试变量添加到两个不同的组中。具体步骤是:

  1. 先定义一个调试变量,例如 debug camera distance,然后为这个变量创建一个引用。
  2. 然后,使用这个变量引用,确保它能够在多个组中共存,即将同一个调试变量添加到不同的组中。这里需要注意的是,确保能够通过引用管理这个变量,而不是直接复制它。

在实现中,通过定义 debug camera reference 来引用调试变量,并将其添加到目标组中。在这个过程中,通过创建引用来保持变量的唯一性,但允许它出现在不同的地方。测试时,需要注意检查是否能够成功地将相同的变量添加到多个组中,而不会产生冲突或错误。

整体上,这个操作是为了验证引用机制是否有效,确保能够通过引用的方式对变量进行管理和操作,而不影响它的本质或数据一致性。在执行时,需要去掉一些不必要的代码或错误部分,保证程序的正确运行。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

运行游戏并查看 UseDebugCam 是否出现在两个组中

在测试过程中,运行时可以看到新的调试变量(例如“debug camera”)成功地添加到多个位置,并且它们之间的关联得到了验证。这个变量现在出现在了不同的地方,并且可以在多个位置进行修改,而且它们的值是同步变化的。

具体来说,当将相同的变量添加到多个组时,不会产生任何性能上的问题,也不会有额外的复杂性。这种设计使得能够在不同的地方引用相同的变量,而不会出现重复或冲突的情况。这是为了确保在多处使用同一个变量时,不会有任何额外的开销,同时保证变量值的一致性。

另外,通过对调试变量进行高亮显示,可以直观地验证它们是否正确关联。当鼠标悬停在这些变量上时,能够看到它们都被高亮显示,并且在修改其中一个变量时,其他引用相同变量的地方也会同步变化。这进一步验证了引用机制的有效性和正确性。

总的来说,整个过程确保了能够在多个地方灵活地使用同一个变量,而不会导致重复或数据不同步的问题。

game_debug.cpp: 实现支持多个 debug_variable_hierarchy

在实现过程中,首先需要更多的内存来支持调试系统的扩展。由于当前的内存分配限制,无法创建更多的功能或增加新的菜单,因此需要增加内存的使用量,以便能够在需要时创建更多的调试项或功能。

当前实现中,调试系统使用了一个“调试区域”来进行临时存储和计算,但这个区域并未使用全部可用内存。因此,实际上在内存分配上还有一定的剩余空间,这意味着如果需要更多的内存空间来处理更多的调试任务,是可以做到的。

为了扩展功能,目标是能够绘制多个调试变量层级,而不仅仅是一个单一的调试菜单。现有的实现只能显示一个硬编码的调试层级,但希望通过修改,能够实现动态的、多层次的调试变量层级的绘制。这个目标的实现方式是通过使用循环遍历调试层级,允许多个调试菜单独立显示,并且每个菜单都可以根据需要进行修改。

此外,当前的实现存在一些问题,比如调试菜单的位置和附加的方式还不明确,属于一些较为粗糙的设计,需要后续的优化。在代码中,调试变量的管理通过“层级引用”的方式进行控制,接下来的目标是能允许更多层次的调试变量体系,从而使得在不同的上下文中可以灵活地引用和修改这些变量。

为了解决这些问题,计划引入一个“层级哨兵”概念,通过遍历这些层级,允许更多的调试菜单实例同时存在,并能够在每个菜单中显示特定的调试信息。同时,也会增加一个函数 AddHierarchy,用于动态地添加新的调试层级,确保每个新的调试项都能被正确地管理和展示。

简而言之,整体目标是增强调试系统的扩展性,使得可以更灵活地管理调试变量,动态添加更多的调试层级,而不会受到当前内存和设计的限制。

在这里插入图片描述

在这里插入图片描述

game_debug.cpp: 引入 AddHierarchy

在实现过程中,目的是通过修改代码来支持调试系统的扩展,以便可以创建和管理多个调试变量层级。首先,构建调试变量引用时,需要初始化各个指针和节点,确保它们可以正确地连接和引用。

使用了双向链表的结构来管理调试变量层级。在实现时,首先创建一个新的调试变量层级(debug variable hierarchy),并初始化相关字段。例如,初始化它的组(group),这通常是一个根组(RootGroup),但随着后续操作,它可能变成其他的组。链表的“前驱”和“后继”指针也需要初始化,以确保链表的正确性。

具体来说,链表的操作需要设置节点之间的连接关系。每个节点的“前驱”指针指向它之前的节点,而“后继”指针指向下一个节点。为了实现这一点,需要在初始化阶段将这些指针互相连接。这样,每个节点的前后关系都被正确地管理。

当新层级添加到链表中时,还需要确保链表的头部(sentinel)指针的正确性。这个“哨兵”节点会始终指向链表的开始或结束,作为链表的边界。对每个新节点,确保其“前驱”和“后继”指针正确指向相邻的节点,避免出现空指针或断链的情况。

在代码中,链表操作需要处理好指针的初始化和链接,确保节点的指针在整个操作过程中始终有效。这包括初始化每个节点的指针,并在需要时清除旧的指针,确保没有残留的无效指针。

同时,对于每次新的层级添加到链表中的操作,需要返回正确的指针,确保其他代码能够使用这个新节点。为了避免错误,还需要确保每次操作的内存分配和指针设置是合理的,避免出现内存泄漏或访问无效内存的情况。

总之,通过这种方式,可以为调试系统提供一个灵活的结构,允许动态添加和管理多个调试层级,而不必受限于当前的单一结构或内存分配。
在这里插入图片描述

game_debug.cpp: 编写 Hierarchy 双向链表

在实现过程中,首先需要创建调试系统的上下文(context),并初始化调试状态。调试变量层级的创建是通过标准的双向链表实现的。链表的起始点(sentinel)指向自身,确保链表开始为空。为了避免迭代时出现问题,初始化时会将链表的组指针设为零,提醒在没有正确初始化的情况下,不要进行迭代。

在链表实现中,每个节点的前驱指针(previous)指向链表的“哨兵”节点(sentinel),后继指针(next)指向链表的下一个节点。这样可以确保链表的正确性,避免出现循环或指针错误的情况。

在绘制调试菜单时,出现了一个问题:调试菜单的绘制循环似乎没有正确结束。需要检查代码,确保在绘制时能够正确退出循环,避免重复绘制同一个菜单。特别是在调试菜单绘制的函数中,需要确保调试变量的层级在迭代时能够正确前进,而不是在错误的节点上停留。

在调试菜单的名称和函数调用上,也有一些需要改进的地方,例如draw debug menu函数的命名需要更一致,以便提高代码的可读性和可维护性。

总之,当前的目标是确保调试系统能够正确地创建、管理和绘制多个调试变量层级,并确保每个层级的链表结构和指针的操作都能正常工作,避免错误的循环或无效的菜单绘制。
在这里插入图片描述

在这里插入图片描述

有点卡

调试器: 进入 AddHierarchy

在调试系统中,首先检查了调试变量层级(hierarchy)的链表结构,确保链表的起始点(哨兵节点)正确地指向自身,并且链表中的每个节点的前驱指针和后继指针都指向正确的地方,形成了一个正确的双向链表。特别是在链表的"next"指针中,当前节点正确地指向下一个节点,而下一个节点的"previous"指针又指回当前节点,确保了双向链表的完整性。

接着,进入了调试菜单的绘制部分,确保调试菜单能够正常绘制并退出绘制循环。在draw debug menu函数中,通过检查代码,确认了调试变量组(debug variable group)能够正确显示,而且绘制完成后能够正常退出,不会卡在某个地方。

在调试过程中,调试层级的循环和菜单的绘制过程都得到了修复。退出条件和绘制的逻辑都被确认无误,确保了代码按预期工作。最终,调试系统的功能看起来正常运行,调试菜单能够正确地显示和更新,调试变量也能如预期一样进行处理和显示。

game_debug.cpp: 在正确的位置调用 AddHierarchy

在调试过程中,发现了一个潜在的问题。问题的根源在于AddHierarchy函数被每一帧都调用,这会导致每一帧都生成大量的调试层级和绘制内容,这显然会影响性能。因此,决定停止这种做法。

接着,考虑如何决定调试层级的位置,因为在不知道具体尺寸的情况下,不能直接确定位置。然而,通过进一步分析,意识到实际上是可以提前计算出这些尺寸的。这意味着虽然不直接知道尺寸,但可以通过一定的方式来预估和计算这些尺寸。

具体来说,可以在初始化时为每个调试层级预设一个起始位置,假设开始时的位置为某个默认值,比如将其高度设置为初始值的一半。这样,通过预先计算和布局,可以更高效地管理调试菜单和层级的绘制,而不会因为每一帧都重新计算和绘制而带来性能上的问题。
在这里插入图片描述

在这里插入图片描述

运行游戏,发现界面有所改善

现在,调试系统的状态看起来已经变得更好了,一切都在正常运行,所有功能都按预期工作。接下来,计划实现一个功能,使得可以从当前的调试界面中“拆卸”某些元素,这样就可以更灵活地操作调试界面。

为了实现这一点,计划引入一种交互概念,使得能够通过某些操作将调试界面中的部分元素从当前显示中分离或移除。这样就可以更方便地管理调试界面,特别是在需要动态调整显示内容时,能提供更多的灵活性和操作空间。

game_debug.cpp: 实现从菜单中拆分项目的功能

为了实现从调试界面中“拆卸”元素的功能,计划增加一个新的交互概念。这个交互概念将允许用户将调试界面中的一些元素从当前的位置“拆卸”并重新放置到其他地方。这一功能的实现将通过对调试界面交互机制的调整来完成。

首先,计划在交互过程中添加一个“拆卸”操作(tear),并确保在某些特定条件下(例如按下特定的快捷键或鼠标按钮)触发这个拆卸操作。为了实现这一点,选择了一个简化的方式,即通过添加一个小组件来表示拆卸的交互功能。这种拆卸功能将允许用户将元素从原始位置拆卸并放置到新的位置。

具体的实现步骤包括:

  1. 在调试值切换功能中,引入“拆卸”功能。首先,会通过修改交互处理逻辑来实现该功能,确保在用户点击时可以触发拆卸操作。
  2. 为了简化实现,采用了一种基本的方法来定义拆卸功能,并设置特定的输入条件来触发该功能。例如,可以通过鼠标右键按下或其他特定的输入键来执行拆卸操作。
  3. 在交互处理中,将判断是否处于拆卸模式。如果用户在按下特定的修改键(如“Alt”键)时触发该模式,则启动拆卸操作;否则,执行默认的交互操作。
  4. 增加了条件判断,判断是否在交互过程中点击了右键或特定的鼠标按钮,并根据这些操作决定是否执行拆卸。如果触发了拆卸,调试界面将允许用户将选定的调试元素从当前位置拆卸并移至其他位置。

总体而言,这种方式使得调试界面变得更加灵活,用户可以根据需要自由地调整和重新排列调试元素,提升了调试系统的交互性和可操作性。
在这里插入图片描述

game_debug.cpp: 实现 DebugInteraction_TearValue

为了实现“拆卸值”(tear value)的功能,需要在现有的代码基础上添加一些新的实现细节。具体的步骤如下:

  1. 定义拆卸值功能:首先,拆卸值功能需要与“拖拽”功能相结合。具体来说,当用户执行拆卸操作时,需要创建一个新的调试变量层级(debug variable hierarchy),类似于现有的拖拽层级(dragging hierarchy)。这个新的层级将用于存储拆卸的值,并且可以在拖拽操作时与当前交互对象进行关联。

  2. 创建新的调试变量层级:为了实现拆卸操作,首先要检查是否已经存在调试状态的层级。如果还没有创建,应该先初始化一个新的层级。这一层级会在用户拖拽或拆卸操作时使用,并且会在操作结束后清理掉,恢复到初始状态。

  3. 更新交互状态:在处理拆卸操作时,需要将当前的鼠标位置与新的调试变量层级关联。具体来说,鼠标所在的位置将成为新的层级状态的位置,并且在拆卸过程中,该层级会随着鼠标的位置变化而移动。

  4. 清理交互状态:当拆卸操作结束时,需要清除调试状态中的相关层级,以确保下一次操作时没有遗留数据或状态影响新的交互。

  5. 考虑新组的引入:为了更加有效地管理拆卸操作,可能需要引入新的分组来保存这些拆卸后的元素。这样,在后续的操作中,可以根据需要重新组织这些拆卸的元素。这个步骤可以暂时推迟,以避免引入过多的复杂性。

通过这些步骤,拆卸值的功能得以实现,用户可以在交互过程中自由地“拆卸”调试元素并重新组织它们的位置。
在这里插入图片描述

在这里插入图片描述

game_debug.variables.h: 介绍 DEBUGAddRootGroup

当前的目标是实现添加调试变量组(debug variable group)的功能。具体来说,目标是在不依赖上下文的情况下,能够将调试变量组直接添加到程序中,并使其能够与调试状态有效地交互。

  1. 添加调试变量组的需求:当前的系统中,调试变量参考(DEBUGAddVariable)需要一个实际的组引用。而目前的实现并没有提供这种引用。为了支持这一需求,需要能够创建并添加一个调试变量组,并且在不依赖上下文的情况下直接操作。

  2. 添加根组(RootGroup):通过定义一个函数(如 DEBUGAddRootGroup),可以在没有上下文的情况下创建根组。这一根组需要关联到当前的调试客户端状态,并初始化一些属性。例如,根组的“扩展”状态需要设置为 true,并且其子元素的内容应初始化为空。这确保了根组在后续的操作中能够正常工作,并且能够作为其他调试变量组的容器。

  3. 简化变量引用的设计:如果每个调试变量都已经包含一个分组,那么实际上不需要单独引用变量。此时,所有的调试变量可以直接与它们的分组关联,而不必创建额外的变量引用。因此,设计上可以简化,调试变量组直接与调试状态关联。

  4. 调试变量的引用和扩展:通过 debug add reference variable 等操作,能够将调试变量与对应的组进行关联。当创建根组时,可以立即将该根组的引用添加到调试变量中。这样就能在不扩展的情况下,确保所有调试变量和根组的关联正确无误。

  5. 默认行为:为了简化操作,根组和调试变量组可以在初始状态下设置为不扩展,避免不必要的扩展操作,直到有需要的时候再进行。

  6. 后续验证:完成添加调试变量组和引用的功能后,需要进行验证,确保这些操作按预期工作。通过检查添加的内容,确保其行为正常且符合预期。

通过这些步骤,能够更好地管理和组织调试变量组,从而提升调试功能的灵活性和可操作性。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

game_debug.cpp: 调用 DEBUGAddRootGroup

为了完成拖拽操作的实现,接下来的步骤包括创建一个根组,并将调试变量添加到该组中。详细步骤如下:

  1. 创建根组:首先,需要创建一个根组,并将其与调试状态(debug state)关联。这一根组将成为后续操作的基础,并且会成为当前调试变量组的容器。

  2. 添加调试变量到根组:接着,应该将调试变量引用添加到该根组。通过这样的操作,根组和调试变量建立起直接的联系。

  3. 层次结构的管理:为了处理这些调试变量和根组之间的关系,操作需要支持两级结构,而不依赖于上下文(context)。这样可以避免在每次交互中都需要获取上下文,而是直接通过调试状态和根组进行交互。

  4. 确保层次结构正常工作:通过添加 DEBUGAddRootGroup,可以获得一个根组,该根组随后会成为调试层次结构的一部分。根组本身并不会显示名称,而是作为一个“用户组”存在,用于组织和管理调试变量。

  5. 调整层次结构的引用:在完成根组和变量的关联后,需要确保所有的层次结构(如 at hierarchy)都能正确地接受调试变量组。每个层次结构项应该引用调试变量,而不是单独的上下文信息。

  6. 调试变量的引用方式:由于调试变量已经与根组关联,层次结构中的每个元素将直接引用调试变量,而不需要额外的引用操作。这简化了整个过程。

  7. 最终效果验证:完成这些操作后,调试系统应能正确地处理拖拽操作,并确保层次结构中的每个元素都能正确显示和管理。

通过这些步骤,拖拽操作的实现不仅能够正常运行,还能确保调试状态和变量组的管理更加高效和灵活。

在这里插入图片描述

在这里插入图片描述

game_debug_variables.h: 使 DEBUGAddVariableReference 接受 debug_variable *Group

现在需要实现变量的拆分功能,虽然相关代码已经基本完成,但目前仍然缺少一个关键部分,即在交互过程中真正地将变量添加到新的组中。具体的实现步骤如下:

  1. 调整交互逻辑

    • 目前 tear value 操作本身不需要在 end interaction 时执行任何特定的操作,实际的拆分逻辑应发生在交互过程中。
    • 需要一个方法,将当前正在交互的变量添加到新的组中,而不是仅仅创建空的组。
  2. 实现变量添加

    • 需要引入 debug add reference 函数,使其能够将变量添加到目标组中。
    • 现有的 DEBUGAddVariableReference 方法需要修改,使其不依赖于上下文(context)。
    • 由于 debug state 负责内存分配,因此可以直接在 debug state 中分配变量,而不需要依赖 context 来获取信息。
  3. 调整变量引用逻辑

    • 目前变量引用依赖 context 获取组信息,这部分需要改为直接从参数传递。
    • add reference 需要同时接受目标 groupvariable,然后将变量添加到 group 中。
    • 这意味着 context 不再是必要的,它可以被视为一个辅助工具,而不是强制依赖项。
  4. 优化父级引用

    • 变量引用通常会包含其父级信息,但在 tear value 操作中,某些情况下可能不需要父级信息。
    • 需要判断是否仍然需要维护父级关系,如果不需要,则可以省略相关字段。
  5. 调整 context 逻辑

    • 由于不再依赖 context 来获取 group,在 context 内部仍然可以提供一个封装层,以简化对 add reference 的调用。
    • context 仅仅作为辅助工具,确保调用时可以方便地获取 group 信息,但底层逻辑不再依赖于 context,而是直接操作 debug state
  6. 最终实现

    • 交互过程中,拆分变量时会创建一个新的组,并将当前变量添加到该组中。
    • 变量引用不再从 context 获取 group,而是通过传递参数直接指定 group
    • context 仅作为封装工具,以便在某些情况下提供便利,而不会影响底层逻辑的独立性。

这样,tear value 的功能就可以正常工作,并确保变量能够正确地拆分到新的组中,同时保持整体逻辑的清晰性和可维护性。

game_debug.cpp: 调用 DEBUGAddVariableReference

现在,我们需要确保 DEBUGAddVariable 可以被直接调用,并且支持将交互中的变量拆分到新的层级结构中。然而,在实现过程中,我们遇到了一些挑战,最终决定采用最初的实现方式,尽管这并不是最理想的方案,但由于层级树的父指针结构的限制,这成为了唯一可行的方法。

具体实现步骤:

  1. 允许变量拆分(tear-off)

    • 交互过程中,检测当前操作的变量,并允许其被拆分。
    • 创建一个新的层级结构,使得变量能够被正确地放入其中。
  2. 修正 DEBUGAddVariable 逻辑

    • 由于 DEBUGAddVariable 需要支持直接调用,我们需要确保它可以正确处理新的交互模式。
    • 这意味着在调用 DEBUGAddVariable 时,必须提供正确的上下文,并确保 statecontext 能够被正确传递。
  3. 调整 parent 指针逻辑

    • 由于层级树的组织方式,我们无法绕过 parent 逻辑,因此仍然需要维护 parent 指针。
    • 这样可以确保在交互过程中,变量仍然能够正确地插入到新的层级中。
  4. 修复 DEBUGAddVariableReference 参数问题

    • 之前调用 DEBUGAddVariableReference 时,遇到了参数类型不匹配的问题。
    • 需要调整 contextstate 的传递方式,确保能够正确调用 DEBUGAddVariableReference 方法。
  5. 最终优化

    • 通过修正 parent 逻辑,使变量拆分功能正确工作。
    • 调整 DEBUGAddVariableReference 以匹配正确的参数类型,确保 contextstate 传递无误。
    • tear-off 逻辑变得更稳定,确保变量能够被正确拆分到新的层级中,并正确显示在 UI 结构中。

最终,虽然不得不采用最初的 parent 逻辑,但现在变量拆分功能已经能够正确工作,交互逻辑得到了优化,并且 DEBUGAddVariable 也可以被直接调用,实现了所需的功能。

game_debug_variables.h: 使 DEBUGAddRootGroup 添加 debug_variable_reference

现在,我们需要确保 DEBUGAddRootGroup 能够正确工作。之前我们认为 RootGroup 不需要额外的变量引用,但事实证明它确实需要,因此我们必须首先创建一个 DEBUGAddVariable 才能继续操作。

具体实现步骤:

  1. 创建 DEBUGAddVariable

    • RootGroup 被添加时,必须首先创建一个 DEBUGAddVariable
    • 这意味着 DEBUGAddVariable 需要在 DEBUGAddRootGroup 之前被调用。
  2. 调整 DEBUGAddRootGroup 的逻辑

    • 之前 DEBUGAddRootGroup 只是简单地创建了一个组,但现在它必须在创建 RootGroup 之后,立即创建一个变量引用。
    • 这个变量引用会被关联到 RootGroup,确保调试系统可以正确地追踪变量关系。
  3. 修改参数传递

    • RootGroup 的变量引用需要接受 statevar 参数。
    • RootGroup 的 ID 需要传递 0,因为它是层级结构的顶层。
  4. 调用方式

    • DEBUGAddRootGroup 现在会调用 DEBUGAddVariable,并将 statevar 作为参数传递进去。
    • 之后,继续进行正常的层级操作,确保 RootGroup 被正确地添加到调试系统中。
  5. 最终优化

    • 由于 DEBUGAddRootGroupDEBUGAddVariableReference 的逻辑基本相同,我们可以优化代码,减少重复部分。
    • 通过抽取公共逻辑,提高代码的可读性和可维护性。

最终,通过这些修改,我们可以确保 DEBUGAddRootGroup 能够正确地创建并管理 RootGroup,同时支持变量引用的正确传递和关联,从而保持整个调试系统的完整性和稳定性。

game_debug_variables.h: 介绍新的 DEBUGAddRootGroup,并将原始方法重命名为 DEBUGAddRootGroupInternal

现在,我们可以调用 DEBUGAddRootGroup 并确保它能正确运行。主要的调整如下:

1. 封装 DEBUGAddRootGroup internal

  • 这是一个仅供内部使用的工具函数,外部不会直接调用它。
  • 它封装了创建 RootGroup 以及相关的 DEBUGAddVariable 的逻辑,确保代码整洁。

2. 不同调用方式

  • 第一种调用方式:使用 context 来添加 RootGroup
  • 第二种调用方式:直接使用 DEBUGAddVariableReference,不依赖 context,而是直接传递 stategroup

3. 具体实现

  • DEBUGAddRootGroup internal 现在能从多个地方被调用。
  • context 方式下,它通过 context 进行层级管理。
  • 在非 context 方式下,直接使用 DEBUGAddVariableReference,手动管理 group 层级。

4. 代码优化

  • 由于 DEBUGAddRootGroup 发生了变化,我们需要调整 AddHierarchy,使其恢复到之前的逻辑。
  • 通过修正编译器错误,我们确保 DEBUGAddRootGroup 现在的实现与最初设想的一致,不会影响其他部分的功能。

5. 测试 tear-off 交互

  • 现在,我们可以测试 tear-off(分离变量)。
  • tear-off 逻辑中,我们监听鼠标右键按下的事件来执行 tear-off 操作。
  • 我们通过 DEBUGAddVariableReference 让变量能够正确加入到 RootGroup

6. 最终检查

  • 确保 tear-off 功能能正确地创建并管理变量的层级关系。
  • 观察 tear-off 交互是否符合预期,比如按下鼠标右键时是否正确地将变量分离并移动到新的层级。

通过这些调整,我们优化了 DEBUGAddRootGroup 的实现,并确保 tear-off 交互能够正常运行,从而增强了整个调试系统的稳定性和可用性。
在这里插入图片描述

在这里插入图片描述

运行游戏并测试新的拆分功能

现在,我们可以拖拽并创建新的分组,已经可以正确地在界面上生成多个独立的调试变量组。这一调整基本实现了 tear-off(分离变量)功能,使得多个变量组可以独立存在。然而,目前仍然存在一个问题:不同变量组的展开/折叠状态是同步的,因为它们共享相同的状态管理方式。

当前进展

  • 现在可以创建多个独立的变量组,并且它们能够正常交互。
  • 拖拽变量可以生成新的变量组,并且变量能够正确地被分配到新的组中。
  • 问题点:变量组的展开/折叠状态是共享的,导致多个变量组的状态无法独立维护。

如何解决展开/折叠状态共享的问题

针对这个问题,有两种解决方案:

方案 1:在变量组内部存储自己的展开/折叠状态
  • 目前,所有变量组可能都在使用相同的 expansion state 变量,而不是各自维护独立的状态。
  • 可以在 group 结构体中添加一个 expanded 标志,每个 group 实例单独存储自己的展开/折叠状态。
  • 这样,每个 group 的展开/折叠不会影响其他 group,从而解决状态同步问题。
方案 2:使用唯一 ID 作为状态键
  • 另一种方法是,将展开/折叠状态存储在一个全局映射(如 dictionary)中,并使用 group 的唯一 ID 作为键来访问它。
  • 这样,每个 group 通过自己的 ID 获取和存储展开/折叠状态,从而确保它们不会互相影响。
  • 这种方式适用于更复杂的状态管理需求,比如需要持久化状态或在不同会话之间保持一致。

下一步优化

  • 确认当前 group 结构体的状态管理方式,检查是否所有 group 共享同一个 expansion state 变量。
  • 选择合适的方案,决定是让每个 group 独立存储状态(方案 1),还是用映射表来管理状态(方案 2)。
  • 实现独立的展开/折叠逻辑,确保每个 group 可以独立控制自己的 UI 展示状态,而不会影响其他 group

通过这些调整,我们可以进一步优化 tear-off 逻辑,使得多个变量组不仅可以独立拖拽,而且可以独立控制展开/折叠状态,从而提供更灵活的调试体验。
在这里插入图片描述

在这里插入图片描述

game_debug.cppgame_debug.h: 介绍 DebugInteraction_MoveHierarchy

目前,我们暂时不专注于变量组的展开/折叠状态管理,而是先实现另一个重要的功能:允许用户在放置变量组后,对其进行拖动和重新定位。目前的交互方式仅支持从原始位置拖拽出新的变量组,但一旦变量组被放置,就无法再移动。因此,我们的目标是为现有的调试 UI 添加一个移动功能,使得变量组可以在屏幕上自由调整位置


实现思路

  1. 引入“移动层级”功能

    • 我们定义一个新的 UI 交互方式,比如 MoveHierarchy,它的作用是让变量组能够被拖拽移动,而不是仅仅分离。
    • 这类似于 Tear-off 功能,但区别在于 Tear-off 负责从原始变量列表中分离,而 MoveHierarchy 允许调整已放置变量组的位置。
  2. 处理鼠标拖动事件

    • 当用户开始拖动某个变量组时,系统需要记录当前拖动的是哪个 Hierarchy ID,并跟踪鼠标位置。
    • 逻辑类似于 Tear-off 处理鼠标交互的方式,但这次不创建新的变量组,而是直接调整现有变量组的位置。
  3. 存储变量组的位置

    • 目前系统没有任何机制来存储变量组的绝对位置,而所有组的显示位置可能是基于 UI 布局计算的。
    • 需要在 Hierarchy 结构体中新增一个 Position 属性,记录当前变量组的屏幕坐标。
    • 当用户拖动时,更新该 Position 值,并重新渲染 UI。
  4. 检测拖拽完成

    • 监听鼠标释放事件,当拖动结束时,更新变量组的最终位置。
    • 确保拖拽过程中不会影响变量组的层级结构(Hierarchy),仅调整其视觉上的位置。

具体实现步骤

  1. 在 UI 逻辑中添加 MoveHierarchy 入口

    • 在适当的位置(如鼠标点击事件或拖拽事件)增加 MoveHierarchy 逻辑,以便能够检测到用户尝试移动变量组。
  2. 修改变量组结构体,添加 Position 字段

    • 允许变量组记录自己的位置,使其可以随用户交互而变化。
  3. 监听拖拽事件

    • 记录当前正在拖动的 Hierarchy ID,并跟踪鼠标位置的变化,以更新变量组的 Position
  4. 在 UI 渲染时使用 Position

    • 确保 Hierarchy 的 UI 位置是基于 Position 渲染,而不是固定的布局规则。

潜在问题与解决方案

  1. 如何区分“拆分变量组”(Tear-off)和“移动变量组”(Move)?

    • 可以使用不同的鼠标按键(如 Tear-off 使用 右键,而 Move 使用 左键)。
    • 也可以添加一个模式切换,比如“编辑模式”下可以拖动,而普通模式下只能拆分。
  2. 如何防止变量组移动到不可见区域?

    • 在拖动过程中添加边界检测,确保 Position 始终在屏幕范围内。
  3. 拖拽时是否需要吸附功能?

    • 可以实现吸附到网格或其他 UI 组件的对齐功能,使得变量组更易整理。

下一步

  • 实现 MoveHierarchy 逻辑,让变量组支持拖拽。
  • 修改渲染方式,使变量组位置受 Position 控制
  • 优化交互逻辑,区分 MoveTear-off

通过这些改进,用户可以更自由地调整变量组的位置,使得调试界面的交互体验更加流畅。
在这里插入图片描述

在这里插入图片描述

game_debug.cpp: 为每个 Hierarchy 绘制 MoveBox

目前,我们正在尝试实现变量组的拖动功能,并探索最简单的实现方式。一个可能的方法是,在渲染层级(Hierarchy)时,为每个变量组创建一个“移动控制区域”,用于检测用户的拖动操作。该区域可以是变量组的一部分,例如一个小方块,作为交互热区(Hotspot)。


实现思路

  1. 在渲染层级时添加“移动框”

    • 在绘制变量组时,同时渲染一个小方块,作为拖动热区。
    • 该方块的 UI_ID 需要与对应的变量组关联,以便识别拖动操作。
  2. 放置热区位置

    • 该方块可以位于变量组的右上角、左上角,或者稍微偏移,以便用户可以清楚地区分拖动区域和其他交互区域(如展开/折叠按钮)。
    • 通过 push_rect 之类的方法,创建一个 UI 组件,代表这个热区。
  3. 检测用户拖动操作

    • 当用户点击热区并开始拖动时,记录当前选中的 Hierarchy ID
    • 追踪鼠标位置,并更新变量组的 Position 值,使其跟随鼠标移动。
  4. 渲染时更新变量组位置

    • Position 发生变化时,重新计算变量组的 UI 坐标,确保其显示在正确的位置。

具体实现步骤

  1. 定义热区并渲染

    • Hierarchy 渲染逻辑中,添加 MoveBox(拖动热区)。
    • 位置稍微偏离 Hierarchy,避免干扰其他 UI 交互。
  2. 处理拖动事件

    • 监听 MoveBox 的鼠标事件:
      • 鼠标按下:记录当前拖动的 Hierarchy ID
      • 鼠标移动:更新 HierarchyPosition 值,使其跟随鼠标。
      • 鼠标释放:停止拖动,并存储最终位置。
  3. 优化交互

    • 调整热区大小和位置,使得拖动操作易于使用,不影响其他 UI 交互。
    • 边界检测,防止变量组拖出可视区域。

下一步

  • 实现热区检测逻辑,确保拖动操作能正确识别用户的交互意图。
  • 调整 UI 细节,优化热区位置,使其既不影响用户操作,又能直观地表示拖动功能。
  • 存储和恢复位置,确保变量组的位置在刷新后不会重置。

这样,用户就可以通过拖动热区,自由移动变量组的位置,进一步提高交互体验。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

game_debug.cpp: 实现移动 Hierarchy 的功能

我们在交互逻辑中引入了 拖动检测区域(MoveBox),用于判断用户是否正在拖动层级(Hierarchy)。当前的实现思路是:当鼠标进入该区域时,标记当前的交互状态,使系统知道用户想要移动某个层级对象。


具体实现逻辑

  1. 检测鼠标是否进入拖动区域

    • 在 UI 交互更新逻辑中,检查鼠标是否位于 MoveBox 内部。
    • 如果鼠标位于该区域内,就将当前的交互状态更新为“即将移动层级”(Move Hierarchy)。
  2. 标记交互状态

    • 定义 NextHotInteraction 变量,用于存储当前用户的交互目标。
    • 在检测到鼠标进入 MoveBox 后,设置 NextHotInteraction 为移动层级操作。
  3. 调整交互标记逻辑

    • 使用 NextHotHierarchy 变量来存储即将被拖动的层级 ID。
    • 这意味着:
      • 在下一帧,系统可以根据 NextHotHierarchy 确定是否需要移动某个层级。
      • 交互逻辑可以继续扩展,使得 层级拖动、交互切换等功能更加统一
  4. 未来改进方向

    • 目前,我们在不同地方使用了多个类似 NextHotHierarchy 这样的标记变量,它们的职责有些重叠,可能会导致逻辑变得复杂。
    • 计划在后续重构时,优化这些变量的管理方式,减少冗余,提高代码清晰度。

后续计划

  • 优化 NextHotHierarchy 变量的管理方式,让层级交互更清晰。
  • 在交互逻辑中更好地整合拖动功能,确保拖动、选择、展开/折叠等功能互不干扰。
  • 优化 UI 反馈机制,让用户更清晰地感知当前的交互状态。

这样,我们的层级拖动交互就会更加自然、直观。
在这里插入图片描述

game_debug.h: 在 debug_state 中添加 debug_hierarchy *NextHotHierarchy

我们在实现层级拖动交互的过程中,调整了 NextHotHierarchy 变量的使用方式,并尝试将其与 DraggingHierarchy 关联,以便在检测到鼠标交互后,可以正确地执行拖动操作。


具体实现步骤

  1. 设定 NextHotHierarchy

    • 在 UI 交互逻辑中,检测鼠标是否进入 MoveBox(拖动触发区域)。
    • 如果鼠标进入该区域,则将 NextHotHierarchy 设置为当前的层级对象。
  2. 更新 DraggingHierarchy

    • debug_state HotHierarchy 执行时,检查 NextHotHierarchy 的值。
    • 如果检测到 NextHotHierarchy,则将 DraggingHierarchy 赋值为 NextHotHierarchy,从而让系统知道哪个层级正在被拖动。
  3. 拖动交互逻辑

    • debug_get_interact_HotInteraction 中,确保 DraggingHierarchyinteraction 结束时被正确清除,以防止错误状态残留。
    • MoveBox 交互过程中,更新 NextHotInteraction,确保 UI 层可以正确感知拖动状态。

遇到的问题

  • 拖动状态未正确更新

    • 代码逻辑应该正确,但 NextHotHierarchy 似乎没有按预期生效,导致 DraggingHierarchy 仍然是 0,而未能正确切换到新的层级对象。
  • 可能的原因

    1. NextHotHierarchy 的赋值未能正确触发,可能是 MoveBox 计算区域有误,导致鼠标进入时未能正确触发交互更新。
    2. DraggingHierarchy 的更新逻辑可能在 interaction 结束时被错误地重置回 0,导致拖动状态丢失。

下一步改进

  1. 调试 MoveBox 碰撞检测

    • 确保 MoveBox 计算正确,并且 NextHotHierarchy 能够在鼠标进入时成功更新。
  2. 优化 DraggingHierarchy 赋值逻辑

    • 确保 DraggingHierarchy 只有在 interaction 结束后才被清除,而不会在鼠标仍然处于拖动状态时提前归零。
  3. 进一步简化 NextHotHierarchy 的管理

    • 可能需要优化变量的使用方式,以减少不必要的重复赋值,使交互逻辑更清晰。

通过这些调整,我们可以确保拖动交互能够按预期运行,让层级对象能够顺畅地拖拽和移动。
在这里插入图片描述

在这里插入图片描述

调试器: 进入 DraggingHierarchy

在调试 NextHotInteraction 赋值逻辑的过程中,发现它从未被正确设置,导致 DraggingHierarchy 无法更新,最终无法实现拖动层级的功能。


具体分析

  1. 检测 Move Box 逻辑

    • 发现 NextHotInteraction 变量未被正确赋值,意味着鼠标交互逻辑可能存在问题。
    • 需要确认 Move Box 的矩形区域是否正确计算,并检查鼠标是否真的进入了该区域。
  2. NextHotInteraction 赋值问题

    • 代码逻辑预期:如果鼠标进入 Move Box 区域,则 NextHotInteraction 需要被赋值当前的层级对象。
    • 实际情况:NextHotInteraction 似乎始终为空,说明 if 判断条件从未成立。
  3. 交互状态管理

    • 代码确保 DraggingHierarchyinteraction 结束时会被清除,但 NextHotInteraction 未能正确赋值,导致 DraggingHierarchy 无法更新。
    • 可能 NextHotInteraction 赋值代码从未被执行,或者 Move Box 检测机制未正确触发。

可能的原因

  1. 鼠标位置检测失败

    • 需要确认 Move Box 的边界是否正确计算,可能当前 Move Box 坐标与鼠标位置计算不匹配,导致 if (is_inside_rect(...)) 条件一直为 false
  2. NextHotInteraction 赋值逻辑未触发

    • 可能是在 UI 逻辑更新的顺序中,NextHotInteraction 赋值过程被某些条件限制,导致其始终为空。
  3. 事件优先级导致的覆盖

    • 可能 NextHotInteraction 被设置后,又在某些逻辑路径上被覆盖或清空,从而无法传递到 DraggingHierarchy

改进方案

  1. 调试 Move Box 检测

    • 直接打印 Move Box 的计算结果,确保它的坐标范围正确。
    • if (is_inside_rect(...)) 语句中加 printflog 语句,确认鼠标是否真的进入了 Move Box 区域。
  2. 强制设置 NextHotInteraction

    • 临时手动设置 NextHotInteraction 为某个有效值,观察是否能够正确触发 DraggingHierarchy 更新。
  3. 检查 NextHotInteraction 是否被清除

    • 在整个交互过程中打印 NextHotInteraction 的值,确认它不会被错误地提前清空。

通过这些步骤,我们可以确保 NextHotInteraction 赋值正确,使 DraggingHierarchy 能够更新,从而成功实现层级拖动交互功能。

game_debug.cpp: 清除 NextHotHierarchy

在当前的交互逻辑中,NextHotHierarchy 的清除方式实际上并不影响核心功能,因为唯一真正需要被清除的变量是 debug_interaction_none。但为了便于观察 NextHotHierarchy 的变化,仍然保留了清除逻辑。


具体分析

  1. NextHotHierarchy 清除逻辑

    • 代码当前的交互管理方式会在特定情况下清除 NextHotHierarchy,但从技术角度来看,这并不真正影响交互结果。
    • 事实上,唯一必须被清除的变量是 debug_interaction_none,因为它的状态会直接影响交互逻辑的下一步处理。
  2. 调试目的

    • 尽管 NextHotHierarchy 的清除不是必要的,但仍然保留它,以便可以通过观察其值的变化,确认交互流程是否按预期执行。
    • 这样在调试时,可以更容易地排查 NextHotHierarchy 是否在错误的时间点被清除,或是否未能正确设置。

优化方案

  1. 保留 NextHotHierarchy 的调试输出

    • 可以在交互逻辑执行过程中,打印 NextHotHierarchy 的值,以观察其变化情况。
    • 例如,在 NextHotHierarchy 赋值和清除的位置加上日志输出,以便确认它在正确的时间点发生变更。
  2. 检查 debug_interaction_none 的状态

    • 确保 debug_interaction_none 仅在必要的情况下被清除,以免意外影响 NextHotHierarchy 或其他交互状态。
  3. 优化清除逻辑

    • 既然 NextHotHierarchy 的清除并非必要,可以考虑在调试完成后移除这部分代码,以减少不必要的状态变更,提高交互逻辑的清晰度。

通过这些改进,可以确保 NextHotHierarchy 仅在必要时被清除,同时保持调试时的可观测性,以便更好地理解交互逻辑的运行情况。
在这里插入图片描述

game_debug.cpp: 介绍 DEBUGTextLine 以显示交互状态

在当前的交互逻辑调试过程中,我们尝试通过 DEBUGTextLine 输出调试信息,以确保 debug_state.NextHotHierarchy 在交互时是否被正确设置。


具体分析

  1. 调试 NextHotHierarchy
    • debug_interact 执行时,增加 DEBUGTextLine 以检查 NextHotHierarchy 的状态。
    • 目标是确定在交互过程中,NextHotHierarchy 是否被正确赋值,或者是否存在某些条件导致它始终为零。
      在这里插入图片描述

在这里插入图片描述

  1. 检查 DEBUGBeginInteract 的执行情况

    • 由于 NextHotHierarchy 似乎已经正确检测到交互对象,但交互仍未触发,因此在 DEBUGBeginInteract 处设置断点,以确认函数是否被正确调用。
    • 这有助于排查 DEBUGBeginInteract 是否由于某些条件未被触发,例如鼠标按键未被检测到或其他条件限制。
      断点没进来
      在这里插入图片描述
  2. 发现的问题

    • 通过调试信息发现,NextHotHierarchy 确实被正确识别,但 DEBUGBeginInteract 并未触发交互。
    • 可能的原因:
      • PlatformMouseButton_Left 事件未被检测到(例如输入状态未正确更新)。
      • DEBUGBeginInteract 逻辑存在其他前置条件,导致它未能正常执行。

优化方案

  1. 增加更多调试信息

    • 继续在 DEBUGBeginInteract 附近增加 DEBUGTextLine,以跟踪代码执行流,确保能够正确进入 DEBUGBeginInteract
  2. 检查鼠标输入处理

    • 确保 PlatformMouseButton_Left 事件能够被正确触发,检查输入管理逻辑是否存在问题,例如:
      • 鼠标点击未注册(可能由于输入系统未激活)。
      • 事件处理顺序导致 PlatformMouseButton_Left 被提前清除或覆盖。
  3. 确认 NextHotHierarchy 赋值逻辑

    • 观察 NextHotHierarchy 是否可能在某些情况下被错误清除,例如:
      • 交互状态在执行 DEBUGBeginInteract 之前被重置。
      • NextHotHierarchy 被其他逻辑覆盖。

通过这些改进,我们可以更准确地定位问题,使 DEBUGBeginInteract 正确触发,确保 NextHotHierarchy 在交互时能够正常工作。

game_debug.cpp: 让检查发生在 HotInteraction

在当前交互逻辑的调整中,我们发现 HotInteraction 的逻辑存在问题,导致 DEBUGBeginInteract 不能正确触发。


具体分析

  1. 问题原因

    • HotInteraction 应该是检测交互的主要判断条件,而之前的代码错误地使用了 next_hot_hierarchy
    • HotInteraction 本应始终被检测,并且如果存在有效交互,就应该直接执行,而不需要额外的条件判断。
    • 之前的逻辑错误导致 HotInteraction 没有正确生效,因此交互未能触发。
  2. 调整方案

    • 修正 HotInteraction 的逻辑
      • 交互逻辑调整为:如果存在 HotInteraction,那么直接设置 HotInteraction,不进行额外的条件判断。
      • 如果 HotInteraction 为空,则进入默认交互处理流程。
    • 移动 else 分支
      • else 分支逻辑应该在 HotInteraction 为空时才执行,以确保没有交互时仍然有合理的默认行为。
  3. 最终逻辑

    • 如果 HotInteraction 存在,则直接执行交互逻辑,无需额外判断。
    • 如果 HotInteraction 为空,则执行默认交互处理逻辑,以确保界面交互的完整性。
    • HotInteraction 应该在整个流程的核心位置,避免错误触发或遗漏交互检测。

优化后的代码逻辑

  1. 修正 HotInteraction 检测

    if (HotInteraction) {// 直接执行交互逻辑current_interaction = HotInteraction;
    } else {// 如果没有 HotInteraction,则进入默认处理逻辑handle_default_interaction();
    }
    
  2. 优化默认交互逻辑

    void handle_default_interaction() {if (user_clicked_on_nothing) {reset_interaction_state();}
    }
    

改进后的交互逻辑

  • 修正了 HotInteraction 作为主要交互检测变量的错误使用。
  • 确保交互逻辑在 HotInteraction 存在时始终执行,不受额外条件影响。
  • 增加 else 分支,确保无交互时的默认处理逻辑仍然有效。
  • 让代码结构更清晰,更容易理解 HotInteraction 何时生效。
    在这里插入图片描述

在这里插入图片描述

运行游戏并再次测试

在修正了代码逻辑后,问题得到了有效解决。最初的错误源于代码的设计步骤中出现了一个疏忽,导致 HotInteraction 的处理方式不符合预期。通过回顾并修正了这一点,交互的逻辑终于变得如预期般顺畅。


当前状态

  1. 代码修复

    • 修正了 HotInteraction 的判断和执行逻辑,确保了交互过程中的有效性。
    • 通过调整交互逻辑,现在能够正确地处理交互状态。
  2. 功能恢复

    • 所有其他功能都恢复正常,交互和拖拽操作都可以顺利执行。
    • 代码中实现了“拖拽”操作,确保了界面元素的流畅移动。
  3. 修正后的行为

    • 之前的问题已解决,现在的代码能够支持拖拽操作,用户可以像预期的那样对元素进行交互。
    • 系统可以处理拖拽及拖拽后的状态变化,确保所有的交互和操作都能无误地完成。

总结

在经历了修复后的调整后,系统恢复了预期的交互功能,并且可以进行拖拽操作,界面响应也非常流畅。之前的设计疏忽已经被修正,现在系统的交互逻辑更加稳定和可靠。

game_debug.cpp: 移动 Assert(Var)

代码调整与优化

在调试过程中发现 DEBUGEndInteract 似乎默认假设交互状态始终是“正在交互中”,但这并不是我们真正想要的逻辑。因此,需要调整代码逻辑,使其不再强制要求这一点。


修正内容

  1. 移除不必要的断言

    • 发现 DEBUGEndInteract 仍然包含一个不必要的断言(assert),原本的逻辑假设交互状态在调用该函数时始终有效,但实际上并非如此。
    • 由于这并不是我们想要的行为,因此需要将其移除,避免程序因无意义的断言而崩溃。
  2. 调整交互逻辑

    • 由于 ToggleValue 在所有情况下都应该有明确的状态,因此可以考虑在更合适的位置执行该操作,避免在无效的交互状态下进行无意义的处理。

调整后的预期行为

  • DEBUGEndInteract 不再假设交互始终存在,而是更加灵活地处理交互状态。
  • 确保 ToggleValue 在适当的地方执行,避免因状态不一致导致的错误。
  • 代码更加健壮,不会因多余的断言或错误假设导致程序异常。

总结

这次调整主要是针对 DEBUGEndInteract 逻辑进行了优化,使其不再依赖于始终存在的交互状态。同时,合理地调整 ToggleValue 的执行位置,确保其行为符合预期,避免引发额外的问题。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

运行游戏并检查拆分和移动功能

目前,整个 UI 系统的构建已经逐步完善,虽然仍然需要一些时间来打磨和优化,但大部分核心功能已经基本就绪。从整体来看,搭建一个合理的 UI 系统只需要几个小时,这个时间并不算长。

当前 UI 系统已经具备大部分基础功能,后续的主要任务是进行优化和美化,使其更加精致、易用。目前,系统的整体结构已经趋于完善,可以支持各种复杂的交互操作,并且在运行时能够正常工作,这一点非常重要。尽管当前的界面可能在视觉上仍然显得粗糙或简陋,但已经能够实现预期的功能,后续只需要进一步调整和优化,使其更加美观和流畅。

没有问题

目前没有任何问题需要解决,因此没有额外的内容需要讨论,一切都已完成,没有需要关注的地方。

你提到封装很重要,但不是以 OOP 的方式。你能详细说明一下你的意思吗?

封装的作用在于划分项目的不同部分,使其不被外部直接访问。当确定某些部分已经完全定型,并且不希望外部代码直接依赖它们时,就可以将这些内容从公共接口中移除。例如,可以将对外开放的函数保留在头文件(.h),而将实现细节放入源文件(.cpp),这样可以确保外部代码只调用预期的接口,而不会依赖具体的内部实现,从而提高代码的清晰度和模块化程度。

菜单项的垂直间距是均匀的吗?看起来有点奇怪

当前的文本对齐问题是由于未使用字体的实际间距,而是依赖矩形的间距进行排布。这样一来,如果某些文本没有下降部(descenders,例如字母g、j、p等的下部),它们所占的空间会相对较小,导致整体排列在视觉上显得不均匀。换句话说,不同字符的高度差异使得它们在垂直方向上的间距看起来不一致。要解决这个问题,需要基于字体的实际行高(line height)或基线对齐,而不是简单依赖矩形尺寸来计算间距。

game_debug.cpp: 使用 LineAdvance 调整行间距

当前的排版方式主要依赖于文本的实际边界(bounds)来决定布局,但由于某些字符没有下降部(descenders)或上升部(ascenders),导致文本在垂直方向上的对齐可能会出现不均匀的情况。为了解决这个问题,可以采用统一的行高(line advance)来确保所有文本元素的间距一致。

首先,调整 DEBUGGetTextSize 计算文本边界时,强制其符合统一的行高,而不是使用文本自身的边界。具体操作是,在计算文本大小时,将 bounds.min.y 设为 bounds.max.y - LineAdvance,确保每行文本的高度一致。

接着,在排版时,对于非文本元素(例如按钮或其他 UI 组件),由于它们没有行高的概念,因此需要额外添加一个 spacing_y 参数,使它们能够与文本对齐,同时保留一定的间距。这样一来,所有元素的间距都能够遵循相同的逻辑,而不会因文本字符的特性不同而出现视觉上的错位。

然后,在最终的布局计算中,确保 LeftPXTopPy 计算正确,使其能够正确地匹配 text boundsMin.xMax.x,以及 TopPy - LineAdvance 来定义统一的基准线。这样,无论文本包含何种字符,其排布都会保持一致。

最后,通过变量优化和代码重构,消除可能的变量遮蔽(shadowing)问题,确保代码逻辑清晰,同时避免因不同字符高度不一致导致的 UI 变形问题。经过这样的调整,文本对齐方式将更加稳定,使得所有 UI 元素的间距更加整齐统一。
在这里插入图片描述

在这里插入图片描述

拖动 hierarchies 时为什么会有这么大的延迟?还是只是 Twitch 的流媒体问题?

在拖动 UI 元素(例如心形图标)或进行某些交互时,存在明显的延迟和卡顿现象。经过分析,发现这种延迟主要来源于调试系统的重置(debug system reset),而不是游戏本身的性能问题。

目前的调试系统在执行调试数据整理(debug collation)时开销较大,这部分逻辑尚未进行优化。当调试数据整理触发时,会导致短暂的卡顿,因为当前的实现方式会一次性清理并重新整理所有调试数据,而不是采用增量更新的方式。

如果禁用调试数据整理(debug collation),可以明显看到拖动交互是流畅的,没有任何延迟。这表明问题并不出在 UI 渲染或游戏逻辑上,而完全是由于调试系统的重置过程导致的性能损耗。

目前的调试系统每次进行数据整理时,都会清空整个调试状态,这种做法导致短暂的帧率下降。一个更优的方案是采用增量式滚动缓冲区(incremental rolling buffer),使调试数据能够逐步更新,而不需要每次都完全重置整个状态。如果能够实现这一点,不仅能消除调试系统引起的卡顿,还能改善整体调试体验,使得 UI 交互更加顺畅。

拆分出的界面实例是应该具有相同的层级结构,还是它们应该允许独立钻取不同的内容?

当前的目标是改进 UI 交互,使得不同的调试面板(或层级结构)在拆分和调整时可以独立展开,而不会相互影响。同时,希望能够支持更自由的拖放操作,使用户可以更灵活地管理调试面板和数据分组。

目前的设计思路包括:

  1. 独立展开不同层级:允许用户单独展开某个层级,而不影响其他同类型的面板。这样可以更直观地查看不同类别的数据,而不导致所有相同类型的面板都同时发生变化。

  2. 可扩展的拖放交互

    • 允许用户拖拽某个调试项,并将其附加到现有面板中,而不是只能进行单独的拆分操作。
    • 例如,用户当前在一个面板中查看某项调试数据(如 UI 状态),如果想要将 “Debug Camera” 添加到当前面板中,应该能够轻松拖拽并合并,而不需要额外的步骤。
  3. 创建自定义分组

    • 允许用户通过拖拽方式创建新的调试数据分组,而不是只能使用固定的预设分组。
    • 例如,用户可以选择一部分调试项,并将它们拖动到一个新创建的容器中,以便组织数据。

实现这一功能的技术要点:

  • 在拖放时增加逻辑,检测目标区域是否允许合并,如果允许,则将拖拽的元素插入到目标面板或分组中。
  • 在界面渲染时,确保不同的面板状态是独立存储的,而不会互相干扰。
  • 提供合适的 UI 反馈,比如在拖拽过程中高亮目标区域,以提示用户可以放置数据的位置。

这项改进能够使调试 UI 更加灵活,允许用户根据自己的需求定制界面布局,提高操作效率和可视化效果。

我注意到你使用 internal 关键字,我之前没见过,它的作用是什么?

internal 只是一个宏定义,它的作用是被 #define 预处理器指令定义为 static。换句话说,在代码中看到的 internal 关键字,实际上在编译时都会被替换成 static

作用

在 C 或 C++ 代码中,static 关键字用于控制变量或函数的作用域:

  1. 对于函数:声明为 static 的函数仅在定义它的 编译单元(即源文件 .c.cpp)内部可见,不能被其他文件访问。
  2. 对于变量
    • 全局 static 变量:作用域限制在当前编译单元,不会被其他文件访问。
    • 局部 static 变量:在函数内部声明的 static 变量,其值在整个程序运行过程中保持不变,不会在每次函数调用时重新初始化。

为什么使用 internal

通常,internal 只是用作 static 的别名,这是代码风格上的选择:

  • 提高可读性internal 让代码更具语义化,明确表示这个函数或变量是“内部使用的”,不会暴露给其他编译单元。
  • 减少命名冲突:由于 static 变量和函数的作用域仅限于当前文件,避免了多个文件中同名函数或变量引起的冲突。
  • 符合特定代码规范:一些代码库或项目使用 internal 代替 static,统一风格。

示例

#define internal staticinternal int InternalFunction() {return 42;
}int PublicFunction() {return InternalFunction();
}

在这里,internal 只是 static 的替代写法,使 InternalFunction 仅在当前文件可见,外部文件无法访问它。

总结

  • internal 只是 #define internal static 的宏定义,本质上就是 static
  • 主要用于标记仅限当前编译单元使用的函数或变量
  • 这样做的目的是增强可读性,减少命名冲突,同时符合某些项目的代码风格规范。

抱歉,我加入得比较晚,出于好奇,你在拖动子部分时使用的容差(像素)是多少?

当前的实现方式并没有使用特定的像素阈值来判断拖拽子区域的操作,而是直接通过 右键点击 触发该功能。

在交互过程中,用户可以通过 按住右键 并拖拽来执行子区域的拆分或拖动,而不依赖于特定的像素距离来判定是否进行拖拽。这种方式相对简单,避免了设定拖拽敏感度的问题,也确保了交互的直接性和即时反馈。

从逻辑上来看,这种方法省去了对鼠标位移距离的检测,不需要设定一个固定的拖拽判定阈值,例如 “拖拽超过 X 像素才会生效” 之类的规则。这样,只要用户按住右键并移动鼠标,就会直接进入拖拽模式,从而实现对界面子部分的拆分或移动。

这种实现方式的优点是:

  • 减少误触:相比于基于像素阈值的拖拽判定,右键拖拽的方式更明确,不容易因为鼠标的微小移动而触发拖拽。
  • 交互直观:用户只需记住使用右键即可拆分和拖动,而不需要关心鼠标移动的距离是否超过某个限制。
  • 代码逻辑简洁:不需要额外检测鼠标位移的阈值,使得实现方式更加直接和高效。

如果未来需要改进,可以考虑引入 拖拽判定阈值,比如设定一个最小拖拽距离(如 5-10 像素),以避免误操作。

你提到在工作中使用元编程来处理链表,你是指什么?

在当前的实现中,已经有一个 用于生成结构化内容的系统,它能够自动创建一些结构化的元素,使得开发过程中不需要手动重复编写类似的内容,而是由系统 自动生成 这些部分。

这个系统的作用主要是处理 超结构化(super structural) 的内容,也就是一些 通用模板或固定模式的结构,可以在不同的情况下重复使用。例如,某些 UI 组件、调试信息面板、数据可视化布局等,都可以通过这个系统 自动构建,而不是手动编写每一个部分。

具体来说,这个系统的特点包括:

  • 自动生成基础结构:不需要手动定义某些 UI 元素或调试窗口,系统会根据已有的逻辑自动创建它们,并填充对应的数据或信息。
  • 减少重复代码:开发人员不需要手动编写相同的代码片段,而是可以依赖系统提供的生成机制,让代码更加简洁和模块化。
  • 动态适应需求:如果数据或逻辑发生变化,这个系统能够动态地调整结构,而不需要手动修改代码,使得界面和功能更加灵活。
  • 提高开发效率:对于某些需要频繁调整的内容,例如调试界面、工具面板、数据展示等,使用这个系统可以快速调整布局,而不需要手动更新每一个元素。

简单来说,这个系统相当于一个 自动化的结构生成工具,它能够在运行时或开发时帮助创建一些固定模式的内容,从而简化工作流程,提高开发效率。

我错过了 90% 的直播,想请教一下游戏设计的入门建议?有哪些值得做的事情和需要避免的坑?

如果有兴趣进入游戏设计领域,以下是一些 值得注意的做法和避免的错误,以及一些简单而有效的建议:

做法(Do’s):

  1. 从基础做起

    • 了解并掌握游戏设计的基本原则,如玩家体验、游戏机制、平衡性等。
    • 学习并熟练使用一些游戏设计工具和引擎,比如Unity、Unreal Engine或Godot等。
  2. 注重玩家体验

    • 游戏设计的核心是 玩家体验,确保游戏设计考虑到玩家的互动、反馈以及可玩性。
    • 在设计游戏时,可以多进行 用户测试,通过玩家的反馈来调整和优化设计。
  3. 从简单的项目开始

    • 不要一开始就尝试做复杂的游戏,应该先从小型项目或原型开始,逐步积累经验。
    • 制作简单的小游戏或工具,帮助理解基本的游戏机制和设计流程。
  4. 学习并借鉴成功的游戏

    • 研究和分析市场上已有的成功游戏,学习它们的设计理念、玩法和用户交互方式。
    • 了解不同类型的游戏及其玩家群体,掌握各种游戏类型的设计要素。
  5. 多与其他设计师交流

    • 加入游戏开发社区,参与讨论、分享和学习他人的经验。与其他开发者、设计师进行 合作反馈,有助于提升自己的设计水平。

不做的事情(Don’ts):

  1. 避免忽视游戏平衡性

    • 游戏的 平衡性 是非常重要的。设计时不要过于偏向某种玩法或角色,而忽略其他部分的体验。
    • 避免让某一玩法过强,或者让游戏机制过于复杂,让玩家感到困惑。
  2. 不要陷入完美主义

    • 游戏设计过程中,过分追求完美可能会导致项目拖延,甚至无法完成。快速迭代快速验证设计思路才是关键。
    • 要学会在有限时间内做出 妥协,然后通过反馈继续优化。
  3. 避免忽视可玩性和互动性

    • 游戏不只是视觉和声音效果好看,互动性和趣味性才是留住玩家的关键。
    • 不要仅仅依赖于华丽的图像或复杂的机制,最终还是要确保玩家有趣、刺激的体验。
  4. 避免忽略优化和性能

    • 在设计游戏时要始终考虑 性能优化,确保游戏能够流畅运行,尤其是当目标设备是性能有限的设备时。
  5. 不要过度设计

    • 游戏设计应该有节制,不要过度添加复杂的功能或玩法,保持简洁,让玩家能够快速上手,避免冗余设计。

简单而有效的建议:

  • 保持好奇心和学习的态度:游戏设计领域不断变化,新技术和新理念不断涌现,要始终保持学习的心态。
  • 了解玩家:无论是从玩家的角度设计,还是通过测试收集反馈,都要关注玩家的需求和反应。
  • 制作原型:通过制作原型和简单的游戏测试,可以帮助发现潜在的问题并及时调整。
  • 团队合作:游戏设计是一个多学科的合作过程,学会和程序员、艺术家等其他成员有效沟通并协作。

总的来说,游戏设计是一项充满创造力和挑战的工作,通过不断学习、实践和反思,可以不断提升自己。

未来有计划支持其他平台吗?

关于这个项目,未来计划支持多个平台。虽然目前关注的是编程部分,但确实有意在未来扩展到不同的平台,这意味着这个项目将会在多个平台上得到支持和运行。

你有没有为 PS1 编写过任何代码?(我记得 Jeff 在 J&C 的某一集里提到过)

关于PS One的相关内容,并没有涉及。

你玩过 Space Quest 系列游戏吗?网站上的音乐让我想起了它的配乐

提到了对太空探索类游戏的了解,特别是提到了与《Space Quest》相关的音乐。对这款游戏的音乐记忆不深,计划去重新听一下以唤起记忆。

你打算对文本渲染中的字符间距做一些调整吗?

讨论了是否有计划在文本渲染中处理字符间距的问题。提到在某个项目中已经有相关实现,并询问对方具体指的是哪种类型的字符间距处理。

“sentinel” 是一种设计模式,可以帮助拆分的 UI 实例独立运作,还是这是你在工具开发过程中积累的术语?现在我理解了你所谓的“拆分”界面,感觉确实对独立管理层级很有用

讨论了设计模式的概念,特别是如何利用双向链表来处理接口的拆分。提到当创建双向链表时,常常使用链表中的一个元素作为头尾指针,从而帮助管理不同的层次结构或数据。还提到曾经有过一集讨论如何实现和理解这种链表结构。

这不是什么大问题,但现在标签不再显示点击后会展开还是折叠。此外,目前也不太清楚哪些是类别,哪些是变量

讨论了关于标签的问题,提到目前点击标签后它们会展开或最小化,但从外观上看,标签没有显示出来。这种情况还没有在界面渲染上做任何处理,意味着这些只是初步的概念,还没有实现完整的渲染功能。

哪款游戏主机的 SDK 体验最糟糕?

讨论了不同游戏控制台的SDK(软件开发工具包),并指出任天堂的SDK通常是最差的。根据记忆,任天堂每次发布的SDK都是最差的,并且至今没有被超越,尽管也有可能记错。这只是个人的回忆。


http://www.mrgr.cn/news/96693.html

相关文章:

  • 大模型高质量rag构建:A Cheat Sheet and Some Recipes For Building Advanced RAG
  • 配置防火墙和SELinux(1)
  • 【Yolov8部署】 VS2019 + opencv + onnxruntime 环境下部署目标检测模型
  • mysql 八股
  • C语言常用的字符串函数
  • 06-02-自考数据结构(20331)- 查找技术-动态查找知识点
  • 蓝桥杯 刷题对应的题解
  • Java基础 3.31
  • 【Feign】⭐️使用 openFeign 时传递 MultipartFile 类型的参数参考
  • SpringBoot详细教程(持续更新中...)
  • HCIP(RSTP+MSTP)
  • 记忆学习用内容
  • Sentinel[超详细讲解]-4
  • Axure疑难杂症:完美解决文本框读取、赋值、计数(玩转文本框)
  • 安卓一些接口使用
  • python文件的基本操作和文件读写
  • 实现在Unity3D中仿真汽车,而且还能使用ros2控制
  • Docker部署sprintboot后端项目
  • 【Golang】泛型与类型约束
  • 【第十三届“泰迪杯”数据挖掘挑战赛】【2025泰迪杯】【思路篇】A题解题全流程(持续更新)