游戏引擎学习第204天

发布于:2025-04-05 ⋅ 阅读:(16) ⋅ 点赞:(0)

回顾并为今天的内容做铺垫

好,现在开始这一集。今天我们将进行一些用户界面编程,觉得这是一个展示如何编写这类代码的好时机。很多人对如何做用户界面代码都很好奇,所以展示一下如何编写是非常有意义的。

我之所以在现在的这个地方做这些工作,是因为对于目前的游戏来说,并没有太多理由去做复杂的用户界面代码。毕竟,这不是一个需要复杂界面的游戏,没有很多工具栏、对话框或者其他类似的东西,游戏的类型是动作游戏。因此,我利用这个机会,编写一些用户界面代码。

我们目前正在做的是一个调试查看器,基本上就是一个查看调试信息的工具。我们在这个功能上已经取得了相当大的进展,能够做一些很酷的事情。但仍然有更多的工作要做,所以今天我想继续在这个方面进行编码。

运行游戏,看看当前进展

好,现在来看一下目前的进展。这里是我们的键盘系统,正在运行的状态。上次我们离开时,做了一些小的测试,我们最近做的事情是添加了能够管理多个层级版本并分别跟踪它们状态的功能。这意味着我们可以有多个层级的状态,并且可以分别处理每个层级的状态。

这样,如果我们希望有一个渲染部分在某些地方显示,而在其他地方隐藏,系统是完全支持的,完全不会造成问题。我们通过这个系统解耦了层级的状态与实际层级本身的关系。这样,我们就不需要担心层级的状态与正在交互的对象之间的耦合问题。

这是我认为应该做的事情,因此我们也实现了它。接下来的目标是,我希望今天开始,因我们已经接近完成,能够继续推进我们的调试层级视图的工作。

考虑是否要摆脱显式构建调试层次结构

我想做的事情是开始查看调试变量的实际表现形式。我的目标是将调试基础设施向前推进一步,达到一个比较重要的地方。目前我们所做的是,调试层级的底层数据是显式构建的,并且一直存在在内存中。

具体来说,如果查看代码的话,你会看到我们在这里显式地构建了这个层级的实际版本,并且它会一直存在于内存中。这意味着任何想要添加调试变量的地方,都必须实际创建一个真实的调试层级,并将其存储在内存中。这样做的一个问题是,它可能会让一些代码变得麻烦,尤其是当其他代码想要快速暴露调试变量时。

因为现在这是一个保留模式的东西,任何想要暴露调试变量的代码都必须经历创建它并将其放到某个地方的麻烦。对于全局变量来说,这并不算什么大问题,因为它们像是开关,开了就能用,关了就没有影响。对于像是否使用大地震的轮廓这样的全局变量,开关的存在和关闭没有太大影响,我们只需要创建它然后忘记它,调试系统就可以始终使用它。

然而,我更感兴趣的是处理更复杂的问题,特别是当游戏中的某些内容想要暴露调试信息时,它们不一定想花费额外的精力去创建一个实际的调试树。这种情况下,我们可能希望避免让这些内容强制管理或存储一些东西,而是希望它能够直接工作。

所以,我想做的是探索一下如何使这个过程变得更顺利,尤其是让我们能够检查实体的状态。比如,能够查看一个实体的值,因为这是调试过程中可能比较困难的一部分。我们可能并不总是知道哪个实体出了问题,想要了解它的状态很困难。能够查看某个实体的状态,可能会是一个我们系统中非常有用的功能。

今天,我希望能够从这个方向开始,着手解决这个问题。

讨论编写使用代码并统一系统

决定完全忽略已经写好的调试代码,先去修改实体代码,使得能够检查那些变量。接下来,回到调试系统,并将这两个部分统一起来。

这种做法是常见的编程技巧。如果知道某个操作或任务需要系统完成,通常会先编写该任务的使用代码,即先实现如何使用该系统,然后让这个使用代码决定系统应该如何工作。在这种情况下,虽然之前已经做了一些初步的工作,例如为预处理定义了一些常量,但对其他用例并没有真正考虑过。所以,计划清除当前的用例,并让新的需求驱动系统的更新,而不是先改变系统结构,期望它能支持新需求,再去使用时发现不合适。通过先实现目标,然后根据实际需求来完善系统,是一种更加高效的工作方式。

game_sim_region.cpp:查看当前实体的处理方式

如果我们要做类似的事情,可以先看看game_sim_region代码,这是我们处理敌人的地方。你可以看到我们在这里是怎么做的。

当我们进行初始化时,我们会循环遍历所有在当前区域中的实体,并将它们添加到模拟sim系统中。这部分代码在这里有体现。所以,如果我们在这里做类似的操作,去循环并将实体添加到模拟系统中,那么就会出现一个有趣的问题:是否应该在这个地方宣布实体呢?这个问题并不好回答。

但是我知道的是,在将所有这些实体添加到模拟系统之后,我们现在可能有一种比较简便的方式来选择它们。所以我想做的是,能够做一些操作,让我们能够很容易地选择并检查这些实体。

实现点击实体进行检查的功能

我们希望能够通过鼠标点击或者其他方式来选择实体。具体来说,就是能点击到某个实体,并选中它。这样,我们就能知道这个实体在游戏中的具体情况,或者它的属性和状态,基本上就是可以通过这种方式来“查看”该实体。

在初始化过程中,我们会添加实体到模拟系统中,可以看到在这段代码里,我们将它们加入到模拟中,并且有一个实体碰撞体积组,这个组告诉我们实体的边界范围。基本上,我们目前对实体边界的了解就是这些。

我记得,当我们进行“移动实体”时,可能会使用这些碰撞体积来处理,或者使用其位置来插入数据到集合中。我认为插入时不太关心实体的边界,而是直接用位置将其放入集合。虽然碰撞体积是我们大部分实体的一个属性,但其实我们也可以仅使用实体的位置来进行选择。

目前来说,不确定哪种方法更适合,但是无论哪种方式,都有一些选择和可能性。
在这里插入图片描述

运行游戏并演示我们想要实现的效果

首先,想要处理的第一个问题是如何绘制实体的碰撞体积,以便我们可以看到它们。到目前为止,如果观察当前显示的内容,我们可以看到实体和它们的碰撞体积,但是这些碰撞体积并没有在屏幕上显示出来。实体可能是不可穿透的,但它的碰撞体积并没有直观呈现给我们。

为了能够看到这些碰撞体积,我打算引入一种方法,在屏幕上绘制这些碰撞体积。这样,我们就能直观地看到这些碰撞区域。为此,我们可能需要引入像线条这种基本图形,或者考虑使用矩形来绘制碰撞体积,尽管这有些复杂,因为矩形的两个点在三维空间中的投影等问题需要考虑。

碰撞体积的定义中包含一个位置偏移和一个尺寸(dim)。这意味着每个碰撞体积都有一个位置坐标,相对于实体的中心位置,以及一个表示其大小的尺寸。在渲染系统中,我们没有直接用来渲染碰撞体积的东西。现有的矩形渲染功能只支持二维坐标的尺寸,而碰撞体积的尺寸实际上是三维的,这样就无法直接渲染出一个三维矩形。

因此,考虑到这一点,我们可能需要设计一种新的方法来绘制这些碰撞体积。例如,我们可以创建一个函数,接受两个三维点来构建一个矩形,围绕这两个点绘制出碰撞体积的区域。或者,我们也可以使用“绘制矩形轮廓”这样的功能,因为我们可能不想填充碰撞体积,而是仅仅绘制出其边界,这样可能会更有意义。

game.cpp:引入DEBUGUI_DrawEntityOutlines

首先,开始时的目标是实现一个简单的功能。当处理所有实体和渲染循环时,可以在渲染的最后阶段,遍历所有实体的碰撞体积,并为每个实体绘制轮廓。这是一个简单的过程,可以在渲染流程的末尾完成。

在渲染过程中,已经有了处理“推送矩形”的功能,而现在的目标是将这种功能扩展到所有实体的碰撞体积。可以通过遍历所有实体并为每个实体的碰撞体积绘制轮廓,这样无论是什么类型的实体,都能绘制出它们的碰撞体积边界。

接着,需要在配置文件中定义一个新的选项,比如 "DrawEntityOutline "。这个选项的作用是启用绘制实体轮廓的功能。通过将这个设置添加到配置文件中,就能控制是否绘制所有实体的碰撞轮廓。

在实现时,会进行一些变量和代码的定义工作,比如在 RGB 变量文件中添加对应的定义,并确保功能能够编译通过。这样,系统就可以根据需要绘制所有实体的轮廓,帮助调试和检测问题。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

运行游戏,查看新的轮廓

在实现了实体轮廓绘制功能后,得到了大致正确的效果,可以看到不同的实体轮廓已经被绘制出来。我们使用了与之前相同的代码来绘制这些轮廓,效果是可以接受的。绘制的轮廓有些许厚重,但整体上效果基本正确。

对于碰撞体积的绘制,虽然只考虑了二维的 X 和 Y 维度,而忽略了 Z 维度的范围,这种做法虽然简化了问题,但对于当前的需求来说,这种处理方式是可以接受的。毕竟,这样的绘制方式对于调试和检测是足够的,尤其是在当前的系统中,可能不需要过于复杂的三维碰撞体绘制。

game_render_group.cpp:使PushRectOutline函数可以额外接受Thickness参数

目前绘制的轮廓看起来稍微有些厚,可能需要调整一下。在考虑这个问题时,怀疑目前使用的 PushRectOutline 方法是否支持调整线条的粗细。似乎它并不直接支持这种功能,因此需要考虑对这个方法进行修改或扩展,允许设置轮廓的线条厚度。

理想情况下,可以在调用 PushRectOutline 时,传入一个可调整的厚度值,这样在绘制实体的轮廓时,可以根据需求调整线条的粗细,使得轮廓显示更加精细或符合调试需求。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

运行游戏,查看更细的品红色轮廓

现在,已经能够在屏幕上看到实体的边界,边界使用了鲜明的品红色轮廓,效果不错。接下来,目标是实现鼠标选择这些边界中的一个。当鼠标在屏幕上移动时,可以点击某个实体的边界,并将其选中,突出显示该实体。

为实现这个目标,只需要判断鼠标指针是否位于某个矩形区域内,就像之前所做的那样。通过这种方式,可以轻松地实现点击选择某个实体的功能。

考虑视角变换的问题

问题在于,当前的视图使用了一个变换(transform)。例如,使用调试摄像机时,整个场景会缩小,所以显然在渲染时并没有一个标准的坐标系统。实际的过程是,世界坐标和摄像机的位置通过投影转换为屏幕上的实体位置。

为了让鼠标能够准确地与实体进行交互,需要将鼠标位置从屏幕坐标反转回游戏世界坐标。这是因为,鼠标的位置是基于屏幕坐标的,而实体的位置是通过摄像机变换投影到屏幕上的。因此,需要逆转这个渲染过程,将鼠标位置从屏幕坐标转换回世界坐标,才能进行正确的碰撞检测和选择。

在调试界面中,不需要做这种转换,因为调试界面是直接在屏幕上的像素空间中进行的,鼠标位置和界面元素的像素位置是一致的,所以没有这种复杂的变换过程。而在处理游戏实体时,由于这些实体是经过摄像机变换的,需要找到一种方法来逆转这个过程,才能确保鼠标点击能够正确地选中对应的游戏实体。
在这里插入图片描述

game_render_group.cpp:查看Unproject函数

在代码中,存在一个名为 Unproject 的函数,它用于将屏幕上的坐标逆投影回世界坐标。这个过程通过获取显示器的维度,进行逆投影,从而了解摄像机视野的范围。事实上,这个逆投影过程本来就是有用的,且已经存在了。

函数的使用是基于某种矩形区域,目的是从显示器的维度推算出摄像机能够看到的世界坐标。因此,理论上,如果将鼠标坐标传入这个 Unproject 函数,它可以返回鼠标在世界坐标中的位置。

这个过程的关键在于能够将屏幕上的像素坐标转换回世界中的相应位置,这样鼠标点击事件就可以正确地与世界中的实体进行交互。
在这里插入图片描述

黑板:为什么我们需要相机的距离

在讨论屏幕上鼠标的位置时,假设屏幕、摄像机和世界中的某个点(如树)之间的关系,可以通过投影来理解。首先,摄像机会将树上的某个点投影到屏幕上,屏幕上的每个点都对应着世界中一条从焦点到该点的线上的所有点。换句话说,在屏幕上的一个点对应着一条线,而这条线上的每个点都会投影到该屏幕上的同一位置。

但是,当我们进行逆投影时,问题就变得复杂了。如果已经知道了屏幕上的某个点,想要找出对应世界中的位置,实际上并不存在一个唯一的世界坐标与屏幕上的单一点一一对应。因为世界中的每个点都会映射到这条线上的某一个位置,所以我们无法直接得到一个明确的坐标。

为了克服这个问题,需要引入一个额外的参数——摄像机到屏幕上的某个平面的距离。这个距离表示的是沿着投影线的距离,用来确定在该投影线上应该选择哪个点。通过这个距离,可以找到相应的世界坐标,因为在知道这个距离后,我们就能确定对应的世界位置。

黑板:概念化实体拾取的过程

在进行选取操作时,问题变得更加复杂,因为我们并不总是知道要选取的是哪个平面。假设在场景中有多个物体重叠在一起,例如远处有更多的树木,这时我们需要能够在多个实体之间进行选择,并且可能有多个实体与鼠标光标重合。因此,当我们尝试进行选择时,不仅仅是在寻找与光标位置对应的单一实体,而是要确定哪些实体与光标所在的射线(即从摄像机到鼠标位置的这条线)相交。

这就涉及到更复杂的操作,因为我们需要考虑每个实体在摄像机视野中的位置,并计算它们是否与这条射线相交。接下来还需要判断,哪些交点离摄像机更近,从而决定最终选中的实体。虽然这个过程稍微复杂,但实现起来并不困难,因此可以通过这个方法来进行选择操作。

我们的目标是当鼠标移动时,能够实时地改变实体的颜色,比如将颜色改为黄色,这样就能直观地看到鼠标当前选中的实体,从而确保选择功能正常工作。

game.cpp:对MouseP进行Unproject处理

为了实现这个功能,可以通过比较简单的步骤来进行。首先,可以设定一个标志来检查是否高亮显示某个实体,或者直接在绘制轮廓时修改轮廓的颜色。例如,初始时轮廓颜色是品红色,然后在某些条件成立时将颜色改变为黄色。这样,就能够直观地看到哪些实体是当前选中的。

具体的做法是,首先需要反向投影鼠标的位置。投影的目的是将鼠标在屏幕上的位置转换成世界空间中的位置,这样才能确定鼠标点击的是哪个实体。在进行这个操作时,还需要确保坐标系是合理的,并且会处理鼠标位置在场景中的转换。

接下来,我们需要计算实体与鼠标光标之间的距离。在这里,计算的是实体在其所在平面上的位置,通常这个位置是相对于摄像机的。计算这个距离时,首先要确定摄像机的位置,并确保摄像机设置正确。通过这种方式,可以得到鼠标点击的实体是否位于指定的平面上。

然后,根据这个信息,我们可以更新鼠标的选择状态。当鼠标点击某个实体时,会根据计算的距离和投影结果来确定实体是否被选中。最终,根据鼠标位置所对应的实体,修改其轮廓颜色,使得选中的实体变为黄色,其他未选中的实体保持原来的颜色。
在这里插入图片描述

game.cpp:将LocalMouseP与2D实体Volume->Dim进行比较

我们讨论的是如何实现碰撞检测,特别是在一个二维空间中判断一个点(比如鼠标位置)是否与某个矩形碰撞。首先,我们假设有一个碰撞体积的矩形,该矩形的维度和偏移量都已知。我们的目标是基于这些信息,判断一个给定的点(如鼠标的位置)是否位于该矩形内部。

  1. 理解碰撞体积的表示:每个碰撞体积有一个矩形维度(宽度和高度)以及一个偏移量(相对于某个参考点的位置)。我们假设碰撞体积的矩形是以某个中心点为基准,定义了一个矩形区域。

  2. 鼠标位置转换:为了判断鼠标是否在矩形内,首先需要计算出鼠标相对于碰撞体积中心的局部坐标。也就是说,要将鼠标的全局坐标转换为相对于碰撞体积中心的坐标。这可以通过减去碰撞体积的中心位置来实现。

  3. 判断是否碰撞:当我们得到了鼠标的局部坐标后,可以比较这些坐标是否在碰撞体积矩形的范围内。具体来说:

    • 如果鼠标的x坐标在矩形的左右边界之间,并且y坐标在矩形的上下边界之间,那么可以认为鼠标与该碰撞体积发生了碰撞。
    • 由于碰撞体积是以中心点为基准的矩形,我们需要检查鼠标坐标是否在矩形宽度的一半和高度的一半的范围内。
  4. 局部坐标转换和碰撞测试:通过将坐标转换为局部坐标后,判断条件会变得非常简单:

    • 检查局部坐标x是否大于负的矩形宽度的一半且小于矩形宽度的一半。
    • 同理,检查局部坐标y是否大于负的矩形高度的一半且小于矩形高度的一半。
  5. 简化处理:这种碰撞测试方式仅适用于二维空间。虽然可以使用更复杂的三维射线检测来进行更精确的碰撞检测,但在调试过程中,二维检测已经足够使用,不必复杂化。

通过这种方式,可以高效地检测鼠标是否在碰撞体积内,而不需要进行复杂的三维碰撞测试。
在这里插入图片描述

game.cpp:对鼠标位置进行PushRect处理

我们正在讨论如何处理鼠标输入,并记录鼠标位置以便进行调试。首先,系统会给出鼠标的x和y坐标,也就是我们可以通过这些坐标知道鼠标的位置。但问题是,系统提供的这些坐标并不总是准确的,因此我们希望能够记录下鼠标的位置,以便查看系统认为的鼠标位置与实际的鼠标位置之间的差异。

为此,我们决定使用一种方法来“推送”记录鼠标的位置。具体来说,不需要特别复杂的操作,可以通过临时记录鼠标的坐标来实现。这样,在渲染操作之后,也就是在控制器逻辑执行完毕之后,我们就能看到鼠标的真实位置。

这个过程涉及到在渲染时记录鼠标坐标,而不是依赖于每次都重新计算或记住系统认为的鼠标位置。通过这样的方法,我们可以轻松地调试和检查鼠标坐标的准确性,而不需要做复杂的处理。

game.cpp:做一个正交(Orthographic)调用绘制MouseP

在进行鼠标位置调试时,我们希望能够在投影之前显示鼠标的位置。为此,需要先进行一个正交投影(orthographic projection),这其实不算复杂的操作。正交投影可以帮助我们在渲染时准确地显示鼠标位置。

首先,我们要在执行控制器逻辑之后(也就是做其他事情之前),通过正交投影来绘制鼠标的当前位置。这可以通过简单地将鼠标坐标用 ASCII 图形或其他简单方式显示在屏幕上,作为额外的调试输入。

为了做到这一点,我们需要确保在开始绘制之前清理屏幕,避免任何残留的图形干扰当前的显示。清理操作通常会清空整个表面,但具体行为需要确认。然后,清理之后,我们可以绘制一个矩形框来表示鼠标的当前位置。这个矩形框的尺寸可以简单设置为固定的值,比如 1x2 或者 2x2,这样就能在屏幕上看到鼠标的准确位置。

完成这些步骤后,我们就可以在进行更复杂的操作之前,确认鼠标的位置是否正确。需要注意的是,绘制鼠标位置的操作应该在所有其他渲染内容之后进行,以确保鼠标位置能够正确显示在其他图形之上。

最后,应该确保这些绘制操作按顺序进行,尤其是在进行更复杂的渲染之前,这样才能有效地进行调试,确保鼠标位置的准确性。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

game_render_group.cpp:向Orthographic函数中添加OffsetP和Scale参数

在渲染过程中,另一个想法是重置变换矩阵,特别是重置渲染偏移量(OffsetP)。我们需要检查是否有办法将渲染变换矩阵设置为单位矩阵(identity)。这一步非常重要,因为单位矩阵代表没有任何变换,所有坐标都会按原始状态进行处理。这样可以确保在渲染其他内容时,不会受到之前的变换影响。

查看当前的变换矩阵,发现偏移量(OffsetP)并没有被设置,而变换矩阵包含了偏移量和缩放(scale)。这些值没有在当前的渲染过程中得到适当的重置,这可能会影响后续的渲染操作。因此,可能需要手动清理这些变换,确保它们被正确重置为初始状态。

如果不清理这些变换,可能会导致后续渲染结果不正确,因为之前的偏移和缩放操作会影响新的渲染内容。所以,觉得在渲染之前重置这些变换矩阵是必要的,这样可以确保渲染的每一步都是基于正确的坐标和状态进行的。
在这里插入图片描述

在这里插入图片描述

两行要移动到函数结尾不然会有问题

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

运行游戏,查看鼠标位置

现在可以看到鼠标位置已经正确显示在屏幕上,这表明我们已经验证了鼠标位置的准确性,这是我们想要的结果。然而,仍然存在一个问题,就是在正交模式下,虽然我们能够看到鼠标的位置,但我们并不确定这个鼠标位置是否已经正确地投影到屏幕上。具体来说,我们不清楚鼠标的坐标是否已经按照正确的投影方式显示在屏幕上。

因此,我们需要查看鼠标位置的实际值,尤其是当鼠标位于屏幕的不同位置时(例如,当鼠标位于屏幕的顶部)。通过查看这些实际值,我们可以确定鼠标坐标是否正确地被投影到了屏幕上,并确保坐标转换过程没有出错。这样可以帮助确认渲染和投影过程的准确性,确保鼠标位置在不同情况下的正确显示。

调试器:进入GameUpdateAndRender并检查MouseP

目前,想要检查鼠标坐标的实际值,因此决定在代码中随便一个地方设置断点,不管是在游戏更新(game update)还是渲染(render)过程中的任何位置。目标是查看鼠标位置的实际值,即 MouseP 被设置成什么样,特别是当鼠标坐标最终被计算出来时。

为了更方便地查找,使用了快速查找功能,搜索了 mouse 相关的代码。在查看了 MouseP 的值后,发现当前的鼠标 x 坐标是 -934,y 坐标是 511。这表明,鼠标坐标实际上是使用屏幕坐标系来表示的。这个坐标值看起来是完全合理的,符合预期的屏幕坐标系统。

通过这种方式,能够确认鼠标的坐标系统是基于屏幕的,确保了坐标转换和渲染过程中的准确性。
在这里插入图片描述

game.cpp:尝试绘制LocalMouseP

接下来,想要将调试过程提升到一个新的层次。在绘制调试轮廓时,除了绘制之前的内容,还希望能够绘制局部坐标的鼠标位置(local mouse position)。这样可以帮助更清楚地查看鼠标在本地坐标系中的位置。

具体来说,当前的操作是遍历这些碰撞体积,并计算出局部鼠标位置(local mouse p)。然后将这个局部位置投影回世界坐标系。接下来,目的是在绘制时能够显示出这个局部鼠标坐标,也就是说,先将局部鼠标位置计算出来,然后通过投影再绘制出来。

为了实现这一目标,需要首先计算出局部坐标对应的z值,然后使用这个值来帮助计算鼠标位置在世界坐标中的实际显示位置。通过这些计算,最终可以决定如何绘制这个位置,可能会用更大的标记或者某种颜色(比如青色),让它在调试过程中更容易辨识。

另外,还可以考虑用一种方式将这个点与其他参考点(如原点)连接起来,形成一个从零到鼠标位置的向量,进一步帮助理解鼠标的准确位置。
在这里插入图片描述

运行游戏,未能显示LocalMouseP

接下来,想要检查是否能够看到绘制出来的局部鼠标坐标。如果看不到,可能是因为计算有问题。理应每个实体的局部鼠标坐标都能显示出来,但目前并没有看到预期的结果。这表明可能存在一些错误,导致鼠标位置没有正确地显示。

考虑到这一点,意识到可能自己在思考这个问题时有所偏差,没有正确地执行所需的操作。当前的做法显然并没有按照预期产生效果,因此需要重新审视代码,确保计算和绘制过程是正确的。

game.render_group.cpp:调查为什么看不到LocalMouseP

目前出现了问题,无法看到预期中的鼠标位置。虽然已经遍历了所有体积(volumes),并且根据每个体积的z值进行了逆投影(reverse projection),并且将x和y坐标与体积进行差值运算,理论上这些应该是局部坐标(local to the entities)。虽然这个计算有一点偏差,因为体积是球形的,但应该还是比较接近预期的局部坐标。

当鼠标悬停在实体上时,应该能够看到鼠标位置附近的某些标记或图形,但实际情况是并没有看到任何预期的结果。这意味着当前的计算或者操作有误,需要进一步分析为什么没有得到正确的结果。

仔细回顾了一下代码,当前的计算方式是:根据相机距离、焦距(focal length)和投影的x、y坐标,进行一些计算来得到鼠标位置的投影。然而,这个计算似乎没有正确地转换为像素坐标。问题出现在投影到世界坐标后,进行从“米”(meters)到“像素”(pixels)的转换时。

具体来说,投影后的x、y坐标经过了“米到像素”的转换,这个转换可能是问题所在。如果我们查看代码,可以看到在进行投影后,计算的是从米到像素的转换。因此,在未进行“米到像素”转换之前,应该先应用这个转换,才能得到正确的像素坐标。

game.cpp:将鼠标的Transform乘以PixelsToMeters

目前,存在一个问题,鼠标位置的转换没有正确完成。鼠标坐标(MouseP)的局部z值已经是以米为单位的,而鼠标位置本身还没有进行单位转换。因此,需要将“米”转换为“像素”才能得到正确的屏幕坐标。

目前有一个“米到像素”(MetersToPixels)的转换方法,但似乎没有实现反向转换(像素到米)。为了解决这个问题,可以通过将“米到像素”的转换反转,即将像素坐标乘以转换的逆值,来进行正确的转换。

此外,发现实际的转换并没有如预期那样进行,因为转换过程应该基于“像素到米”的反向操作,而不是仅仅进行“米到像素”的操作。为了修复这个问题,可以在处理过程中将米转换为像素,然后按照反向转换的方式来进行操作,这样就可以得到正确的鼠标位置。

还需要注意的是,当前计算出来的坐标是在“米”单位下的,这会导致坐标的值过大。因此,需要将计算结果缩小,使得它适应正确的屏幕坐标系统。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

运行游戏,发现矩形位置不正确

目前整体进展逐渐接近目标,虽然还有一些细节没有完全到位,但整体逻辑开始趋于正确。从当前表现来看,鼠标的投影结果已经可以显示出来,但存在一个明显问题:所有的绘制元素似乎都共享了同一个偏移量,而不是根据每个碰撞体(bounding element)分别计算的。

理想情况下,每一个碰撞体的偏移应该是相对于其各自的局部坐标系进行的,而不是统一使用一个通用的偏移量。当前的状态下,虽然鼠标移动可以触发绘制,但由于缺乏个体差异化处理,所有绘制内容的相对位置是一样的,导致逻辑上的偏差。

不过从整体来看,系统已经具备了鼠标位置获取、反投影、单位换算以及初步的绘制能力,说明核心架构已经具备,只是细节尚未处理完全。例如需要为每个碰撞体计算独立的局部坐标偏移,使得最终绘制的位置能够正确反映实体之间的位置差异。

尽管目前效果有些抖动、不稳定,表现略显粗糙,但可以确认各项逻辑正逐步接近预期功能。接下来,只需理清每个体积对应的局部偏移计算逻辑,并确保单位换算在每个实体下都进行独立处理,就可以得到准确的调试绘制结果。整体思路是正确的,剩下的主要是实现细节上的完善与调整。

game.cpp:调查为什么矩形位置不正确

现在在完成了像素与米之间的单位换算后,我们在反投影鼠标位置并将其围绕特定碰撞体进行居中时,又发现了另一个问题。

我们最初在进行偏移量计算时,使用了 OffsetP.xOffsetP.yOffsetP.z,但实际上这些值并不是碰撞体在世界空间中的真实位置。它们仅仅是相对于某个实体的局部偏移。因此,我们必须先将鼠标的位置转换为相对于该实体的位置,才能继续后续的操作。

为此,我们引入了一个新的变量 MetersMouseP,表示以米为单位的鼠标坐标,然后需要将它转换为以该实体为基准的相对坐标。只有在正确地获取这个相对位置后,后续的投影和碰撞检测等操作才能基于正确的空间坐标系进行。

此时,问题还出在 LocalZ 的使用上。原本的 LocalZ 实际上并没有包含该实体的空间位置信息,因此它不是一个正确的局部深度值。要解决这个问题,需要将实体自身的位置(比如实体的位置 entity_p)加入到 MetersMouseP 的计算之中,从而保证所得到的局部坐标是真正相对于该实体的。

另一方面,unproject 操作在内部使用了 transform,而这个 transform 已经是基于当前实体配置过的,因此投影操作本身是没有问题的。然而,进行 MetersMousePOffsetP 的减法操作时,没有经过 transform,就没有包含实体的位移信息,导致计算出来的相对位置不准确。

这一点非常关键:所有发送给渲染系统用于绘制的坐标,已经在 transform 设置后自动变成了相对于当前实体的位置,但我们自己手动做减法计算位置偏移时,必须也要做相应处理,否则就会造成局部坐标的错误。

总结如下:

  • 鼠标坐标必须先转换为米为单位,并基于实体的位置做相对化处理;
  • LocalZ 的值需要包含实体位置的信息,才能代表正确的深度;
  • unproject 自动使用已配置的 transform,因此不会出错;
  • 手动计算偏移时,需要手动包含实体的位移,才能与 unproject 之后的结果对应;
  • 渲染系统在 transform 设置之后,所有坐标都是基于当前实体的,因此后续绘制、测试等都必须使用与之匹配的坐标系。

这些调整是确保调试绘制精确对齐实体和鼠标交互判断的关键步骤。经过这些校正后,鼠标位置的绘制应该可以正确地反映实体之间的空间关系,真正实现准确的调试与交互分析。
在这里插入图片描述

game.cpp:将实体的Transform传递给Unproject调用

当前这部分数学计算存在问题,因为它并没有真正做到“相对”的处理。现在的实现仅仅使用了碰撞体(volume)的 OffsetP,但实际上它还应当考虑实体(entity)本身的位置 p,因为最终的空间位置不仅取决于碰撞体的偏移,还取决于实体在世界空间中的位置。

换句话说,如果只使用 OffsetP,那计算出来的局部坐标就只是相对于碰撞体的原点,而不是相对于整个实体的位置,因此得出的结果是错误的。

正确的做法应该是:需要同时考虑两部分信息:

  1. 碰撞体自身的偏移量 OffsetP
  2. 实体的空间位置 p(也就是 transform 中的基础位置);

只有将这两部分组合在一起,才能得到真正的本地相对坐标,也才能确保和渲染系统的 transform 逻辑一致。

这里其实也进一步说明了一个核心问题:这个错误可能就是之前调试中鼠标位置始终偏离实体、对不上实际位置的根源。因为没有把实体的位置包含进来,所以无论怎么调试,鼠标与碰撞体之间的对齐始终存在偏差。

通过补全这部分逻辑,现在的数学模型才能真正准确反映实体和鼠标在世界空间中各自的位置和偏移关系,进一步保证调试可视化时能够准确显示鼠标所指向的对象和区域。

总结如下:

  • 原来的计算只使用了 OffsetP,缺失了实体的 p
  • 实际上需要把两者组合起来,才能表示完整的空间位置;
  • 渲染系统会基于 transform 来处理坐标,因此手动逻辑也必须与之保持一致;
  • 这可能就是之前鼠标调试逻辑不准确的根本原因;
  • 修复这部分后,坐标的局部化转换才能正确,调试也才真正有效。

在这里插入图片描述

在这里插入图片描述

运行游戏,看到接近正确的位置

我们现在离目标更近了一步,但目前绘制出来的调试图形仍然显得过于保守,偏小,表明计算中仍存在偏差。

当前的处理流程如下:

  • 首先将鼠标位置 MouseP 从像素坐标转换为米单位,这是通过乘以 pixels_to_meters 实现的,这是正确的;
  • 接着使用这个转换后的坐标,结合深度值 z 进行透视反投影 unproject,这也是我们想要做的;
  • 然后从反投影得到的位置中减去实体变换的偏移量 transform.offset 加上碰撞体的偏移量 volume.offset
  • 最后将这个结果用于绘制调试图形;
    在这里插入图片描述

从逻辑上看,这一系列操作是合理的,特别是在反投影时,已经考虑了当前实体的变换变换矩阵,所以理论上变换后的坐标应该就是鼠标在当前实体坐标空间下的位置。

然而实际表现上,并没有达到预期。鼠标移动时,各个实体上的图形没有正确对齐在光标之下,说明推导的相对位置仍有偏差。

这是为什么?

因为在透视投影下,空间坐标的映射并不是线性关系。我们在执行反投影后,得到的是世界坐标中的点,接着将其视为相对于某个实体的局部坐标时,仍需要精确考虑该实体的位姿变换,这里可能出现两个问题:

  1. 反投影之后没有再进行一次变换:虽然 unproject 使用的是 transform,但在绘制前我们手动进行了 OffsetP 的减法,而这个过程可能是冗余或不一致的;
  2. 绘制逻辑与 transform 的使用不一致:如果 render 系统已经考虑了 transform 的偏移量,我们手动再减一次 OffsetP + volume.offset,就可能导致重复变换,使得绘制点位置不准确;

也就是说,当前的做法很可能存在“减多了”的问题,导致绘制位置偏移。

此外,当前结果显示“所有实体的偏移一致”,也说明我们并没有正确对每个实体分别计算对应的本地位置,而是使用了某种全局统一的参考,这进一步表明计算逻辑中仍有瑕疵。

我们判断是否正确的一个核心依据是:调试图形应始终与鼠标光标保持一致。如果不一致,就说明反投影、偏移或绘制中的某个步骤不对。

接下来可以尝试的方向:

  • 检查反投影后的点是否直接就可以被绘制,无需再手动减 OffsetP
  • 或者确保 transform 设置与绘制逻辑完全一致,不产生重复变换;
  • 可以尝试仅减去实体的位置(非 transform),而保留其他不动;
  • 打印实际绘制坐标与鼠标位置进行对比,确认误差来源;

目前调试的方式、流程和思路已经非常接近问题核心,只需再细化变换顺序即可精准对齐。现在的结果已经逐步逼近正确状态。

game.cpp:选择一个实体进行测试

这里我们已经进入了一个新的阶段,即希望针对某一个实体进行操作或调试。由于我们手上有实体的索引 entity_index,我们完全可以从中挑选出某一个实体来处理或绘制。

不过问题在于,我们当前并不清楚到底该选择哪一个实体,也就是说我们缺少一个明确的判断逻辑来决定“选中”的应该是哪一个实体。这个问题暂时可以搁置,因为接下来我们会进一步探索和试验,很快就能搞清楚。

重点在于:现在已经具备条件,可以对某个单独实体进行调试,只需要确认要处理的是哪一个。选中实体后,就可以更准确地观察当前反投影、坐标变换和绘制逻辑是否在该实体上表现正确,从而进一步缩小调试范围,推进整体调试进度。
在这里插入图片描述

在这里插入图片描述

运行游戏,发现鼠标矩形位置和缩放不正确

现在我们运行了调试代码,并选取了一个实体进行分析,比如一个树。接着,我们尝试在该实体的局部空间中绘制鼠标的位置,目的是验证鼠标在该实体坐标系下的位置是否正确。

然而,结果显示,绘制出来的鼠标位置完全不正确,不仅位置不对,连缩放比例都显然是错误的。这说明目前的反投影或坐标变换逻辑仍然存在问题,至少在处理坐标系统之间的转换过程中,某些步骤还未正确实施。

虽然系统已经考虑了从屏幕坐标到世界坐标的转换、从像素到米的单位换算等多个环节,但实际结果仍不合理,说明这些变换之间的关系还没被完全理清。

因此,为了更有效地排查问题,我们采用了经典的调试手段:将调试范围限制到单个实体。这么做的好处在于可以屏蔽掉其他干扰因素,只聚焦在一个对象上,从而更清楚地观察实际绘制与预期之间的偏差,更快速地定位问题源头。通过简化可视信息,有助于我们理清当前坐标偏差的根本原因。

game.cpp:尝试去掉减法操作进行Unproject

现在我们已经得到了 MetersMouseP(转换为米单位的鼠标位置),并完成了反投影操作(unproject)。接下来,我们出于“学习目的”,想看看在不进行坐标修正的情况下,单纯反投影后的结果是什么样子。

具体来说,我们暂时移除了原本用于将反投影结果转换到局部空间的坐标差(subtract)操作。也就是说,我们不再把鼠标位置从世界空间转换到相对于实体的局部空间。这样可以清晰地看到:仅做反投影而不对其做局部空间转换时,坐标会落在哪个位置,以及其视觉效果如何。

这个过程的目的是为了深入理解:反投影操作本身输出的是一个怎样的空间位置,它是否已经靠近我们所需的结果,或者是否仍需进一步处理来变换到我们最终希望可视化的位置。

简而言之,这是一种通过故意“省略”某步处理来帮助理解系统行为的调试策略,有助于掌握每一阶段变换对最终渲染的影响。

在这里插入图片描述

在这里插入图片描述

运行游戏,发现结果意外地在正确的位置

在不进行坐标修正的情况下,可以看到反投影后的结果居然大致出现在了正确的位置。这一点出乎预料,因此需要进一步思考其原因。

反投影操作(unproject)本身并不会考虑实体的位置,也就是说,它只将某个屏幕坐标(如鼠标位置)转换回三维空间的某个点,而不会根据当前实体的偏移或坐标系进行调整。所以按理说,反投影之后应该还需要做一次减法(subtract),也就是把实体的偏移从中减去,才能变换到相对于该实体的局部坐标中。

但奇怪的是,在没有进行这一步减法的情况下,绘制出来的位置似乎已经是正确的。这个现象起初看起来非常奇怪,不过仔细想想也说得通:绘图系统在渲染时是以实体为中心进行绘制的,即绘制的所有东西默认都相对于当前实体的变换进行转换。因此,即使没有手动减去偏移,绘图的结果也会自动加上了实体的变换,最终仍然落在了相对合适的位置上。

所以这种情况下,反而不需要手动执行那一步坐标减法,否则就会多减了一次偏移,导致位置错误。

在进一步观察中,发现图形围绕屏幕中心旋转时,并没有正确地向鼠标指针移动。这说明当前虽然坐标位置看起来是合理的,但在动态变化上并未完全表现出期望的行为,因此仍存在逻辑问题。这就指出了接下来的目标:我们希望看到的是,鼠标点相对于实体在空间中准确地移动和对齐,这才意味着坐标转换真正是正确的。

总结:

  • 反投影不会考虑实体偏移;
  • 渲染系统自动将坐标转换为实体相对位置;
  • 手动减去偏移可能导致坐标错误;
  • 当前看似正确的结果实际在动态表现上仍有偏差;
  • 接下来应进一步验证动态交互下的坐标行为是否正确匹配。

game.cpp:考虑Unproject函数可能存在根本问题

在执行反投影(unproject)操作时,使用了当前的 localZ 值来从屏幕坐标转换回世界坐标。然而在观察整个过程后,发现反投影过程似乎存在根本性的问题

仔细检查 unproject 的实现逻辑,会发现它从未考虑过偏移量 OffsetP。也就是说,当前的反投影实现完全忽略了实体本身的位置偏移,就好像这个偏移量根本不存在一样。这意味着反投影的结果仅仅是基于摄像机视角下的原始计算,未包含任何对象自身的变换信息。

这种处理方式会导致一个关键问题:反投影得到的位置不具备任何“局部性”,无法准确反映鼠标在某个特定实体或局部空间中的位置。理论上,若要实现精确的坐标转换,反投影逻辑就应该结合当前的实体变换信息(例如偏移、缩放等),否则投影和反投影之间就不对称,最终导致渲染和交互行为偏离预期。

总结:

  • 当前的 unproject 实现未考虑偏移量 OffsetP
  • 导致反投影结果总是相对于摄像机中心,而不是具体实体;
  • 这让后续逻辑如坐标对齐、局部定位等功能失效;
  • 如果要修复,应该在反投影过程中加入实体偏移信息,使其结果真正反映世界空间或实体局部空间下的位置;
  • 当前表现表明 unproject 逻辑在设计上是不完整甚至有缺陷的,需要系统性地修正。

game.cpp:将OffsetP.z添加到LocalZ

确实,经过对坐标转换流程的深入分析后,发现当前逻辑存在设计上的不足。

在进行反投影操作时,LocalZ 理应包含完整的空间变换信息,也就是说,它应该包括整个 transform(变换矩阵或结构)所代表的全部位置、偏移、旋转等内容。然而当前的做法只是使用了一个 LocalZ 值,完全忽略了 transform 中其他的关键部分,导致反投影的结果并不准确。

换句话说,真正准确的 LocalZ 应该是带有完整 transform 上下文的世界空间深度信息,否则反投影得到的位置将失真,不再反映实际鼠标在实体局部空间中的位置。

因此也得出一个重要结论:目前的 projectunproject 两个函数实现方式存在问题,它们都没有真正处理 transform 中的重要数据,尤其是在涉及多个实体、偏移和局部空间时,这种简化处理方式会直接造成坐标错位、行为不符等问题。

最终总结如下:

  • 当前的 LocalZ 不完整,应包含整个 transform 的影响;
  • 反投影计算过程中没有考虑 transform 的实际变换内容;
  • projectunproject 两个函数在设计上缺乏对 transform 的支持,导致整个坐标转换链条不闭环;
  • 为了保证空间逻辑的对称性与准确性,应当对这两个函数进行重构,使其自动处理 transform
  • 否则所有以鼠标或世界坐标为基础的判断与绘制都可能存在系统性偏差。

这个问题虽然看似细节,但从底层影响了所有依赖空间转换的系统逻辑,是一个必须要修正的核心问题。
在这里插入图片描述

在这里插入图片描述

运行游戏,发现这更接近正确

经过分析,当前的处理流程已经朝着正确的方向发展,但仍然存在一些细节问题。

首先,LocalZ 确实需要包含完整的变换信息,包括目标实体的所有偏移和缩放等。对于投影和反投影的操作,应该考虑到整个 transform 的影响,而不仅仅是 LocalZ 本身的值。这样,计算出的空间位置才能更加准确,避免了之前提到的变换信息缺失的问题。

然而,尽管变换部分已经得到改进,当前的主要问题仍然出现在坐标缩放部分。特别是在与鼠标位置相关的坐标计算时,坐标的缩放依然不正确。虽然我们进行了正确的变换,但最终鼠标位置在屏幕上的显示没有随着鼠标的移动而正确调整。

具体问题在于:当前的 unprojectproject 过程中,虽然涉及到局部坐标和变换矩阵,但**LocalZ 在投影时没有得到适当的处理**,导致反投影后的坐标没有按照预期进行缩放。

此外,在将“米制坐标”转换为“像素坐标”时,可能没有正确执行反向转换。具体来说,当前的 MetersToPixels 变换可能在某些情况下没有正确应用,导致鼠标位置无法与屏幕坐标正确对齐。

为了确保最终的坐标转换无误,应该对以下几个方面进行进一步检查和修正:

  1. 确保每次反投影时都使用完整的变换信息,包括目标实体的所有相关变换。
  2. 在将“米制”坐标转换为“像素坐标”时,确保所有的尺度因子都被正确应用,特别是在涉及鼠标坐标时。
  3. 验证 unprojectproject 是否在计算过程中正确地应用了与屏幕坐标相关的所有因素,包括坐标缩放。

总的来说,虽然已经取得了一定进展,但需要对坐标缩放的部分进行更细致的调整,确保所有的计算在空间转换时能够准确地反映屏幕坐标系统中的变化。

在这里插入图片描述

game_render_group.cpp:研究我们的常规处理函数

经过一番推理与计算,目标是通过正确的数学操作来获取鼠标位置的相对坐标。

首先,我们尝试通过将米制坐标转换为像素坐标,来得到鼠标位置在屏幕上的投影坐标。具体来说,我们将 米制坐标 乘以 像素转换因子,再乘以 投影后的 x、y 坐标 来计算鼠标的位置。这是我们希望达到的目标——得到一个正确的、以像素为单位的鼠标坐标。

然而,出现了一个问题:即使做了这些计算,投影的结果看起来并不完全正确。特别是,投影的坐标似乎没有按照预期正确缩放,导致鼠标在屏幕上的位置与预期不一致。

之后,分析表明,可能是 米制坐标到像素坐标的转换 过程中存在一些问题。通过对比计算过程,可以发现我们遗漏了对 米制到像素的反转转换。虽然在操作过程中,我们已经进行了像素到米制坐标的转换,但由于未正确处理反向转换,导致结果无法准确反映鼠标的实际位置。

在检查了所有操作后,决定通过对投影坐标进行逆运算来进一步解决问题。换句话说,我们尝试通过反向操作,恢复原始的坐标,以便在正确的坐标系下进行显示。为此,我们将 投影后的坐标 除以一个变换因子,再通过焦距调整这些坐标,理论上这应该能够让结果正确。

尽管反向操作的数学公式看起来合理,但最终的结果依然没有完全符合预期。分析后发现,尽管投影坐标计算的顺序和方法是正确的,但在处理 焦距米制到像素的转换 时,似乎还有一些遗漏。特别是 焦距 的处理方式,虽然我们将其应用到了计算中,但似乎没有正确地融入到最终的转换过程中。

总结来看,问题的根本在于如何精确地控制和调整 米制坐标与像素坐标之间的转换关系,以及如何确保 反向投影操作 中所有因素都能正确地影响最终结果。
在这里插入图片描述

game.cpp:尝试将减法操作重新添加回Unproject

目前问题还没有完全解决,因为即使进行了多次计算和调整,预期的效果依然没有实现。虽然表面上看似问题应该已经解决,但实际情况却并非如此,这让人感到困惑。希望能够继续调试,仔细分析每一步,找出真正的原因,但由于问题太过复杂,已经进入了反复调试的循环。

从目前的调试情况来看,问题可能出在 逆向转换 的某些环节,尤其是 米制坐标到像素坐标 的转换可能没有完全按预期工作。这类问题尤其难以解决,因为它涉及到 投影坐标变换,而且这些操作不是那么直观,通常很难通过视觉或简单的调试工具捕捉到错误。

目前的方案是通过逐步检查代码中的每个细节,确保每一步的数学操作都准确无误。可以看出,在进行一些具体的变换和投影时,局部 Z 坐标焦距 等因素可能还没有得到正确处理。虽然调整了坐标转换和投影操作,但 缩放 方面的处理仍然存在疑问。也有可能是在计算时出现了缩放的偏差,但目前还无法确定具体原因。

总的来说,虽然大部分步骤似乎是合理的,但依然无法达到预期效果。问题可能出在某些细节上,例如 相机的距离(是否正负),或者 局部坐标缩放转换 之间的相互关系。需要进一步的调试和实验来找出具体问题。
在这里插入图片描述

在这里插入图片描述

运行游戏,尝试确定差距

目前的问题是 缩放 错误,首先是没有围绕正确的中心进行缩放,这导致了预期效果与实际效果之间的偏差。虽然一开始没有发现是缩放问题,但现在意识到 缩放的错误 比较有趣,需要进一步的调整。首先,问题可能出现在 缩放计算坐标中心 上。中心位置没有正确对齐,而 缩放 本应根据目标的 位置和距离 来调整。

为了进行调试,打算尝试一些比较激进的方案,进行不同的实验来进一步检查缩放和位置的关系,尝试通过不同的操作来找出 问题的根源

game.cpp:尝试硬编码一些值

目前的想法是,如果之前我们总是围绕地面和某个点进行偏移,那么接下来可以尝试改变这种方式,看看会发生什么。如果把相对变换的偏移量设置为零,那么所有的绘制操作就会相对于普通的世界坐标进行。这意味着不再考虑任何相对位置的偏移,而是直接将鼠标的位置相对于世界坐标进行处理。

接下来,可以尝试用 10米 这个值来表示鼠标和物体的距离。假设鼠标距离物体 10米,就可以绘制出物体在该位置的世界坐标。这时,通过使用 相同的局部 z 值 进行投影,期望看到物体正确地显示在鼠标光标的正上方。

这个过程的核心是去掉变换的偏移量,使得绘制操作直接发生在标准世界坐标系中,利用投影将其放到正确的位置,期望通过这种方式验证缩放和位置是否正确。
在这里插入图片描述

运行游戏,结果尚可,但仍不完美

目前的情况是,虽然画面显示出来了,但效果并不理想。物体的位置接近鼠标的位置,但并没有完全跟踪鼠标,说明存在一些问题。这个问题可能出现在坐标转换或投影的计算上,导致物体并未准确地与鼠标位置同步。
在这里插入图片描述

game.cpp:将LocalZ设置为1.0f

问题出在坐标转换的过程中,当使用LocalZ时,应该是尺度不变的,无论我们传入什么z值,unprotectedproject都应该返回相同的位置。然而,实际情况并没有这样发生,显示的位置并没有按预期的方式改变。这意味着有部分计算或者数据处理是错误的。

在分析过程中发现,主要的问题在于我们没有正确考虑pz值,即物体距离目标的距离。在unprotected过程中,未正确处理这个z值,而是直接用了distance from camera,这导致了错误的结果。实际上,z值应当是物体距离目标的实际值,并且在计算时,应该从DistanceAboveTarget减去dz来获得正确的z值。

game.cpp:将RenderGroup->Transform.DistanceAboveTarget添加到PushRect

问题出在unprotected过程中,未正确处理z值的逆向变换。当前,LocalZ值并没有正确地反映出物体在空间中的位置。解决办法是,在unprotected步骤后,需要将返回的值与LocalZ进行处理,使得LocalZ值能够正确反映物体的位置。

具体来说,需要进行如下处理:我们首先获取Transform.DistanceAboveTarget,然后将这个值从LocalZ中减去。通过这种方式,最终的结果应该与LocalZ一致,这样才能确保坐标的转换和缩放是正确的。

在处理过程中,需要确保两者的变换是对称的,也就是说,我们需要在进行坐标变换时,确保最终的计算能够回到正确的LocalZ值,而不是错误的计算结果。

这意味着我们需要通过正确的方程来进行计算,使得最终的结果符合预期,并且能够精确还原到物体的原始位置。虽然当前实现还没有完全正确,但这是导致问题的一个重要部分。
在这里插入图片描述

在这里插入图片描述

焦距会如何影响?会有FOV的设置吗?

焦距(focal length)在投影和反投影过程中起到了至关重要的作用。焦距决定了视场的缩放程度,也就是说,它影响了如何将三维空间中的物体映射到二维屏幕上,尤其是物体与相机的距离对其在屏幕上的表现有多大影响。焦距影响的是物体在z轴上的压缩程度,即物体越远,投影会变得越小,而焦距越长,这种压缩效应越明显。

在这个过程里,焦距帮助决定了物体在屏幕上的位置和大小。例如,在进行投影时,物体的Z值(与相机的距离)将会被焦距所影响,焦距越长,物体越远离相机时,它的投影就越小。这个比例关系也影响着反向投影的计算,因此需要在将物体的坐标从屏幕空间转换回世界空间时,考虑到焦距的影响。

尽管焦距的作用相对简单,但在调试时,问题可能出在处理细节上。焦距的处理可能没有完全按照预期工作,导致投影和反投影操作的结果不如预期。因此,调试过程中,需要确保焦距的影响在每一步都被正确地纳入计算,特别是在物体与相机之间的距离变化时,焦距的影响应该被适当地反映出来。

半相关问题:你怎么看待使用不同类型的点和向量?

关于使用不同类型的点(Point)和向量(Vector),认为这种区分并不是特别有用,反而有时会带来不必要的麻烦。点和向量的区别并不在于存储方式,而在于它们的使用方式和概念化。在很多情况下,可能需要根据不同的情境,将同样的事物概念化为两种不同的东西,而这时候,如果类型被严格区分,就会需要进行类型转换或强制转换,导致额外的工作量和麻烦。

从使用的角度来看,将点和向量视为不同的类型可能会限制灵活性,因为在实际开发过程中,可能需要在某些情况下把一个点当作向量使用,或者反过来。强行区分它们的类型不仅增加了代码的复杂度,也可能导致一些不必要的转换,增加了出错的机会。因此,认为在很多情况下,这种区分并没有太大必要,反而可能让事情变得更加繁琐。

问题的一部分是不是因为代码没有规范化鼠标和实体之间的运动关系,尤其是在不同层次的世界坐标下?

问题的核心在于代码没有正常化鼠标运动与屏幕尺寸之间的关系,尤其是在世界坐标系中的不同层级。这个变换和未保护的部分实际上是完全线性的,仅仅是多个乘法操作的组合。因此,理论上,当将鼠标从像素转换为米时,不论是在变换之前还是之后进行转换,最终的结果应该是相同的,不应该有任何变化。

game.cpp:尝试在事后进行MetersToPixels处理

在调试过程中,验证了当前的变换方法,无论是先进行变换再转换为像素,还是先转换为像素后再进行变换,最终结果是相同的,说明这种方法仍然有效。然而,尽管方法本身没有问题,仍然存在问题,主要体现在如何处理变换的细节上。还没有仔细检查是否涉及到“PushRect”部分,这可能是问题的关键所在。

game_render_group.cpp:检查PushRect是否没有其他变换操作

在渲染过程中,使用了渲染基准点 p,并且直接应用了它。在绘制时,使用了 p 作为坐标,进行矩形的绘制操作。可以确认,p 被直接使用,没有发生任何其他的变换。这表明当前渲染过程中的坐标没有经过额外的变换处理。

Unproject出现了什么问题?

遇到的问题是关于这个项目的具体原因,自己并不清楚具体的原因是什么。这是一个很好的问题,自己也很想知道问题出在哪里,但目前还没有找到确切的答案。

game_render_group.cpp:调查问题

问题出在计算过程中,特别是在涉及到目标距离和 z 值时,最后的结果应该是基于本地 z 值的,并且涉及到焦距的运算。现在遇到的问题是无法准确理解和解决这个问题,导致情况变得复杂且没有明确的答案。因此,计划明天继续排查问题,逐步分析代码,确保每个步骤都被仔细检查。这是一个没有简单解决方法的复杂问题,可能需要更细致的调试和逐步验证。

问题:讨论开发工具的潜在功能,这些功能有助于解决此类问题

这个问题的核心在于某些程序错误往往浪费大量时间,特别是像这样的 bug。通常在编程时,我并不会遇到很多因为忘记释放内存之类的错误。相反,常常是一些计算逻辑上的 bug,尤其是在游戏编程中,涉及到转换和计算时,难以找出问题的根源。很多时候,尽管工具和语言提供了各种优化,比如垃圾回收,虽然可能偶尔会节省时间,但这种节省并不是最重要的,因为很多开发者都会解决内存管理的问题,像链表这种结构自己写就好。

然而,像当前的问题,涉及到复杂的数学计算和向量转换,这类问题才是影响编程效率的根源。理想的开发工具应该能帮助可视化这些计算步骤,帮助开发者清晰地理解数据在每个步骤中的变化,而不是只看着一堆数字。这种开发工具可以让我们立刻看到问题出在哪里,从而减少查找 bug 的时间。而现在的问题是,虽然开发工具在优化某些简单任务方面做得很好,但像这种复杂的数学推导和逻辑错误往往没有好的工具来帮助解决。因此,在这种情况下,开发者只能依靠手动调试,逐步分析问题所在,这让调试过程变得异常困难。

总的来说,理想的工具应该专注于帮助解决那些隐藏得更深、难以调试的复杂问题,而不是专注于那些简单且很容易解决的问题。

调试器:检查一些值并将其复制到临时缓冲区

首先,我们来看一下我们的原始坐标p,并进行处理。首先,原始的鼠标坐标被转换成了更小的值,单位是米。接下来,我们知道局部坐标系(local xie)的固定值是1,我们在进行投影计算时,使用的是焦距(focal length)进行的计算,结果得到了一个投影的xy坐标。

然后,我们将这个计算结果传递到下一个步骤。在进行这一系列操作时,我们在调试过程中发现,当我们调用push wrecked函数时,会进行一个偏移量的计算,这个偏移量是基于xy值的,这个计算与绘制矩形有关。为了简化计算,原本应该在绘制矩形时,将维度设置为零,从而使得计算变得更简单。在这种情况下,我们可以直接修改这些值,确保我们传入的p值保持不变。

通过这种方式,最终输入值保持一致,并且我们不再绘制矩形,而仅仅进行计算。我们接下来传递的是目标与当前z坐标之间的距离。通过查看变换(transform)的情况,我们知道目标距离为8,z坐标是1。按照公式计算,目标与z坐标的差值应该为8,符合预期。

最后,我们进行一步调试,查看是否得到了正确的变换。经过验证,最终的结果与预期一致,坐标并没有发生变化。

整体而言,以上步骤的操作确保了在进行坐标变换时,相关的数值都能按照预期正确计算和转换。
在这里插入图片描述

在这里插入图片描述

我们把相对于基础P的Z轴运动去掉了

在这个过程中,我们忘记了一些关键点。首先,我们没有进行正交变换(orthographic transform),而是使用了距离目标的9个单位。在计算过程中,我们将pz设置为了某个值,然而在这个过程中,实际上我们曾经移除了相对于基础b的z轴移动。

记得之前做了一个决定,即我们不再考虑z轴的移动,将其移除了。这实际上意味着,这整个过程并没有像我们之前以为的那样存在bug,可能从一开始就没有问题。
在这里插入图片描述

在这里插入图片描述

game.cpp:正确设置Transform.OffsetP

所以基本上,我们真正需要做的就是忽略一些不必要的部分,重新审视整个流程。通过去除不需要的z轴移动,实际情况变得清晰了许多。原本以为的问题,其实并不存在,只是我们忽略了一些细节,导致了误解。

运行游戏,发现位置正确

问题最终出现在一个没预料到的地方。原本以为问题复杂,结果发现只是因为在设计时忽略了一些细节。最初的设想是我们并不想使用z轴的偏移量,然而后来发现我们并没有明确注意到这一点,导致了bug的产生。虽然我们原本的设计是正确的,但由于忘记了实现的方式,导致出现了问题。

这个bug本质上是因为我们在处理时没有考虑到一些已经实现的特性,而这些特性本应被考虑到。最终,经过反复检查和回顾,才发现问题出在了这部分。可以说,这是一个完全可以避免的问题,如果当时更仔细地查看代码,可能就能很早发现。

这整个过程其实也说明了一个点:即便是看似简单的设计和代码实现,也容易被细节忽视。


网站公告

今日签到

点亮在社区的每一天
去签到