游戏引擎学习第192天

news2025/4/5 9:19:33

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

回顾

我们现在正在编写一些界面代码,主要是用户界面(UI),不过这里的UI并不是游戏内的用户界面,而是为开发者设计的用户界面。我们正在尝试做一些小的UI元素,这些元素可以在游戏的开发模式下使用。通过这些UI元素,我们可以在运行游戏时进行一些操作,比如修改值,切换开关,或者选择在性能分析器中查看哪些内容。

今天我们要做的工作是基于昨天已经实现的简单径向控制。虽然它已经能正常工作,但在外观上还不是很理想。所以今天的任务是对它进行进一步的优化和美化,提升它的视觉效果。

今天的计划

今天的计划是继续扩展之前的UI功能。除了径向菜单的选择方式,我们还打算加入一些列表式的选择,尝试不同的UI元素。我会按照自己的节奏逐步做这些UI的开发,具体步骤取决于在编程过程中遇到的需求和感觉。在不同的部分看到一些需要改进的地方时,我会灵活调整。可能会有一些改动,比如将径向控制的代码从硬编码中提取出来,让它更加简洁和可用;也可能会继续硬编码更多的UI元素,等它们能够一起运行后再考虑重构成更加合理的架构。

对于架构化的改进,我会等到觉得有足够的内容积累后,再进行相关的拆分和重构。目前的代码和设计仍处于实验阶段,因此很多部分会先硬编码,等到整个系统运作起来之后,再考虑如何进行模块化和优化。

在这里插入图片描述

当前径向菜单的功能和局限性

昨天停止时,已经实现了一个基本的径向菜单功能。虽然基本功能已经完成,但界面还是有些问题,比如菜单项之间可能会重叠,而且当前的实现没有办法确保不选择任何项。现在菜单会根据鼠标指针的位置选择离它最近的项,这样就不太适合复杂的交互需求。另外,菜单项的位置也没有经过精心设计,显得有些杂乱。

不过尽管存在这些问题,功能已经能正常工作了。例如,如果想切换配置图表,只需简单地做个右滑就能快速选择。这个设计的优点就是高效,能通过肌肉记忆轻松选择选项,因此径向菜单在这种快速选择中非常有用。

目前的菜单实现虽然简单,但也足够实用。如果再花20-30分钟优化一下界面和行为,应该能够让它看起来更好、操作更流畅。整个实现过程的开发时间大约是一个半小时左右,尽管从零开始做,依然可以很快搭建一个基本的功能。

接下来,会继续思考如何改进这个功能,逐步优化和扩展它,使得交互更加直观和美观。

我们将围绕已经拥有的调试功能来构建UI

在考虑接下来的UI需求时,我不太喜欢提前预设未来需要的功能,而是倾向于根据实际需求逐步展开。因此,我想采用一种智能的方式来处理这个问题,那就是回顾我们之前写过的调试代码。

实际上,我们在开发过程中已经实现了很多调试功能,比如清空某些数据、添加调试覆盖层等。这些功能帮助我们检查和验证引擎的状态,并确保一切按预期工作。比如,屏幕上有一个黄色的边框,它的作用是帮助我们确认屏幕上的元素是否已正确对齐,这只是我们添加的其中一个调试功能。

另外,我们还实现了可以让相机缩放的功能,也就是将一些调试信息直接嵌入到游戏中。通过这些功能,我们已经积累了很多的调试工具,帮助我们确保引擎各项内容达到了预定的要求。

接下来,我要思考的是如何让这些调试功能更加基础化和易于访问。我的理想情境是,只需点击右键,就能快速访问所有调试选项。不过,问题是,所有这些调试功能的数量可能会非常多,无法全部显示在屏幕上。即使是通过径向菜单呈现,也无法将所有选项有效地展示出来。所以,即使我们将一些选项分布在屏幕各个地方,也可能需要更多的思考和设计,以便让这些选项既能方便访问,又不至于显得过于杂乱。

找到一个简单的方式通过调试界面来开启和关闭调试功能

在继续实现更多UI元素之前,可能需要先思考如何让调试功能能够方便地开启和关闭。我之前就有考虑过这个问题,觉得可以将调试系统的功能整合起来,提供一个开关来控制调试功能的启用与禁用。这样做可以让我们更加灵活地管理这些功能,并且便于在开发过程中快速调整。

现在,我可能会改变思路,决定先实现这个功能。具体来说,就是将这些调试功能从现有的UI中抽离出来,纳入到调试系统的整体框架中,这样我们就能更清晰地看到调试功能的运作方式,并且确保它们能够在需要时方便地开启或关闭。这也是目前我的首选方案。

简单回顾调试相机

我们可以从一个最简单的例子开始,比如游戏中的渲染组。在这个例子中,曾经有过一个调试相机的概念。具体来说,调试相机允许我们以某种方式将相机拉远。这种方式并没有改变相机的使用方式,所有的内容仍然完全一致,唯一的不同之处在于,如果改变了某个设置,比如把它改成1,相机就会把所有内容拉远。

这只是一个例子,意味着在调试模式下,可以通过调整某些设置来查看不同的游戏视角,虽然没有根本性改变相机的工作方式,但它能够提供更多的调试视角和功能。
在这里插入图片描述

重新加载系统和调试事件数组汇总之间存在冲突

这段内容主要在讨论一个关于代码重载(reload)可能带来的问题。具体来说,担心在进行重载操作时,代码可能会发生一些问题,尤其是在处理事件时。问题的焦点在于当重载发生时,某些变化可能会导致无法正常使用某些变量或事件,可能会遇到无法正常调用的情况。

他提到的问题可能是与事件相关的一个错误,虽然可能是一个普通的bug,但需要确认在重载时,是否需要刷新(flush)某些内容,以保证代码能够正常执行。总的来说,讨论的重点在于如何处理代码重载时可能出现的意外问题,并确保重载后的状态能够正常工作。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

也许我们应该在重新加载后刷新所有事件

讨论了在重载代码时需要重置调试系统的问题。具体来说,每次重载代码时,所有的标记点和计数器可能会发生变化,因此可能需要刷新所有的调试事件来确保系统恢复正常。这是一个逻辑上合理的步骤,因为在重载之后,调试事件可能会受到影响。

然而,他也担心这样做可能会掩盖其他潜在的问题,可能会有某种不同的bug存在,因此希望先调查一下,确认是否存在其他问题。通过观察,发现第一次打开的块是垃圾数据,完全是重载后产生的无效数据。这表明,由于使用了增量帧处理机制,在代码重载后,指向调试内存的指针会被清除,因为这部分内存是静态变量。

此外,还提到了计数器的设计问题,计数器需要引用这个静态变量,而这种引用方式比较麻烦,因此在设计上存在一些问题。总体来说,讨论的重点是如何在重载时处理这些数据问题,并确保系统能够正常工作,而不是掩盖可能存在的其他bug。
在这里插入图片描述

刷新事件数组缓冲区

如何在代码重载后清空调试事件缓冲区。为了确保系统在重载后能够正常运行,可以选择清空整个事件数组和相关的计数器。这是一个简单且有效的步骤,因为每次可执行文件重载时,都可以轻松地检测到并执行该操作。

具体来说,可以在调试帧结束时执行重置操作,将所有事件计数器清零。这涉及到清除调试系统中的所有事件计数,确保它们返回到初始状态。通过查阅game_platform.h文件,查看事件数组和事件计数的结构,可以理解这一过程。只需将这些计数器和数组中的所有项重置为零,便可以实现重置。

这样做的目的是确保在每次代码重载时,调试系统能够恢复到干净的状态,从而避免由于旧的调试数据干扰导致的问题。
在这里插入图片描述

DLL重新加载已经清除了缓冲区。我们只需要重新启动汇总过程

如何处理调试系统在执行代码重载后的重置问题。首先,提到由于事件计数器和数组已经在DLL中,因此它们应该会自动被重置为零,因此不需要特别操作这些数据。实际上,唯一需要重置的是增量帧处理的状态。

因此,实际的需求是在代码重载后重启调试事件的整理(collation)过程。这意味着一旦调试系统被重置,就需要重新开始整理事件,而不是重新设置所有的计数器。

为了实现这一点,需要在DEBUGGameFrameEnd中处理调试系统的重置状态。然而,目前在DEBUGGameFrameEnd中只传递了游戏内存,并没有传递是否执行了可执行文件的重载。因此,下一步需要确保输入参数中包含可执行文件重载的状态,并据此决定是否进行重置操作。

总结来说,主要的操作是检测到代码重载后,仅重启事件整理过程,而不必重置其他数据。
在这里插入图片描述

在这里插入图片描述

将ExecutableReloaded标志从游戏输入移动到游戏内存

讨论了如何将可执行文件重载的状态移动到内存中进行处理。原本在其他地方处理这个问题,但认为将其放入内存中可能更为合理,因为通常处理此问题的人会在内存中进行操作。于是决定在这里检查 ExecutableReloaded 是否为 true,如果是,则重新启动事件整理(collation)。

接下来,需要确保在代码中正确设置内存中的游戏内存状态,以便根据可执行文件是否重载来重启整理过程。通过这种方式,确保系统能够在代码重载后正确处理调试事件。

最后,展示了一些游戏画面,表示这项工作已经完成。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试

进行了一次测试以验证修改的效果。游戏中的角色可以正常运行,看起来一切都很顺利。接着,测试了渲染功能,尝试缩放视图并清除整理状态。希望通过这些步骤,可以确保在合适的时机触发调试事件的重置,并且验证这个过程现在变得更加可靠。

当前在常规和调试相机之间切换的方法不太方便

我们希望在代码中能够方便地控制调试功能,而不是每次都去查找和修改代码。我们不希望对现有的代码做大幅度的改动,只是希望在调试时能有更便捷的操作方式。例如,可以通过设置一个类似debug UI的条件判断来实现,如果条件满足,就启用调试功能。具体来说,我们可能会使用诸如debug camera这样的命名方式,通过这种方式,在调试过程中,可以更轻松地切换和控制调试界面的状态,而不需要每次都修改代码。这样做的目的是让调试功能更加灵活和易于操作,同时避免重复查找和修改代码,提高开发效率。

我们更希望自动化更改代码和重新编译的过程

我们希望实现的是,在访问调试界面时,能够动态地编译和切换代码功能。这意味着,当我们修改某个值时,系统应该能够重新编译代码,而不是保留已经编译的版本。这样就可以在调试过程中使用可能非常昂贵的操作,而不需要担心它们对性能的长期影响,因为重新编译后,操作会被动态更新,确保调试功能的实时性。

这听起来可能有点复杂,但并不是不可实现。虽然要实现这个功能确实有一定的挑战,但我们可以从简单的方式开始,逐步实现这个目标。比如,我们可以先从类似game_debug_switchesgame_config.h这样的简单配置文件入手,通过这些配置文件来控制调试功能的开关。这样,我们可以逐步验证这一思路的可行性,并优化实施的细节。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们将把所有的开关收集到全局可访问的game_config.h文件中

我们在尝试实现一个新的功能,通过修改配置文件来控制某些调试选项的启用与关闭。假设我们在配置文件中设置了一个变量,比如让它等于0,这样在编译时,程序就知道该变量不会被使用。但是,由于编译时没有看到这个配置,系统并不会直接认识到它,所以我们需要确保这个配置在代码的早期阶段被引用。为了让每个平台都能看到这个配置,最好的做法是将它放在最基础的地方,比如game config文件中,这样每个层次的代码都能访问到它。

game config文件中,我们可以设置调试相关的变量,比如启用调试相机。这样,我们就能在配置文件中随时开启或关闭这个调试功能,而且修改后不会有问题,系统能够识别这些变化。

然而,接下来的问题是,如何将这些配置项展示到UI界面上?比如,如果我们希望将调试开关放到径向菜单(radial menu)上,如何实现这个功能?这就变成了一个更复杂的问题,需要一些技巧和细节处理才能完成。

在决定是否将某个功能加入到UI时,虽然知道这可能会有些复杂和不寻常,但有时候为了游戏开发的目标,还是需要大胆尝试。于是,决定将调试功能整合进UI界面中。

要实现这一目标,首先,UI必须能够识别并访问相关的配置数据。为了实现这一点,UI需要了解如何获取和使用这些数据,确保在UI中能够正确地反映配置的状态。

为配置开关分配类型

在考虑将调试功能加入到UI时,还需要注意UI必须知道这个功能是一个布尔值,即它的值只能是0或1。这意味着UI需要处理一个b32类型的变量,确保它能够正确地反映这个值的状态。这样,UI就可以根据这个布尔值来显示不同的状态,而不是用其他的定义值。这样的处理方式是合理的,因为它简化了状态管理,并确保UI可以正确地显示调试功能的开关状态。

从游戏代码内部重写配置文件

如果希望在UI中展示一些功能选项,比如“使用调试相机”,要实现这一点的唯一方法是让游戏能够在运行时编辑配置文件。也就是说,UI需要有能力在运行时修改这个文件,并触发重新编译的过程。因此,为了让UI功能正常工作,游戏必须能够在运行时编辑这个配置文件,并重新编译它。

重写这个文件本身并不算特别复杂,只要文件的格式比较简单,操作起来应该没什么问题。通过这种方式,UI就能够动态地更新配置文件,从而反映调试功能的开关状态,并且能在运行时实时生效。

实现WritegameConfig

为了让调试功能能够在运行时动态更新配置文件,可以编写一个函数来实现这一过程。假设我们创建一个名为WriteGameConfig的函数,这个函数接受调试状态作为输入,然后利用平台层的功能将整个配置文件写入磁盘。

具体来说,我们可以在平台层中定义一个函数debug_write_entire_file,这个函数会接受文件名和要写入的数据大小等信息。通过这个函数,游戏能够将修改后的调试配置文件写入磁盘。虽然具体的文件名和数据大小目前还不确定,但这并不算特别复杂,最终目标是让这个过程自动化,让UI能够直接控制调试文件的重写。
在这里插入图片描述

我们再次 resort 使用_snprintf_s 来最小化我们稍后需要替换的CRT函数数量

为了实现自动更新调试配置文件的功能,首先需要将调试状态和调试区域(debug arena)关联起来,并为其提供足够的空间来写入文件。可以从一个简单的实现开始,比如定义一个临时变量,并利用已有的函数来帮助输出调试信息。

接下来,核心操作是将调试配置项(如“使用调试相机”)的值打印到一个缓冲区中。打印的内容包括变量名和它的布尔值(0或1),同时需要打印预处理指令(例如#define)。通过这种方式,程序能够重新生成配置文件的内容,并将其写入一个临时缓冲区。函数返回的输出大小则告诉程序写入的内容有多大。

接下来,使用这个缓冲区的内容写入到指定的配置文件中。为了确保写入正确,程序需要知道配置文件的准确位置,通常可以根据相对路径找到该文件,比如从构建目录回溯到代码目录,再更新配置文件。

目前,可以先手动设定调试状态(比如直接用字符串设置use debug camera),然后根据这个状态打印出0或1,最终自动更新配置文件。为了简化测试过程,可以暂时将文件名硬编码,避免涉及其他复杂问题。

在实现时,还需要确保代码在不同的机器上也能正确工作。通过使用相对路径,确保程序能够定位到正确的目录,并成功更新配置文件。

在这里插入图片描述

#define GAME_CONFIG_FILE_NAME "../../../../game/game_config.h"
internal void WriteGameConfig(debug_state *DebugState, bool32 UseDebugCamera) {  //
    char Temp[4096];
    _snprintf_s(Temp, sizeof(Temp), "#define DEBUGUI_UseDebugCamera %d //bool32\n", UseDebugCamera);
}
    DEBUGWriteEntireFile(GAME_CONFIG_FILE_NAME, TempSize, Temp);

每次调试UI操作后重写配置文件

通过每次执行某个操作时自动重写配置文件,可以看到当我们修改某些设置后,配置文件会根据设置的调试状态(如将值硬编码为true)自动更新。这样,每次执行一个操作或手势时,程序都会将配置文件的状态从0变为1,从而反映调试功能的开关状态。

这个过程的实现是通过让程序自己修改配置文件,从而避免了需要手动修改文件或编写复杂的编译器。程序能够在运行时自动修改并保存自己的状态,这样无需进行额外的复杂操作。

目前,程序已经能够成功地将配置文件从0修改为1,证明了这一机制的有效性。接下来的问题就是,如何进一步完善这个过程,使得它在其他情况下也能正常工作,确保修改的配置文件在每次执行时能够准确反映当前的调试状态。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们希望游戏能够重新编译自己

为了实现程序在修改配置文件后能够自动重新编译,需要确保程序在执行时能够触发重新编译的过程。首先,将调试相机的设置恢复为0,表示不使用调试相机。接下来,目标是让程序能够在修改了配置文件之后自动触发编译操作。

为了实现这一点,程序需要在运行时检测到配置文件的变化,并且能够调用编译过程,重新编译代码并应用新的设置。这可以通过一些自动化脚本或构建工具来完成,确保每次修改配置后,系统能够自动重新编译并加载新的配置。

平台层将允许我们通过DEBUGExecuteSystemCommand来执行cmake

为了实现程序在修改配置文件后能够自动重新编译,需要让平台层执行系统命令。目标是让程序能够在执行某些操作时,自动运行cmake文件,这通常是在游戏代码中执行的构建命令。

在Windows上,执行的是命令行解释器cmd.exe,因此需要通过cmd.exe来执行cmake --build ../../x64-debug

为了实现这一点,平台层需要提供一个方法来执行系统命令。在Win32平台中,确实有办法实现这一点,可以通过扩展平台层,在平台层中增加一个DEBUGExecuteSystemCommand函数。这个函数接受命令路径、命令本身以及命令行参数,并返回执行结果,通常是一个错误码或状态码。

当我们实现了这个DEBUGExecuteSystemCommand函数后,程序就能够在需要时执行构建命令,自动重新编译程序。这需要在平台API中添加相关的实现,确保能够正确执行系统命令并返回相应的结果。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们正在寻找的操作系统函数是CreateProcess

在Windows的Win32环境中,执行系统命令最适合使用CreateProcess函数。虽然ShellExecute可以用来打开文件或执行类似操作,但它并不适用于执行命令行命令,因此CreateProcess是更好的选择。

以下是详细的步骤和思路:

  1. CreateProcess 函数概述
    CreateProcess是一个Windows API函数,用于创建一个新的进程,运行指定的命令或程序。它返回一个布尔值,指示进程是否成功启动。通过该函数,可以详细控制进程的启动和管理。

  2. 参数说明

    • Application Name:执行的程序路径(例如,cmd.exe )。
    • Command Line:传递给程序的参数(例如,cmake --build ../../x64-debug)。
    • Process AttributesThread Attributes:通常设置为NULL,除非需要更复杂的进程或线程管理。
    • Creation Flags:指定进程的创建方式。一般情况下,使用CREATE_NEW_CONSOLE等标志,或者设置为0表示使用默认行为。
    • Startup Info:用于指定如何启动新进程的结构体。需要特别注意设置其中的cb字段为结构体大小。
    • Process Information:接收新进程的信息,如进程ID和进程句柄等。
  3. 代码实现

    • CreateProcess函数需要一个填充完整的STARTUPINFO结构体和PROCESS_INFORMATION结构体。通常,如果没有特殊需求,STARTUPINFO的许多字段可以通过初始化为0来简化设置。
    • 在调用CreateProcess时,必须确保传递正确的路径和命令行参数。
  4. 执行步骤

    • 在函数中,初始化STARTUPINFOPROCESS_INFORMATION结构体,并为其设置必要的属性(如结构体大小cb)。
    • 使用CreateProcess启动进程,执行指定的命令。
    • 如果需要,可以使用WaitForSingleObject等待进程完成,或者立即返回。
  5. 示例代码

    STARTUPINFO StartupInfo = {};
     StartupInfo.cb = sizeof(StartupInfo);
     PROCESS_INFORMATION ProcessInfo;
     if (CreateProcess(
         Command,// 应用程序名称
         CommandLine,// 命令行
         0,// 进程安全属性
         0,// 线程安全属性
         FALSE,// 不继承句柄
         0,// 创建标志
         0,// 环境变量
         Path,// 当前目录
         &StartupInfo,// 启动信息 
         &ProcessInfo// 进程信息
         )) {
     }
     int32 Result = 0;
     return Result;
    

    这个代码片段使用CreateProcess来执行一个命令(在本例中是cmake --build ../../x64-debug),并等待其执行完成。执行成功后,它会返回true,否则返回false并输出错误信息。

总结:

  1. CreateProcess 是执行系统命令时最合适的函数。
  2. ShellExecute 更适用于打开文件,而不是执行命令行指令。
  3. 在使用CreateProcess时,必须正确设置STARTUPINFOPROCESS_INFORMATION结构体。
  4. 使用WaitForSingleObject等待进程结束,确保执行完成。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试,它不起作用

在执行操作时,虽然代码进行了重写,但似乎并没有触发实际的编译过程。尽管配置文件可能已经被成功重写(例如修改了某些设置),但似乎没有任何编译活动发生,或者没有看到预期的编译输出。这可能意味着尽管配置文件的内容已经改变,但可能没有正确地触发编译命令的执行,或者命令没有按照预期正常运行。需要进一步确认是否执行了编译命令,或是检查编译命令的执行过程,确保它能在代码变更后正确地重新编译项目。

CreateProcess返回false

在执行过程中,配置文件已成功重写,但没有触发编译过程。通过调试 execute system command,发现执行返回了 false,说明命令未能成功执行。这表明调用系统命令时存在某些错误,可能是参数或设置未正确指定。需要检查命令的执行参数和调用过程,确保所有必需的设置都已正确配置。进一步调试和修改代码,以确保能够正确触发编译操作。
在这里插入图片描述

使用GetLastError查看原因

为了获取更多的错误信息,Windows 提供了 GetLastError 函数。通过调用这个函数,可以返回一个 DWORD 值,表示具体的错误代码。使用这个错误代码,可以进一步查找错误的原因。在调试时,如果设置断点并运行程序,可以查看 GetLastError 返回的错误代码。通过工具查找错误代码 2,得到了具体的错误信息。

    LPVOID msgBuffer;
        DWORD ErrorCode = GetLastError();
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
                          FORMAT_MESSAGE_IGNORE_INSERTS,
                      NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                      (LPTSTR)&msgBuffer, 0, NULL);
        std::wcout << L"Error Code " << ErrorCode << L": " << (LPCTSTR)msgBuffer << std::endl;
        LocalFree(msgBuffer);

在这里插入图片描述

在这里插入图片描述

提供cmd.exe的显式路径

在执行过程中,出现了“无法找到指定文件”的错误。这通常表示文件路径没有正确设置,或者文件不在预期的位置。为了排查并解决这个问题,采取了以下几种方法:

  1. 路径问题:首先假设文件可能不在预期的路径下。为了确保可以找到文件,可以尝试明确指定文件的路径,尤其是在 Windows 系统中,路径分隔符应该使用反斜杠(\)。因此,检查路径是否包含正确的反斜杠是一个关键步骤。

  2. 调整路径:通过显式指定完整的文件路径来解决路径问题,确保路径能够正确指向文件的位置。这样就避免了依赖环境变量或者默认路径的问题。

  3. 测试路径:在修改路径后,重新尝试运行程序,确认文件是否能够被找到并成功执行。

最终,调整路径后,程序能够正常工作,文件成功执行,这表明路径问题已经解决。
在这里插入图片描述

在这里插入图片描述

/k cmake --build ../../x64-debug 

/k 先不然自动退出cmd 调试完之后改为/c 自动退出cmd

  • cmd /c dir:执行 dir 命令并且退出命令提示符窗口。
  • cmd /k dir:执行 dir 命令并且保持命令提示符窗口打开,等待用户进一步操作。

在这里插入图片描述

    char Temp[4096];
    char CommandLine[] = "/k cmake --build  .\\..\\out\\build\\x64-debug";
    int TempSize = _snprintf_s(Temp, sizeof(Temp), "#define DEBUGUI_UseDebugCamera %d //bool32\n",
                               UseDebugCamera);
    Platform.DEBUGPlatformWriteEntireFile(GAME_CONFIG_FILE_NAME, TempSize, Temp);
    Platform.DEBUGPlatformExecuteSystemCommand("../../../../game", "C:/Windows/System32/cmd.exe",

注释掉#include "game_config.h"可以
在这里插入图片描述

需要close 文件的句柄不然一直会被占用

创建close 函数

在这里插入图片描述

在这里插入图片描述

返回写的文件的句柄

在这里插入图片描述

close 文件句柄

在这里插入图片描述

成功

在这里插入图片描述

在这里插入图片描述

现在它可以工作了

在执行构建命令后,系统似乎能够正常运行构建流程。从反馈来看,当前的执行方式是可行的。为了确保构建过程能够稳定运行,进行了以下步骤:

  1. 执行构建命令
    通过 CreateProcess 启动了构建进程,传递了正确的命令行参数,并设置了必要的进程启动信息。

  2. 确认命令正确性
    运行过程中,检查了命令执行的反馈,确保命令能够正确解析并被 cmd.exe 识别。还测试了路径格式,包括是否需要反斜杠进行转义,确保路径解析无误。

  3. 路径调整
    发现 cmd.exe 可能找不到指定的文件,推测是由于路径未正确解析。随后尝试提供更明确的路径,如绝对路径或更标准的路径格式,最终成功执行了构建命令。

  4. 确认进程状态
    通过 CreateProcess 获取进程信息,并监测返回值。如果进程未正确启动,则使用 GetLastError 获取错误信息,分析错误代码。

  5. 文件访问问题
    在执行构建命令前,会先写入配置文件 game_config.h,但发现后续构建命令执行时可能找不到该文件。经过分析,推测是因为文件仍被当前进程占用。尝试了以下方法来解决:

    • 在写入文件后调用 FlushFileBuffers 确保数据写入磁盘。
    • 及时关闭文件句柄 CloseHandle,确保其他进程可以访问该文件。
    • 在文件写入和构建命令执行之间增加适当的延迟,以确保文件系统已完全刷新。
  6. 最终执行结果
    经过调整后,构建命令成功运行,编译流程正常进行,表明当前方法已能够正确执行构建任务。

总的来说,解决过程中主要涉及:

  • 路径问题:确保命令执行路径正确,避免路径解析错误。
  • 进程管理:正确使用 CreateProcess 启动进程,并检查进程状态。
  • 文件访问:确保写入的配置文件不会被进程占用,影响后续访问。
  • 错误排查:使用 GetLastError 分析错误代码,确定失败原因。

通过上述方法,最终成功执行了构建命令,并且系统能够正常运行构建流程。

在配置文件中切换DEBUGUI_UseDebugCamera的值

现在代码已经可以正确编译,接下来要进一步完善功能,使其可以正常切换。具体步骤如下:

  1. 实现功能逻辑

    • game_debug.cpp 中添加逻辑,使其能够根据当前的配置文件状态进行切换。
    • 设计一个方法,使其能够读取配置文件的值,并在写回时取反,从而实现切换功能。
  2. 优化代码结构

    • 由于不需要额外的变量来存储状态,因此直接在写入配置文件时处理切换逻辑。
    • 每次执行切换时,读取当前 config 文件的值,并在写入时存储相反的值,实现动态切换。
  3. 确保编译流程正常

    • 进行编译并确认切换逻辑生效。
    • 观察输出结果,确保每次切换时,系统都能正确更新配置。
  4. 最终结果

    • 代码能够正确执行切换,每次执行时都会根据当前配置状态进行反转。
    • 通过编译,可以确认切换逻辑已生效,系统能够正常在不同模式间切换。

总结来说,此次实现的核心是在写入配置文件时动态调整值,确保能够正确进行状态切换,并且通过编译和执行测试,确保功能正常运行。
在这里插入图片描述

在这里插入图片描述

引入超级调试UI!

现在基本上已经实现了一个超级调试 UI,因为当前的调试功能完全没有任何运行时开销

  1. 编译时移除调试代码

    • 现在的实现方式可以让调试代码在编译时被完全移除,而不影响运行时的性能。
    • 这意味着即使游戏在运行过程中,我们仍然可以切换调试功能,而不需要在代码中额外增加运行时判断逻辑。
  2. 无需额外操作即可实时切换

    • 通过修改 config 文件,并重新触发 build,就可以在游戏运行过程中切换调试功能。
    • 这样做的好处是,无需手动调整变量,只需修改配置文件并重新编译,调试代码就会自动生效或被移除。
  3. 兼顾性能与灵活性

    • 该方法既保证了高性能(因为调试代码不会影响最终的编译结果),又提供了灵活性(可以随时切换调试状态)。
    • 这样既避免了传统的 #ifdef DEBUG 可能导致的复杂代码管理问题,同时又确保了调试工具的易用性。
  4. 最终效果

    • 通过编译系统与 config 配置的结合,实现了完美的调试控制
      • 调试代码不会影响最终运行时的性能(因其可以完全被编译移除)。
      • 可以随时切换调试功能,无需手动调整代码或重启游戏。

总结来说,这种方法实现了一种理想的调试系统,让调试代码的管理更加高效,同时确保了游戏运行时的最佳性能

去掉编译窗口弹出

目前存在一个弹出窗口的问题,每次执行 CMD 命令时,都会弹出一个窗口,这种行为并不理想,因此需要优化。

  1. 目标

    • 希望在执行命令时不弹出窗口,而是在后台完成进程管理。
    • 这样可以让调试和编译过程更顺畅,避免干扰主程序运行。
  2. 问题分析

    • 目前调用 CMD 时,会默认打开一个终端窗口,这是因为 CreateProcess 没有特别指定窗口行为。
    • CMD 的一些参数(如 /C/K)虽然可以控制命令的执行方式,但无法避免窗口弹出。
  3. 可能的解决方案

    • 调整 STARTUPINFO 结构体
      • Windows API 提供了一些进程启动选项,可以用 STARTUPINFO 控制窗口行为。
      • 需要设置 STARTF_USESHOWWINDOW 并将 wShowWindow 设为 SW_HIDE,这样进程启动时窗口会被隐藏。
    • 使用 CREATE_NO_WINDOW 选项
      • CreateProcess 允许传入 dwCreationFlags 参数,可以使用 CREATE_NO_WINDOW 标志,让进程在无窗口模式下运行。
    • 使用 ShellExecuteEx 进行更高级的控制
      • ShellExecuteEx 提供了更多选项,可以直接指定 SW_HIDE,确保不会弹出窗口。
  4. 最终效果

    • 运行 CMD 命令时,不会再弹出终端窗口
    • 调试和编译过程更加流畅,不会影响主程序的 UI 体验。
    • 代码更干净,并且可以在后台管理进程的执行状态。

通过这些优化,可以更好地控制 CMD 进程的行为,使其在后台静默运行,从而提升整体用户体验。
在这里插入图片描述

使用wShowWindow标志隐藏窗口

当前的目标是在执行 CMD 命令时隐藏窗口,以避免弹出终端窗口影响用户体验。

  1. 问题分析

    • 直接设置 wShowWindow = SW_HIDE 并不能生效,因为 STARTUPINFO 结构体需要额外设置 dwFlags 以启用该功能。
    • 仅仅修改 wShowWindow 的值,CMD 进程仍然会弹出窗口,需要正确配置 dwFlags 才能生效。
  2. 解决方案

    • 需要在 STARTUPINFO 结构体中同时启用 dwFlags 并设置 wShowWindow
    • dwFlags 需要包含 STARTF_USESHOWWINDOW,否则 wShowWindow 不会生效。
    • 通过 CreateProcess 创建进程时,可以确保窗口不会弹出。
  3. 关键点解析

    • dwFlags = STARTF_USESHOWWINDOW 启用窗口控制,否则 wShowWindow 设为 SW_HIDE 也不会生效。
    • wShowWindow = SW_HIDE 确保窗口不会弹出,CMD 进程将在后台执行。
    • CREATE_NO_WINDOW 也有助于防止创建窗口,确保进程运行时不会干扰 UI。
  4. 最终效果

    • 运行 CMD 命令时,不会弹出终端窗口,执行过程在后台完成。
    • 解决了窗口闪烁和不必要的 UI 干扰,提高了用户体验。
    • 代码更加简洁,并且可以灵活控制进程的执行方式

这样,CMD 命令就可以无窗口模式运行,避免影响主程序界面,优化整体的执行体验。

它有效!

整个流程已经实现了无窗口运行 CMD 命令的目标,效果良好,界面干净整洁,不再有弹窗干扰,整体执行体验得到了优化。

之前的问题主要是由于 STARTUPINFO 结构体的 wShowWindow 设置没有生效,需要额外设置 dwFlags = STARTF_USESHOWWINDOW 才能正确隐藏窗口。现在,通过 CreateProcess 创建进程,并结合 CREATE_NO_WINDOW 选项,CMD 进程可以在后台静默执行,不影响主程序界面。

最终实现的方案兼顾了功能性和用户体验,命令执行正常,窗口不会弹出,一切按预期工作,达到了最优效果。

获取编译进程状态的信息

当前已经成功实现了无窗口运行 CMD 命令的目标,但由于窗口不可见,无法直接观察编译过程是否正在进行,也无法得知编译是否出错。因此,下一步的优化目标是提供一种方式来检测进程状态,从而掌握编译进度和可能的错误信息。

当前方案中,CreateProcess 返回的 PROCESS_INFORMATION 结构体包含了新创建进程的句柄 (hProcess),可以利用该句柄检查进程状态。

首先,考虑 Windows API 提供的方法,例如:

  1. WaitForSingleObject

    • 该函数可以用于等待进程完成。
    • 通过传递进程句柄 (hProcess) 并设置超时时间 (0),可以非阻塞地检查进程是否已经终止。
    • 如果返回值是 WAIT_OBJECT_0,说明进程已结束;否则,进程仍在运行。
  2. GetExitCodeProcess

    • 该函数可以获取进程的退出码。
    • 如果进程仍在运行,该函数会返回 STILL_ACTIVE,否则返回具体的退出码,可能包含错误信息。

改进方案:

  • 在创建进程后,使用 WaitForSingleObjectGetExitCodeProcess 轮询进程状态,确保编译完成后再执行后续操作。
  • 如果进程终止,可以进一步检查退出码,判断编译是否成功或失败。

这样一来,既能避免窗口弹出,又能实时监测编译状态,提高调试和错误处理的能力

我们将使用WaitForSingleObject来查询该信息

目前已经成功利用 WaitForSingleObject同步等待编译进程完成,但仍需优化进程状态管理,以提供更好的调试体验和错误处理能力。

改进方案

  1. 增加进程状态管理结构

    • 设计一个结构体 DebugExecutingProcess,用于存储进程执行状态,包括:
      • bool IsRunning:进程是否仍在运行。
      • bool StartedSuccessfully:进程是否成功启动。
      • DWORD ReturnCode:进程的退出码(用于判断是否出错)。
      • HANDLE ProcessHandle:进程的句柄。
  2. 执行系统命令时返回该结构体

    • Platform.DEBUGPlatformExecuteSystemCommand 将返回 DebugExecutingProcess,以便后续查询进程状态。
  3. 更新进程状态

    • WaitForSingleObject 等待后,更新 IsRunning 状态。
    • 使用 GetExitCodeProcess 获取进程退出码,并存储在 ReturnCode 中。

代码逻辑

  1. 创建进程
    • 如果成功启动进程,则 StartedSuccessfully = trueIsRunning = true,并返回 DebugExecutingProcess 结构体。
  2. 检查进程状态
    • 使用 WaitForSingleObject(ProcessHandle, 0) 非阻塞检查进程是否已结束。
    • 如果进程结束,调用 GetExitCodeProcess 获取退出码,并更新 ReturnCode,然后 IsRunning = false
  3. 提供状态查询
    • 在主循环或其他逻辑中,可随时查询 DebugExecutingProcess 结构体,以判断编译是否完成,以及是否有错误发生。

优化后的效果

  • 编译状态可查询:无需强行等待进程结束,主逻辑可以定期轮询查询状态。
  • 更好的错误处理:如果进程失败,可检查 ReturnCode 并输出调试信息。
  • 减少不必要的等待:可以在编译过程中执行其他任务,提高程序响应速度。
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

查询平台层关于编译进程状态的信息

当前我们优化了进程管理,采用了句柄存储进程状态的方式,以便后续查询和管理编译进程的状态。

主要改进点

  1. 引入 debug_executing_process 结构体

    • 仅包含 OSHandle(即进程句柄),用于标识执行中的进程。
    • 如果进程创建失败,OSHandle 设为 INVALID_HANDLE_VALUE
  2. 增加 debug_process_state 结构体

    • bool IsRunning:表示进程是否仍在运行。
    • bool StartedSuccessfully:进程是否成功启动。
    • DWORD ReturnCode:进程退出码(用于检查错误)。
  3. 实现 DebugPlatform_GetProcessState

    • 传入 debug_executing_process,返回 debug_process_state
    • 通过 WaitForSingleObject(OSHandle, 0) 非阻塞检查进程是否结束。
    • 如果进程已结束,则调用 GetExitCodeProcess 获取退出码。
  4. CreateProcess 成功后存储进程句柄

    • OSHandle = ProcessInfo.hProcess,确保后续可以查询进程状态。
  5. 类型安全性优化

    • 由于 HANDLE 可能在不同平台上具有不同大小,因此添加静态断言,确保 OSHandle 能安全存储 HANDLE 值。

优化效果

  • 提高查询效率:可以随时查询编译进程状态,而不影响主线程运行。
  • 减少等待时间:主线程无需强行等待进程结束,可并行执行其他任务。
  • 增强错误检测:如果编译失败,可以通过 ReturnCode 进行额外处理,如输出错误日志或提示用户。
  • 更好的代码结构:分离进程执行状态查询,使代码更清晰、易维护。

在这里插入图片描述

在这里插入图片描述

"我们不在乎它有多大"β

我们优化了进程句柄存储方式,以确保跨平台的兼容性和安全性,避免因句柄大小不匹配导致的错误。

主要改进点

  1. 存储进程句柄时确保大小匹配

    • 直接将 hProcess 赋值给 OSHandle,无论其大小如何,都保证正确存储。
    • 静态断言static_assert 或运行时 assert)确保 OSHandle 至少与 HANDLE 大小相等,以防止数据截断。
  2. 当进程创建失败时,设置 OSHandle = INVALID_HANDLE_VALUE

    • 避免使用未初始化的句柄,确保后续查询逻辑不会访问非法内存。
  3. 进一步封装 debug_executing_process 结构

    • 该结构体仅用于存储 OSHandle,简化进程管理逻辑。
    • DebugPlatform_GetProcessState 通过 OSHandle 查询进程状态,而无需暴露 PROCESS_INFORMATION 结构。
  4. 改进错误处理

    • 如果 CreateProcess 失败,直接返回 INVALID_HANDLE_VALUE,并在 GetProcessState 里检查是否有效,避免访问无效句柄。
    • 日志记录失败原因,便于调试和错误分析。

优化效果

  • 提高代码可移植性:确保 OSHandle 可以安全存储 hProcess,避免因句柄大小不同导致的跨平台兼容性问题。
  • 增强安全性:无效句柄时不会执行错误查询,防止崩溃。
  • 清晰的进程管理结构:执行与查询分离,便于维护和扩展。
    在这里插入图片描述

在这里插入图片描述

实现DEBUGGetProcessState

在处理进程句柄时,主要目的是检查进程的状态并决定是否释放资源。以下是具体的步骤和逻辑:

主要逻辑和步骤:

  1. 验证进程句柄有效性

    • 首先检查 OSHandle 是否为 INVALID_HANDLE_VALUE。如果是,表示进程没有成功启动,结果为 false,此时不需要继续执行后续操作,所有相关状态保留为零。
    • 如果 OSHandle 不是 INVALID_HANDLE_VALUE,则表示进程可能已经启动,此时可以继续检查进程的状态。
  2. 检查进程是否仍在运行

    • 如果 OSHandle 有效,则可以将其传递给 Windows API,查询该进程是否仍在运行。如果进程已经结束,可以关闭进程句柄,因为不再需要操作该句柄。
  3. 释放进程句柄的方式

    • 目前的做法是直接在进程不再运行时关闭句柄,但是否需要一个正式的释放机制仍然是一个待考虑的问题。这可能需要专门的逻辑来清理进程资源,但在调试代码中,当前的做法是可以接受的。
  4. 信号和锁定

    • 通过调用类似 WaitForSingleObject 的方法,检查进程是否被信号触发(即进程是否已完成)。如果信号触发,表示进程已完成,可以继续执行后续逻辑。如果没有信号触发,则可以假设进程仍在运行。

结果:

  • 如果进程已成功启动且仍在运行,进程状态和句柄信息会被更新。
  • 如果进程已结束,可以安全地关闭句柄并清理资源。
  • 通过这种方式,可以有效地管理进程生命周期,避免不必要的资源占用,同时保证系统稳定运行。
    在这里插入图片描述

在这里插入图片描述

使用GetExitCodeProcess确认编译完成

在这个实现中,主要目的是确保编译进程在系统中只有一个正在进行,避免同时启动多个编译进程。具体实现步骤如下:

主要逻辑和步骤:

  1. 获取进程的退出码

    • 当进程完成运行后,可以通过 GetExitCodeProcess 函数获取进程的退出码,判断进程是否顺利完成。这是为了确保编译已经完成,并且能够获取编译过程的状态。
  2. 编译状态管理

    • 引入了一个 debug executing processdebug process state 结构,用于存储和管理编译进程的状态。这样可以确保在调试系统中,编译进程的状态可以被正确追踪。
  3. 防止重复编译

    • 通过在 debug state 中增加 compiling 状态来管理是否正在进行编译。当 compilingtrue 时,表示当前正在进行编译,后续的编译请求会被阻止。
    • 在每次启动编译时,首先检查 compiling 状态,如果已在编译中,则不会启动新的编译进程。这确保了每次只能执行一次编译。
  4. 控制编译执行

    • 使用 platform debug system 来管理编译过程的启动和状态更新。启动编译时,将 compiling 设置为 true,确保没有其他编译进程被启动。如果编译完成后,compiling 状态会保持 true,直到下一次编译请求。
  5. 实现编译状态的保护

    • 一旦编译执行完成,compiling 状态保持为 true,防止多次编译的触发。只有在 compiling 状态为 false 时,才能重新启动编译。

结果:

  • 通过这种状态管理机制,确保了每次编译只会执行一次,避免了重复启动编译进程的情况。
  • 如果编译过程已完成,下次编译不会被重复启动,从而节省了系统资源,避免了不必要的操作。

这个实现使得编译操作更加稳定和高效,避免了并发启动多个编译进程的冲突问题。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

段错误取地址
在这里插入图片描述

通过图形显示正在进行的重新编译

在这个步骤中,目标是改进编译状态的输出并实时反馈进程的状态。具体步骤如下:

主要步骤:

  1. 检查编译状态

    • 首先检查 debug state compiling 是否为 true,表示是否正在进行编译。
  2. 获取进程状态

    • 如果正在编译,需要通过调用平台调试 API(如 platform debug process state)来获取编译进程的状态。
  3. 输出调试信息

    • 如果编译进程仍在运行(即状态为“运行”),则输出调试文本,显示“编译中”。
    • 如果编译进程已经结束,说明编译完成,则输出调试文本,显示“编译完成”,并将 compiling 状态设置为 false,表示编译已结束,可以进行下一步操作。

实现效果:

  • 实时反馈:调试输出会在编译进程进行时显示“编译中”,一旦编译完成,输出“编译完成”,并更新状态。
  • 状态管理:通过这个机制,可以准确追踪编译进程,并避免重复启动多个编译操作。

总结来说,这个改进能够实时显示编译进程的状态,有效地管理和监控编译过程。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

发现平台API没有DEBUGGetProcessStateγ

在这个部分,主要目的是修复平台 API 的问题。具体来说,问题出在调试系统中的某些细节上,可能是平台 API 调用没有按预期工作。因此,需要对平台 API 进行检查和修复,确保它能够正确处理和反馈编译状态。

在这部分中,主要是修复和实现编译过程的检查,确保编译过程能够正确运行。通过调用调试API,能够获取到当前进程的状态,确认编译是否正在进行或已完成。通过这一过程,可以在游戏运行时启动编译任务,并在编译完成后能够准确地得到反馈。这项功能的实现使得调试系统更为强大,可以更好地管理和追踪编译状态。

这些改进为接下来的工作带来了更大的灵活性和便利,虽然这一功能在之前并未尝试过,但现在可以在游戏内部直接控制和管理编译过程,带来了不少潜力和前景。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

为什么要从游戏中编译代码,如果我们可以从编辑器中做到?只是为了酷吗?

这段内容讨论了在游戏中实现编译功能的原因。主要的目的是为了提高工作效率,避免在游戏和编辑器之间来回切换。传统的工作流程可能需要不断切换到编辑器,重新编译,然后再返回到游戏中进行调试。通过将编译功能集成到游戏中,可以在游戏运行时直接进行编译和更新,这样就不需要频繁切换,可以更专注于当前的工作。

这种做法的好处是能实时更新,尤其是在调试UI或其他需要频繁修改的功能时,能够快速看到效果,而不需要中断工作流程。此外,这种方法也避免了在代码中嵌入多个调试开关的复杂性,使得调试过程更加高效和灵活。虽然这种方法可能并非对所有情况都适用,但作为一种新的调试方式,它具有一定的吸引力和潜力。

:在mr4th的编辑器中编译并且在游戏内进行所有开发工作?

我们希望能够有一个类似“森林编辑器”的工具,能够直接集成到游戏开发工作中,从游戏内进行所有的编辑和开发工作。如果能有一个这样的工具,像一个API或者一个库,能够方便地被调用并直接在开发过程中使用,那将是非常棒的。通过这种方式,我们可以在游戏内部直接进行调整、测试和编译,而不需要切换到外部编辑器或者其他开发环境,从而提升工作效率和便捷性。

我喜欢“STARTUPINFO”,但是“PROCESS_INFORMATION”。即使在WinAPI的一个小部分中也有不一致性… 你对专业API中的这种情况会感到恼火吗?

我们会注意到,在处理信息和处理不一致的情况下,哪怕是API中的一个小部分,出现这种问题也会让人感到有些烦恼。对于一个专业的API来说,这种问题确实会让人不太舒服。不过,如果这些小问题是唯一的问题,反而还不至于让人特别不开心。问题的严重性在于,除了这些小问题,往往还会存在一些更加严重和更为糟糕的问题。如果能够解决这些更为关键和严重的问题,解决这些小的不一致问题就显得不那么重要了,甚至可以接受。

为什么我们要写入代码文件,而不是直接传递-D到构建行?这似乎会更简单,尽管我得承认你让这一切看起来很容易

我们选择写入代码文件而不是直接通过命令行传递 -D 参数,原因是接下来的过程会变得更复杂。假设会有大约200个类似的参数,其中可能包含字符串或表达式,这些内容在传递给shell时会非常麻烦,因为需要正确地引用它们,避免出现溢出或其他不确定的问题。而代码文件则可以灵活处理这些内容,不管多复杂。更重要的是,代码文件能够保留这些设置,在下一次构建时仍然能够使用,因此它有助于保存这些设置的持久性,避免每次都重新配置。这种方式更加可靠和方便。

为什么不使用system()而是CreateProcess()?

我们不想在游戏中使用任何外部库,尤其是在最终的发布版本中,目标是尽量不依赖任何库。目前唯一阻碍我们实现这一目标的就是C运行时库。所以,我们希望尽量减少调用的函数,以便在未来能够更容易地去除它。尽管使用 system 函数也许会节省一点时间(可能是五到十分钟),但我们更倾向于使用 CreateProcess,因为它不会依赖C运行时库,这样做有助于减少外部库的使用。我们的最终目标是游戏不使用任何外部库,甚至连编译器的库也不再链接。所以,尽可能避免使用C运行时库,是我们朝着这个目标前进的一部分。

你是否计划读取原始配置文件并用修改后的值重新写入?

我们确实计划读取原始配置文件,然后将其内容与修改后的值一起重新写入文件。这是为了确保配置文件能够反映出最新的设置。至于为什么不直接设置一个布尔值,可能是因为使用更复杂的方式可以让配置的管理更加灵活,特别是在需要进行多个参数修改或调整时,直接使用布尔值可能不够精确或者无法满足复杂需求。

为什么不设置一个布尔变量,而不是重新编译一个#define?

我们选择使用变量而不是重新编译,主要是为了避免性能上的开销。这样做的好处是,可以在游戏中随处插入大量的调试代码,包括性能分析块。我们可以通过游戏的UI动态地开启或关闭这些性能分析块,这样就可以在特定情况下对某些模块进行分析,而不会因为它们生成过多的调试事件而影响游戏的整体性能。如果我们不想在大多数情况下进行某些分析,可以灵活地关闭它们,提供更多控制和灵活性。而且这一切都可以通过游戏的UI来完全处理和调整,十分方便。

至于是否会有开发者控制台,可能不会有。

会有开发控制台吗?

我们目前没有想到任何需要开发者控制台的理由。因此,暂时没有考虑添加这样的功能。

另外,system()是通过CMD.EXE运行程序的,这是另一个依赖项

system 函数通过 cmd.exe 来运行程序,这确实是另一个依赖项。不过,考虑到我们已经需要 cmd 来进行构建工作,所以这并不算是一个额外的依赖。至于微软的编译器,运行速度比较慢,这使得编译的周转时间变得较长。如果编译器能更快一些,整体的效率就会提高。至于资产构建器,现在已经不再需要重新构建它,因为我们已经完成了当初的需求,所以可以不再依赖它了。

聊天室推荐使用CREATE_NO_WINDOW作为进程创建标志。你可以使用它来代替最小化窗口

聊天中建议使用“创建新窗口”作为进程创建的标志,可能可以代替最小化窗口。不过,我不认为最小化窗口是我们需要的,实际上它并不是最小化的。编译时,窗口并不会显示出来,使用的是 SW_HIDE 而不是 SW_MINIMIZESW_HIDE 意味着完全不向用户显示窗口。因此,我们不需要阻止窗口的创建,实际上,它可以创建窗口,只是我们不显示它。我觉得这样可能更好,因为如果完全不创建窗口,可能会出现一些未知的问题,谁知道会不会因此导致某些东西出现故障。有时候,我甚至都不清楚批处理文件里到底包含了什么内容。

运行的bat文件中有什么?

这个过程实际上是在运行我们的标准构建批处理文件,这是我们用来构建的常规文件。它就是在执行我们平时使用的构建流程,所以其实并没有做什么特别的操作。

为什么它缩小了?

每次进行设置时,实际上是在修改调试相机的位置。每次操作时,都会重新设置调试相机的角度。至于编辑器的选择,我使用的是 Emacs,而不是 Sublime。

你可以为调试相机添加一个径向菜单,而不是依赖切换图形吗?它让我有点烦

不能现在就做,因为我们计划明天进行这个工作,不要跳到前面去。我们会在明天处理调试相机的相关功能,包括是否添加一个径向菜单来替代目前依赖的地形系统,避免这种做法带来的困扰。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2328493.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通信数据记录仪-产品概念ID

总结: 1、支持高速CAN、支持容错CAN、支持单线CAN(理解是支持不同的协议,CANFD、CAN2.0和LIN?) 2、 通过上位机设计时间

Mac VM 卸载 win10 安装win7系统

卸载 找到相应直接删除&#xff08;移动到废纸篓&#xff09; 可参考&#xff1a;mac如何卸载虚拟机win 下载 win7下载地址

基于图扑 HT 技术的电缆厂 3D 可视化管控系统深度解析

在当今数字化浪潮席卷制造业的大背景下&#xff0c;图扑软件&#xff08;Hightopo&#xff09;凭借其自主研发的强大技术&#xff0c;为电缆厂打造了一套先进的 3D 可视化管控系统。该系统基于 HT for Web 技术&#xff0c;为电缆厂的数字化转型提供了有力支撑。 HT 技术核心架…

《AI大模型开发笔记》MCP快速入门实战(一)

目录 1. MCP入门介绍 2. Function calling技术回顾 3. 大模型Agent开发技术体系回顾 二、 MCP客户端Client开发流程 1. uv工具入门使用指南 1.1 uv入门介绍 1.2 uv安装流程 1.3 uv的基本用法介绍 2.MCP极简客户端搭建流程 2.1 创建 MCP 客户端项目 2.2 创建MCP客户端…

常见的ETL工具分类整理

一、开源ETL工具 ‌Kettle&#xff08;Pentaho Data Integration&#xff09;--Spoon‌ 设计及架构&#xff1a;面向数据仓库建模的传统ETL工具。使用方式&#xff1a;C/S客户端模式&#xff0c;开发和生产环境需要独立部署&#xff0c;任务编写、调试、修改都在本地。底层架构…

Smart Link 技术全面解析

1.1 网络冗余技术的演进与需求 1.2 Smart Link 的核心价值与本文目标 第一章 Smart Link 技术概述 2.1 Smart Link 的应用场景与背景 2.2 Smart Link 的基本概念与组网角色 2.3 Smart Link 与传统技术的对比 第二章 Smart Link 工作原理 3.1 Smart Link 组的构成与运行机…

Roo Code(前身为 Roo Cline)一个 AI 驱动的自主编码代理

Roo Code&#xff08;前身为 Roo Cline&#xff09; Roo Code 是一个 AI 驱动的自主编码代理&#xff0c;它存在于您的编辑器中。它可以&#xff1a; 用自然语言沟通直接在您的工作区读写文件运行终端命令自动化浏览器操作与任何 OpenAI 兼容或自定义的 API/模型集成通过自定…

数字化三维实训室:无穿戴动作捕捉技术如何赋能体育与舞蹈

在高校体育与舞蹈教学中&#xff0c;精准的动作训练至关重要。传统训练方式依赖教练的肉眼观察与手动记录&#xff0c;存在效率低下、误差较大的情况。尤其在快速连续动作或复杂肢体形态的捕捉中&#xff0c;人工判读易受主观经验限制&#xff0c;难以实现标准化评估。面对传统…

leetcode51-N皇后

leetcode 51 思路 本题可以使用回溯算法来解决。回溯算法通过尝试所有可能的解决方案来找到问题的解的算法&#xff0c;当发现当前的选择无法得到有效的解决方案时&#xff0c;就回溯到上一步&#xff0c;尝试其他的选择。对于 N 皇后问题&#xff0c;我们可以逐行放置皇后&…

linux 命令 awk

awk 是 Linux/Unix 系统中一个强大的文本处理工具&#xff0c;尤其擅长处理结构化文本数据&#xff08;如日志、表格数据&#xff09;。它不仅是命令行工具&#xff0c;还是一种脚本语言&#xff0c;支持变量、条件、循环等编程特性 1. 基本语法 awk [选项] 模式 {动作} 文件名…

R语言——获取数据1

参考资料&#xff1a;学习R 数据的来源可以由很多。R内置有许多数据集&#xff0c;而在其他的附件包中能找到更多的数据。R能从各式各样的来源中读取&#xff0c;且支持大量的文件格式。 1、内置的数据集 R的基本分发包有一个datasets&#xff0c;里面全是示例数据集。很多其他…

从零开始打造HTML5拼图游戏:一个Canvas实战项目

从零开始打造HTML5拼图游戏&#xff1a;一个Canvas实战项目 先看效果&#xff1a; 你是否曾经被那些精美的网页拼图游戏所吸引&#xff1f;用 HTML5 的 Canvas 技术&#xff0c;从零开始&#xff0c;教你怎么画图、处理鼠标事件&#xff0c;还有游戏的核心逻辑&#xff0c…

每日一题洛谷P8649 [蓝桥杯 2017 省 B] k 倍区间c++

P8649 [蓝桥杯 2017 省 B] k 倍区间 - 洛谷 (luogu.com.cn) #include <iostream> #include <vector> using namespace std; #define int long long signed main() {int n, k;cin >> n >> k;vector<int> a(n 1);vector<int> sum(n 1);vec…

Linux(十二)信号

今天我们就要来一起学习信号啦&#xff01;&#xff01;&#xff01;还记得小编在之前的文章中说过的ctrlc吗&#xff1f;之前小编没有详细介绍过&#xff0c;现在我们就要来学习啦&#xff01;&#xff01;&#xff01; 一、信号的基本介绍 首先&#xff0c;小编带领大家先一…

LeetCode算法题(Go语言实现)_30

题目 给定单链表的头节点 head &#xff0c;将所有索引为奇数的节点和索引为偶数的节点分别组合在一起&#xff0c;然后返回重新排序的列表。 第一个节点的索引被认为是 奇数 &#xff0c; 第二个节点的索引为 偶数 &#xff0c;以此类推。 请注意&#xff0c;偶数组和奇数组内…

【读书笔记·VLSI电路设计方法解密】问题61:扫描插入的目的是什么

如问题60所述,要构建可测试电路,必须确保电路中每个节点都具有可控性和可观测性。但对于包含时序元件(如触发器、锁存器等存储元件)的电路,若不采取特殊设计则难以实现这两项特性。这是因为时序元件关联节点的逻辑状态不仅取决于当前输入,还受其先前存储状态影响——它们…

VirtualBox安装FnOS

1.下载FnOS镜像 下载网址&#xff1a; https://www.fnnas.com/2.创建虚拟机 虚拟机配置如图所示&#xff08;注意操作系统类型和网卡配置&#xff09; &#xff08;注意启动顺序&#xff09; 3.启动虚拟机 网卡类型选择桥接的Virtual Adapter 如果没有IP地址或者IP地址无法…

Pycharm(十二)列表练习题

一、门和钥匙 小X在一片大陆上探险&#xff0c;有一天他发现了一个洞穴&#xff0c;洞穴里面有n道门&#xff0c; 打开每道门都需要对应的钥匙&#xff0c;编号为i的钥匙能用于打开第i道门&#xff0c; 而且只有在打开了第i(i>1)道门之后&#xff0c;才能打开第i1道门&#…

集合与容器:List、HashMap(II)

一、ArrayList 是集合框架中最核心的动态数组实现&#xff0c;高频使用的容器之一。 1. 核心数据结构 基于数组实现&#xff0c;维护elementData数组存储元素&#xff1a; transient修饰的elementData不会被默认序列化&#xff08;通过自定义序列化逻辑优化存储&#xff09;…

《AI大模型应知应会100篇》第3篇:大模型的能力边界:它能做什么,不能做什么

第3篇&#xff1a;大模型的能力边界&#xff1a;它能做什么&#xff0c;不能做什么 摘要 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;LLM&#xff09;已经成为许多领域的核心技术。然而&#xff0c;尽管它们展现出了惊人的能力&#xff0c;但也有明显的局限性…