游戏引擎学习第188天
回顾并计划今天的内容
原本这周的目标是进行可视化操作的尝试,但每一天都被一些棘手的bug和问题所阻碍,导致我们一直没能实现这个目标。直到今天,星期四,我们终于解决了这些问题,所有功能都能正常运行了,所以今天终于有机会进行这个可视化操作了。对于这个任务,我一直期待很久,终于可以开始着手做这件事了。
接下来,我们打开之前的调试工具,回顾一下目前为止的进展。通过这些调试输出,我们可以看到一些细节问题,接下来会重点关注这些内容。
游戏运行缓慢,因为每一帧都在收集过去64帧的所有调试事件
有一件事是显而易见的,就是随着游戏的进行,性能变得越来越慢。这是有原因的,因为我们将事件不断地积累到一个缓冲区,直到收集到64帧的调试事件。思考一下这意味着什么,可以看出,这表明我们不应该将所有事件都一起处理。我们可能应该在每次遇到帧边界时,就将该帧的调试信息存储起来,并以已处理的形式存储。这样我们就可以避免当前的麻烦。
因此,我觉得在这里我们需要进行一些组织工作,来优化处理方式。
将我们处理的调试帧数量从64减少到8
为了优化调试,我决定将调试帧数设置为较低的数字,这样我们可以先进行可视化的工作,因为我一直想做这个,但因为有一些bug被耽搁了。我要先处理可视化问题,然后再回过头来优化性能,避免占用太多时间。为了测试效果,我将最大事件数组的大小从34减少到8,这样处理的帧数就大大减少了。
测试结果表明,帧率大幅提高,性能显著改善。这样处理的效果很好,基本上能够确认这是一个合适的方案,之后如果我们将处理的帧数减少到1,性能会进一步提升为现在的1/8时间。
接下来,我想先花些时间把系统改造成一个对我们可视化有用的工具。目前我们能看到的一些信息还很粗糙,比如我们可以看到线程执行的渲染任务以及它们所消耗的时间,可以看到调试信息的处理情况以及等待帧的时间等,尽管这些信息已经能提供一定的帮助,但目前的可视化效果还不够清晰,界面看起来很杂乱,且信息展示不够直观。
之前的一个Bug
我们想从调试显示中提取更多信息
当前的可视化界面存在一些问题,我们虽然已经收集了大量的调试信息,但无法有效地查看这些数据。具体来说,当鼠标悬停在某个条形图上时,无法知道该条形图对应的实际数据,而且也无法放大或查看某个特定条形图的详细信息,比如显示该条形图内部包含的子进程或子函数,以及它们消耗了多少时间。这些信息虽然已经记录下来了,但我们没有办法与之交互,导致目前的调试界面看起来非常不直观,无法有效提取有用的数据。
为了解决这个问题,首先需要建立一个基本的用户界面,使得我们可以与这些调试信息进行交互。具体来说,可以通过点击某个条形图来查看该条形图的详细内容,逐步打开和查看数据。我想从实现一个最简单的功能开始,比如让用户能够点击条形图并查看相关的信息。
目前,我们已经实现了鼠标的位置获取功能,可以获取鼠标的 X 和 Y 坐标以及鼠标按钮的状态。这些功能已经能够免费使用,并且可以帮助我们实现交互功能。所以接下来我计划实现一些简单的用户界面功能,使得我们可以浏览和操作这些调试信息。
我们希望当鼠标悬停在任何条形图上时,调试显示能展示与之相关的信息
为了实现这个功能,最简单的实现方式就是通过鼠标悬停在某个条形图上,能够显示该条形图对应的函数名称。具体操作是,当鼠标悬停在某个条形图时,系统会打印出当前悬停的条形图对应的函数名称。这个功能是实现调试信息交互的第一步,能够让我们简单地查看某个函数的基本信息,作为后续更复杂交互的基础。
将游戏输入传递给调试系统,以便它能够访问鼠标位置
为了实现这个功能,首先需要将鼠标信息传递给调试系统,因为目前系统并没有直接的方式来传递这些信息。调试系统目前没有接收到游戏输入数据,而游戏输入数据在游戏更新和渲染过程中是可用的。我们需要做的就是将游戏输入直接传递到调试系统中,确保调试系统能够访问鼠标的位置信息。
在具体实现中,首先会通过修改主程序,将游戏输入传递给调试系统。在调试系统中,就可以直接读取鼠标的 x 和 y 坐标,从而获取鼠标的位置信息。这个过程相对简单,未来可能需要根据游戏输入缓冲区的处理进一步改进,但目前的实现应该足够用于调试目的。
检查鼠标位置是否落在给定的矩形内
现在的目标是根据鼠标的位置来判断它是否悬停在某个矩形区域内,并显示该区域的相关调试信息。每个矩形代表一个具体的调试区域,矩形坐标已经在渲染过程中确定。通过对比鼠标位置和矩形的位置,判断鼠标是否位于矩形内。这个操作非常简单,可以通过一个基本的比较来实现。
在实现过程中,首先会检查鼠标是否在某个矩形区域内。通过判断鼠标的 x 和 y 坐标是否落在矩形的范围内,若条件满足,就输出一行调试文本,显示该矩形对应的调试信息。这些信息包括了该区域的一些具体数据,例如周期计数、点击计数等。
为了从调试区域中提取这些信息,必须先确认如何获取每个区域对应的调试记录。在调试数据结构中,有存储调试记录的信息,包含了区域的基本信息及与该区域相关的事件。每个区域都有其对应的调试记录,记录了诸如文件名、行号等信息,正是这些数据将被输出以帮助调试。因此,重要的是确保能够正确地关联调试区域和相关的记录信息,方便后续的调试工作。
具体来说,可以通过在调试区域中查找关联的调试记录来获取相应的数据,这样就能够为每个区域显示详细的调试信息。
要显示的信息将来自于与debug_frame_region关联的debug_record
接下来,目标是将每个调试区域与其相应的调试记录关联起来,以便在鼠标悬停在某个区域时,能够获取到该区域的详细信息。具体来说,想要在每个调试区域中存储一个调试记录,通过这个记录可以访问该区域的相关信息。
在鼠标悬停时,可以通过该调试记录获取到该区域的块名、文件名和行号等信息。为了提供更有用的调试信息,可以将调试信息格式化为块名、文件名和行号等,这样开发者就可以直接看到这些信息,知道该区域的具体位置和它所对应的源代码。
此外,还需要获取与该区域相关的计时信息,特别是周期计数(cycle count)。虽然对于某些区域来说,点击计数等其他信息可能不那么重要,但周期计数对于分析性能非常关键。通过记录和显示周期计数,可以帮助分析特定区域的执行时间。
总的来说,目标是通过获取每个调试区域的相关信息(例如块名、文件名、行号和周期计数)来提升调试信息的可视化,使得开发者在查看调试信息时,能够更方便地定位问题并进行优化。
在debug_frame_region中添加一个调试记录和周期计数
接下来,需要对调试信息的存储结构进行升级,以便能够包含更多的信息,具体来说,就是需要添加调试记录和周期计数。
在每次记录调试帧和区域时,都要确保每个区域都包含这些新信息。对于周期计数,这个信息相对简单,因为它可以通过已知的事件开始和结束时间来计算得到,因此不需要太多复杂的处理。
同时,调试记录也需要被正确地关联到每个区域。调试记录的源信息(如文件、行号等)已经在系统中存在,因而可以直接提取并使用。这样每个区域就能存储周期计数和调试记录的详细信息。
一旦完成了这些改动,就能确保每个区域的调试信息包含完整的上下文,使得开发者可以更方便地查看和分析这些数据。
总的来说,整个过程是对现有存储结构的扩展,目的是为了提供更加详细和有用的调试信息,尤其是在鼠标悬停时能够获取到区域的完整信息。
构建一个矩形来测试鼠标指针是否在其中
为了优化矩形的处理和鼠标交互,首先回顾了已经存在的矩形功能。接着决定利用现有的矩形功能进行扩展,创建一个矩形对象,并通过给定的最小值和最大值来定义矩形的范围。这些值可以直接用于计算矩形的边界,进而在游戏或调试中应用。
具体来说,创建矩形时,主要关注了最小值和最大值之间的范围,尤其是矩形的宽度和起始点(x坐标)。矩形的定义考虑了如何传递矩形的最小值、最大值以及其他相关参数,确保矩形能够在后续的代码中正常使用。
此外,还提到了一些改进计划,比如使得矩形可以方便地传递给其他模块,进一步优化代码的可读性和可操作性。在这部分代码中,矩形不仅仅是一个简单的数据结构,还需要与其他组件(如鼠标输入)交互。因此,矩形的边界、位置以及其他相关信息会随着鼠标的移动而动态变化。
实现这一目标的方法是通过鼠标的坐标(鼠标位置)与矩形的位置进行比较。具体做法是,判断鼠标当前的位置是否位于矩形范围内,这样可以通过矩形的最小值和最大值来确认鼠标是否在矩形区域内。如果鼠标在矩形内,那么就可以通过调试信息输出相关数据,如矩形的属性、鼠标的位置等。
总结来说,目标是将矩形的定义与鼠标位置的交互结合,使得能够方便地通过鼠标事件来访问和操作矩形区域。这一过程涉及到对矩形位置、鼠标位置的实时判断,并且能够在调试过程中输出相应的数据。
实现一个接受矩形作为参数的PushRect版本
首先,讨论了如何在渲染过程中推送矩形,这次是使用与之前不同的方法。新的方法涉及传递一个带有Z坐标的矩形,这意味着矩形不仅包含常规的最小值和最大值,还增加了一个新的维度(Z坐标)。目标是使得矩形的推送方式变得更加灵活,能够处理更复杂的空间结构。
为了解决这个问题,提出了一种新的推送矩形的方法。首先创建一个新的矩形对象,并将其与Z坐标结合起来。这是通过对原始的矩形推送方式进行改进来实现的。新的方法允许矩形在渲染过程中根据需要进行更精确的定位,尤其是在三维空间中。
接下来,考虑到优化的问题,决定使用现有的工具来简化实现。例如,调用“get center”方法来获取矩形的中心位置,确保矩形的中心点和Z坐标都能正确地在渲染中体现。矩形的维度(宽度和高度)也通过已有的结构来获取,并将其作为渲染参数传递。最后,将颜色信息直接传递给渲染函数,确保矩形的视觉效果与期望一致。
总结来说,这段工作主要集中在通过增加Z坐标来改进矩形的渲染方式,并使用现有的工具和方法来简化这一过程。最终的目标是确保矩形在渲染时能够正确地显示,且具备更多的灵活性和扩展性。
测试。运行正常,但悬停效果还没有工作
现在的任务是确保鼠标悬停在矩形上时,能够正确地显示出相应的信息。虽然已经成功实现了绘制矩形的功能,但鼠标悬停的效果还未实现。当鼠标移动时,当前并没有看到任何反馈,这意味着还需要进一步的调整。
首先,需要解决为什么当前鼠标悬停没有效果的问题。这是因为在现有的代码中,鼠标悬停的处理逻辑尚未完全实现,因此无法正确捕捉鼠标在矩形区域内的状态。为了让鼠标悬停功能生效,需要继续完善代码。
下一步的工作将是完成鼠标悬停的处理,确保当鼠标经过矩形时,能够正确响应并显示相应的调试信息。虽然已经完成了矩形的绘制,但还需要做更多的工作来确保鼠标悬停能够触发正确的视觉反馈。
鼠标位置是相对于窗口的坐标系,而不是我们的渲染空间
目前面临的主要问题是,鼠标坐标(mouse x, mouse y)和渲染空间的坐标并不一致。鼠标坐标是由操作系统(如Windows)提供的,而渲染系统使用的是不同的坐标空间。具体来说,操作系统的坐标系统与渲染系统使用的正交投影坐标系并不匹配。
在调试过程中,当前的渲染使用了正交投影,将渲染空间的坐标范围设置为从负宽度的一半到正宽度的一半、从负高度的一半到正高度的一半,而这些坐标与Windows提供的鼠标坐标系不一致。因此,鼠标坐标需要进行转换才能正确地映射到渲染坐标空间中。
解决方案是先在当前系统中直接实现这一转换功能,使得鼠标坐标可以与渲染空间的坐标对接。之后,再根据需求考虑是否将这个坐标转换的责任推给平台层。这样做的好处是,无论不同平台如何处理鼠标坐标,平台层都可以统一将其转换为渲染系统需要的坐标格式,保证跨平台的一致性。
将鼠标坐标转换为游戏空间坐标
我们需要做的事情是,首先需要理解Windows中鼠标坐标的范围通常是从0到窗口的宽度。因此,鼠标的X坐标是从0到窗口的宽度,而渲染系统的坐标系可能与此不同。为了将Windows的鼠标坐标映射到渲染坐标系,我们需要做一些转换操作。具体来说,我们要做的是:将鼠标X坐标减去窗口宽度的一半,这样就能将鼠标坐标从Windows的坐标系转换到渲染坐标系中。
目前,我们还没有直接获取窗口宽度的方式,因此需要在合适的地方存储窗口的宽度信息。为了更好地组织代码,也有必要将一些全局变量移出当前的结构,减少对全局状态的依赖。
此外,对于高度的转换也需要进行类似的处理。在Windows中,鼠标的Y坐标是从顶部开始计数的,而渲染系统中Y坐标是从底部开始计数的。因此,Y坐标需要做相反的转换,通常是将鼠标的Y坐标从半高度开始向下计算,随着鼠标位置的增加,Y坐标也增大。
这种坐标转换目前还不完全正确,但已经接近我们需要的效果。接下来,需要进一步检查并调整这些转换,确保鼠标坐标能准确地映射到渲染系统的坐标系中。
现在悬停功能有效
现在,我们的鼠标悬停功能已经正常工作了。当鼠标悬停在任何一个区域时,能够看到相应的信息。至于为什么信息会出现在下方,我并不完全清楚,但这可能是因为输出的文本会在下一次刷新时显示出来。所以在调试过程中,信息似乎总是显示在下一个位置。
整体来说,悬停功能已经完成,可以正确地显示我们需要的调试信息,只是关于文本显示的位置仍然有一些不明确的地方。
改变调试文本在屏幕上的位置
现在,基本功能已经实现,能够通过鼠标悬停查看调试信息。信息的显示位置和内容已经符合预期。尽管如此,仍然存在一些小问题,特别是在显示调试数据时,比如调试协同条可能非常大,显示的开销也很高,这是需要进一步优化的部分。
此外,还需要做一些调整,尤其是在屏幕的高度计算上,目前的设置是从屏幕底部开始计算,这在某些情况下可能导致显示问题。虽然大致上功能已经实现,可以悬停查看每个区域的详细信息,但还需要进行一些修复和优化,才能确保一切正常工作。
基本来说,虽然当前已经能够看到所需的调试信息,并能够进行一些交互,仍然有细节上的问题需要解决,尤其是在处理调试信息的显示和位置方面。
我们的帧时间显示为0毫秒,因为我们是使用未关闭的帧信息来计算的
目前的问题是最后一帧的时间显示为0.00毫秒,这显得非常奇怪。经过排查,发现这是因为最后一帧还没有完成,所以它的时间被错误地计算为0。实际上,这一帧在调试过程中尚未结束,原因是调试代码协同执行时发生在帧标记完成之前,因此应排除该帧,不应将其计算在内。
为了解决这个问题,需要对帧的计数进行更谨慎的处理。具体来说,应该在确认帧已经完全完成后,才增加帧计数。也就是说,只有当帧真正结束时,才应递增帧数,未完成的帧不应该计入帧统计。
为此,在更新帧计数时,应该增加一个检查,只在帧完成后才递增计数。这样可以防止未完成的帧被误算为有效帧。
经过调整后,悬停功能开始正常工作,用户现在能够正确地查看到悬停的区域和相关的调试信息。这样一来,界面交互得到了更好的改善,功能运行也更加稳定。
我们不希望游戏层依赖于Windows的鼠标位置表示。应该由平台层将其转换为游戏的坐标系统
问题的核心是跨平台的鼠标坐标处理,特别是在不同平台上鼠标坐标的表示方式可能不同。例如,Windows系统的鼠标坐标是基于窗口的原点,而我们的渲染坐标系是以屏幕中心为原点的,因此,直接使用平台传递过来的鼠标坐标可能不适用。
为了避免各个平台层都模仿Windows的坐标处理方式,并且考虑到后续会将这些鼠标坐标转换到自定义的坐标系统中,最合理的做法是将坐标转换的任务交给平台层来完成。这样,平台层负责将鼠标坐标从其本地坐标系转换到我们的标准坐标系,避免了每个平台都去模拟其他平台的坐标处理方式。
因此,决定将这一转换的责任推到平台层中,这样在游戏逻辑层可以直接获取到转换后的鼠标坐标,而不需要在每个地方都进行转换。
具体来说,在游戏代码中,鼠标坐标(mouse x
和 mouse y
)应由平台层提供,并且平台层会在这里进行必要的坐标变换。为了避免数据类型的转换问题,平台层会确保传递过来的坐标为合适的格式(如整数而非浮点数)。
这种做法不仅使得代码结构更清晰,还减少了跨平台实现的复杂度,因为每个平台只需要处理它自己的坐标转换,而不需要模拟其他平台的坐标系。
在win32_game.cpp中转换鼠标位置
这个过程的核心是将鼠标坐标转换为游戏系统的坐标系。原始鼠标坐标通常是从窗口的左上角开始的,而游戏的坐标系统是基于屏幕中心的。因此,需要对鼠标坐标进行变换,以便它们能正确地映射到游戏的坐标系统中。
步骤如下:
-
获取全局缓冲区的宽度和高度:通过访问全局的
back buffer
(即渲染缓冲区),我们可以获取当前屏幕的宽度和高度。 -
坐标系统的转换:
-
X坐标变换:首先,我们需要将坐标系的原点设定在屏幕的中心。为了实现这一点,可以将屏幕宽度的一半(
width / 2
)从鼠标的X坐标中减去。这使得原本从左上角起始的坐标系统,现在从屏幕中心开始计算。 -
Y坐标变换:Y坐标的变换稍有不同,因为窗口坐标系通常是从上到下递增的,而我们的坐标系是从中间向上和向下扩展的。为了使得鼠标坐标和屏幕坐标一致,首先需要设置原点为屏幕的中心(负半屏高度)。然后,随着鼠标向下移动,Y坐标的数值应该减小。
-
-
坐标的应用:完成坐标变换后,将新的坐标应用到游戏渲染系统中,以确保鼠标在游戏中的位置正确显示。
最终,这样的处理方式可以确保鼠标的位置能与游戏的渲染坐标系相匹配,而不是直接使用操作系统提供的坐标,从而解决了跨平台坐标映射的问题。
(黑板) 如果我们希望后备缓存的最左列和最右列具有相同的绝对X坐标,则(0,0)坐标将位于像素边界上
这个问题的核心是坐标系统中的像素中心问题,尤其是当鼠标坐标映射到屏幕坐标时,如何处理像素对齐问题。
首先,要理解的是,坐标系统的中心与像素的精确位置关系。在游戏的坐标系统中,原点被设置在屏幕的中心,而操作系统中的坐标系统通常是以左上角为原点。这就导致了如何定义像素的中心,是否将坐标系统的原点放在像素的中心,或者像素的边缘上,成为了一个难题。
具体问题分析:
-
坐标系统的中心:
- 游戏的坐标系统将屏幕的中心视为
(0, 0)
,而操作系统的坐标系统是从左上角开始的,坐标值为正数的部分是向右和向下的。 - 在一个1920x1080分辨率的屏幕上,中心应该位于
(960, 540)
这个像素位置。但问题在于,坐标系统是否应该把(960, 540)
视为一个完整的像素中心,还是将它放在两个像素的边界之间。因为,如果屏幕分辨率是偶数,这个中间点其实是在第959.5像素和第960像素之间,这可能会导致一些不对齐的问题。
- 游戏的坐标系统将屏幕的中心视为
-
坐标系统对齐问题:
- 如果把
(0, 0)
放在屏幕的中心,那么它不完全位于某个像素的中心,而是两个像素之间。实际上,中心应该位于像素的边界线之间。这意味着当鼠标坐标的值是0
时,实际对应的应该是屏幕的-960
位置。类似地,鼠标的最大坐标1920
应该对应的是屏幕的960
位置。 - 如果不进行正确的对齐,鼠标可能会被映射到错误的位置,尤其是当使用调试工具时,这个误差会变得明显。
- 如果把
-
是否需要额外的修正:
- 通过调试可以发现,如果坐标系统中的
(0, 0)
放置在精确的像素边界上,鼠标的映射将不会完全对齐到像素中心。一个可能的解决方法是,在坐标转换时进行额外的微调,比如将-960
和960
位置微调,使其符合预期的像素边界。 - 这会影响渲染效果,尤其是在进行高精度的图形渲染时,像素的对齐会直接影响到显示结果的精细度。
- 通过调试可以发现,如果坐标系统中的
-
关于像素中心的讨论:
- 这个问题涉及到很多图形编程中的细节,特别是在像素对齐、坐标变换和渲染时的精度处理上。通常来说,图形程序员会对这些细节非常敏感,因为它们会直接影响到图形渲染的精度和质量。
- 对于普通的调试工作,可能不需要过于严格的像素对齐,但如果要精确地控制渲染输出,尤其是在高分辨率显示器上,像素的准确对齐是非常重要的。
-
最终决策:
- 基于上述讨论,决定如何处理这个问题并没有一个简单的答案。可以选择将
0
放在两个像素之间,或者根据具体需求调整坐标的转换方法。理论上,为了保证精度,最好是让0
处于像素的边界之间,但这也会带来不同的行为。 - 这个问题通常会在开发过程中通过多次调试、测试和反复调整来找到最合适的解决方案。
- 基于上述讨论,决定如何处理这个问题并没有一个简单的答案。可以选择将
总的来说,这个问题涉及到坐标系统的设计和鼠标坐标与屏幕坐标之间的转换,虽然这看似是一个小问题,但它却直接影响到调试和渲染的精确度。因此,需要深入思考并逐步调整,确保在开发过程中能够最大化地避免像素对齐不准确的问题。
修改字体的设置
暂停收集
目标是实现一种机制,可以在调试过程中停止或冻结当前的计算,而不再继续记录任何信息。为了做到这一点,可以通过条件判断来控制是否暂停或冻结调试过程。
具体步骤:
-
暂停功能的实现:
- 需要在调试状态下引入一个新的变量,标记是否处于暂停状态。例如,可以使用一个名为
paused
的布尔值来表示调试是否暂停。 - 在代码中,添加条件语句来判断是否处于暂停状态。只有当
paused
为false
时,才会继续执行计算或记录操作。
- 需要在调试状态下引入一个新的变量,标记是否处于暂停状态。例如,可以使用一个名为
-
修改条件判断:
- 在调试代码的部分,可以像这样添加条件语句:
// 执行正常的调试操作 if (!DebugState->Paused) { }
- 这样,只有在调试状态开启并且没有暂停的情况下,才会执行调试逻辑。如果暂停了,则不会执行任何操作。
- 在调试代码的部分,可以像这样添加条件语句:
-
实现暂停控制:
- 可以在某个事件(如鼠标点击)触发暂停。例如,当检测到某个鼠标按钮被按下时,可以改变
paused
的状态:if (Input->mouseButton) {paused = !paused; // 切换暂停状态 }
- 这样就可以通过鼠标的点击来控制调试过程的暂停与继续。
- 可以在某个事件(如鼠标点击)触发暂停。例如,当检测到某个鼠标按钮被按下时,可以改变
-
调试界面的变化:
- 需要在调试覆盖层(debug overlay)中显示当前的状态,比如显示 “Paused” 或者当前的调试信息。如果调试被暂停,可以在界面上显示暂停的标识,告诉用户当前的调试状态。
-
设计思考:
- 这种设计的关键在于灵活控制调试过程。通过引入
paused
状态,可以让调试过程在需要的时候停止,不再继续记录和显示信息。这种机制可以帮助开发人员在调试时更精确地控制和查看程序的状态,避免无效或不必要的计算。
- 这种设计的关键在于灵活控制调试过程。通过引入
总之,核心是通过引入一个 paused
标志,结合鼠标按钮的点击事件,控制是否继续调试过程。通过这种方式,能够有效地暂停和恢复调试,避免在不需要记录的情况下继续运行程序。
定义鼠标按钮的枚举类型
为了实现鼠标按钮控制调试暂停功能,计划在平台层(Platform Layer)中添加一个鼠标按钮枚举类型,并使用这些枚举来检测鼠标按钮的按下事件,进而切换暂停状态。
具体步骤:
-
创建鼠标按钮枚举:
- 在平台层中定义鼠标按钮枚举,用来表示不同的鼠标按钮。例如,定义
left
,middle
,right
三个按钮,便于后续引用和管理鼠标事件:enum platform_mouse_button {MOUSE_BUTTON_LEFT,MOUSE_BUTTON_MIDDLE,MOUSE_BUTTON_RIGHT };
- 这样就有了一个标准化的方式来管理不同的鼠标按钮,避免硬编码和提高代码的可读性。
- 在平台层中定义鼠标按钮枚举,用来表示不同的鼠标按钮。例如,定义
-
使用鼠标按钮枚举:
- 在平台层的相关代码中,使用这些枚举来检测鼠标按钮的状态。例如,检查是否按下了右键(
MOUSE_BUTTON_RIGHT
),如果按下了右键,就切换暂停状态:if (Input->MouseButtons[PlatformMouseButton_Right].EndedDown) {DebugState->Paused = !DebugState->Paused;}
- 通过这种方式,可以实现鼠标右键控制暂停的功能。
- 在平台层的相关代码中,使用这些枚举来检测鼠标按钮的状态。例如,检查是否按下了右键(
-
检查鼠标按钮按下状态:
- 需要检查鼠标按钮是否按下的状态。虽然没有直接的
went_down
宏或函数,可以通过检查鼠标按钮的“是否按下”来控制暂停状态的切换。 - 可能需要检查“过渡计数”(transition counts)来确认鼠标按钮的状态变化。虽然目前代码中似乎没有使用这部分功能,但可以在现有代码结构中增加类似的检查,确保按钮的按下事件被正确检测到。
- 需要检查鼠标按钮是否按下的状态。虽然没有直接的
-
调试暂停功能:
- 每当检测到鼠标右键按下时,切换
paused
状态。根据paused
状态,控制程序是否继续记录调试信息或执行其他操作。 - 例如,可以在调试状态下加一个检查:
- 每当检测到鼠标右键按下时,切换
-
清理和优化:
- 如果不需要“过渡计数”功能,也可以简单地删除相关代码,保持代码的简洁性。
- 可以进一步确认是否需要更复杂的鼠标状态变化检测,例如长按、连续点击等,或者是否仅需单次点击事件触发切换。
总结:
通过在平台层定义鼠标按钮枚举,并使用这些枚举来检测鼠标按钮的按下事件,能够更清晰地管理和控制调试暂停的功能。此外,通过检查鼠标按钮的状态变化,控制是否执行调试逻辑,提供了灵活的暂停机制。
实现WasPressed功能,使用记录的HalfTransitionCount
为了实现更清晰的鼠标按钮按下检测和调试暂停控制,计划引入一种新的方法来判断按钮是否被按下,避免仅依赖于鼠标按钮的状态变化。这种方法需要借助“过渡计数”机制来精确控制按钮按下和释放的时间。
主要思路:
-
过渡计数(Transition Count):
- 每个按钮的按下和释放事件都对应一个过渡状态,即按钮从“按下”到“释放”或从“释放”到“按下”的状态切换。
- 如果按钮的“过渡计数”(transition count)大于0,说明按钮经历了至少一次从“按下”到“释放”或从“释放”到“按下”的状态变化。这样就可以知道按钮的状态发生了变化。
-
按钮按下的判断逻辑:
- 通过检查过渡计数,可以判断按钮是否按下。具体的判断方式如下:
- 如果“过渡计数”大于1,说明按钮经历了多次按下和释放,按下事件肯定发生过。
- 如果“过渡计数”等于1,并且按钮的状态是“按下”状态(ended down),说明按钮在当前这一轮按下了。
- 通过检查过渡计数,可以判断按钮是否按下。具体的判断方式如下:
-
按下按钮时触发暂停:
- 如果按钮被按下(符合以上的条件),可以通过设置一个标志来暂停调试信息的记录。例如:
if (half_transition_count > 1 || (half_transition_count == 1 && ended_down)) {paused = true; // 按钮被按下,暂停调试 }
- 这样可以通过检测按钮的按下状态,控制调试是否暂停。
- 如果按钮被按下(符合以上的条件),可以通过设置一个标志来暂停调试信息的记录。例如:
-
代码结构:
- 可以将这种按下判断封装成一个函数,使得判断和处理逻辑更加清晰:
bool was_pressed(int button) {if (button_half_transition_count[button] > 1) {return true; // 按钮肯定被按下}if (button_half_transition_count[button] == 1 && button_state[button] == BUTTON_STATE_DOWN) {return true; // 按钮在当前周期按下}return false; // 按钮没有被按下 }
- 可以将这种按下判断封装成一个函数,使得判断和处理逻辑更加清晰:
-
暂停调试的触发:
- 在主循环中,每次检查按钮是否按下,并在按下时暂停调试:
if (WasPressed(Input->MouseButtons[PlatformMouseButton_Right])) {paused = !paused; // 切换暂停状态 }
- 在主循环中,每次检查按钮是否按下,并在按下时暂停调试:
-
提高精度和灵活性:
- 使用这种方法可以精确控制什么时候按钮被按下,同时避免了仅依赖鼠标按钮的状态的简单判断。通过对过渡计数的判断,可以确保每次按下和释放都被准确地检测到。
总结:
通过引入过渡计数和“按钮按下”判断的逻辑,可以更精确地控制鼠标按钮的按下事件,从而在调试过程中实现更加稳定和灵活的暂停功能。这种方法不仅能够判断按钮是否按下,还能够应对多次按下和释放的情况,确保程序在调试时能够可靠地暂停和恢复。
测试。没有效果
在调试过程中,遇到了关于按钮按下状态检测的逻辑问题。主要是通过“半过渡计数”(half transition count)来判断按钮是否按下。判断的条件是:
- 半过渡计数大于1:如果半过渡计数大于1,说明按钮经历了至少一次按下和释放的切换,因此可以确定按钮按下过。
- 半过渡计数等于1且按钮结束状态为按下:如果半过渡计数等于1,并且按钮状态是“按下”(ended down),也说明按钮在当前周期按下了。
在写这段逻辑时,出现了一个错误。代码中预期“半过渡计数”大于1时按钮被按下,但实际上似乎并没有正确识别这个条件,导致返回的结果不符合预期。通过调试,发现可能是状态没有被正确清除或更新,导致结果不对。
最后,问题得到了确认,正确的判断方式应该是根据按钮状态的变化来确认是否按下。而这种按下判断的核心在于“半过渡计数”是否为1,并且按钮状态是否是“按下”。
我们没有清除鼠标状态!
在调试过程中,发现了一个平台层的 bug,问题出在鼠标按钮状态的清除上。具体来说,鼠标按钮状态的值并没有被正确清除。通过检查代码,发现鼠标按钮状态只在某个地方被引用了一次,但之后再也没有被清除或更新。这意味着鼠标按钮的状态不会在每次新的输入周期中重新计算,导致状态错误。
问题的根本在于,代码并没有正确管理鼠标按钮的状态,导致状态信息可能会在不需要的情况下残留,进而影响后续的逻辑判断。这种处理方式不符合好的软件设计原则,因为状态应该在每次输入周期开始时被清理或者更新,否则会导致错误的行为。
因此,这被认为是一个软件设计中的重大问题,需要修复,避免状态信息不正确地延续到后续的逻辑中。这被归为“坏软件”或不符合最佳实践的代码。
在每一帧开始时清除half-transition计数
在调试过程中,发现了一个问题,涉及到半过渡计数器(half transition count)和状态更新的问题。问题出现在每一帧开始时没有清除半过渡计数,因此导致了状态没有正确重置。这会导致按钮触发不准确,可能会出现双重触发的现象。
解决方法是,在每一帧开始时,应该将所有按钮的半过渡计数重置为零。这是为了确保每一帧的按钮状态都是从零开始,避免上一个帧的状态干扰到当前帧。
此外,代码中还发现了一个问题,就是按钮状态的更新方式不正确。在更新按钮状态时,需要将当前帧的状态复制到上一帧的状态,以便正确计算状态变化。由于没有执行这一步,导致了状态的变化没有被正确记录,从而出现了错误的行为。
总的来说,这段代码需要重新组织和修复,确保每一帧的按钮状态是从零开始的,并且在更新时能正确地保存上一个状态。这些问题如果不修复,会导致系统的行为不稳定,影响调试和输入响应的准确性。
看看我们能否写出一些不完全垃圾的东西β
在解决当前的鼠标按钮状态管理问题时,尝试了改进代码的方式。首先,设定了一个鼠标按钮的索引,并尝试从旧的状态中复制这些信息,确保按钮状态的传递是连续的。
接下来,在处理按钮过渡状态时,清除了半过渡计数器,使得每一帧开始时按钮的状态能够正确初始化。通过设置适当的条件来判断按钮是否被按下,并在按下时正确地更新状态。最后,代码使用了简单的索引和标识符来确保按键状态能够被清晰地跟踪。
尽管这个方案有了一些进展,但还远未达到理想的效果。整体代码还有不少可以改进的地方,甚至仍然可以称之为“垃圾代码”,但相比之前,它至少做了一些基本的修复,提升了稳定性。
测试。有效
代码现在已经有了明显的进步,解决了暂停和调试输入的问题,并且实现了鼠标悬停时的反馈功能。虽然仍然有一些可以进一步改进的地方,比如增加调试按钮的大小,使得更容易定位和点击,但整体上已经在正确的轨道上。
接下来,应该实现一种方法,允许点击和检查这些调试输入,这应该是一个相对简单的任务。同时,还需要清理一些输出的代码,使得整体代码更加整洁。整个项目目前进展顺利,接下来的工作也有了明确的方向,感觉一切都在朝着正确的方向发展。
相关文章:
游戏引擎学习第188天
回顾并计划今天的内容 原本这周的目标是进行可视化操作的尝试,但每一天都被一些棘手的bug和问题所阻碍,导致我们一直没能实现这个目标。直到今天,星期四,我们终于解决了这些问题,所有功能都能正常运行了,所…...
TF-IDF——自然语言处理——红楼梦案例
目录 一、红楼梦数据分析 (1)红楼梦源文件 (2)数据预处理——分卷实现思路 (3)分卷代码 二、分卷处理,删除停用词,将文章转换为标准格式 1.实现的思路及细节 2.代码实现&#…...
Oracle数据库数据编程SQL<2.3 DML增、删、改及merge into>
目录 一、DML数据操纵语言(Aate Manipulation Language) 二、【insert】插入数据 1、单行插入 2、批量插入 3、将数据同时插入到多张表insert all/insert first 三、【update】 更新数据 1、语法 2、举例 3、update使用注意事项: 四、【delete…...
面试计算机操作系统解析(一中)
判断 1. 一般来说,先进先出页面置换算法比最近最少使用页面置换算法有较少的缺页率。(✘) 正确答案:错误解释:FIFO(先进先出)页面置换算法可能导致“Belady异常”,即页面数增加反而…...
启山智软实现b2c单商户商城对比传统单商户的优势在哪里?
启山智软实现 B2C 单商户商城具有以下对比优势: 技术架构方面 先进的框架选型:基于 SpringCloud 等主流框架开发,是百万真实用户沉淀并检验的商城系统,技术成熟稳定,能应对高并发场景,保证系统在大流量访…...
蓝桥杯备考:贪心问题之均分纸牌
咱的贪心策略就是每次分好一个堆儿,如果某个堆已经是满足题意了,就不用管这个堆了,否则要向下一个堆借几个元素 #include <iostream> using namespace std; const int N 110; typedef long long ll; int a[N]; int n; ll x; int cnt…...
免去繁琐的手动埋点,Gin 框架可观测性最佳实践
作者:牧思 背景 在云原生时代的今天,Golang 编程语言越来越成为开发者们的首选,而对于 Golang 开发者来说,最著名的 Golang Web 框架莫过于 Gin [ 1] 框架了,Gin 框架作为 Golang 编程语言官方的推荐框架 [ 2] &…...
centos7 linux VMware虚拟机新添加的网卡,能看到网卡名称,但是看不到网卡的配置文件
问题现象:VMware虚拟机新添加的网卡,能看到网卡,但是看不到网卡的配置文件 解决方案: nmcli connection show nmcli connection add con-name ens36 ifname ens36 type ethernet #创建一个网卡连接配置文件,这里con…...
python的内置方法getitem和len
完整小测试: #python的内置函数,getitemclass Animal():def __init__(self,name):self.name namedef __str__(self):return f"This is {self.name}"class Zoo():def __init__(self,animal_list):self.animal_list animal_listdef __getite…...
深入理解 Git Stash:功能、用法与实战示例
文章目录 深入理解 Git Stash:功能、用法与实战示例一、Git Stash 的核心概念二、Git Stash 的基本用法1. 存储当前修改2. 查看 Stash 列表3. 恢复 Stash4. 恢复并删除 Stash5. 删除 Stash(1)删除指定 Stash(2)清空所有…...
SQL 复杂查询和性能优化
一、掌握复杂查询的核心技能 1. 理解 SQL 执行顺序 SQL 语句的逻辑执行顺序(非书写顺序): FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT 关键点:每一步的结果会传递给下一步&a…...
【web应用安全】关于web应用安全的几个主要问题的思考
文章目录 防重放攻击1. **Token机制(一次性令牌)**2. **时间戳 超时验证**3. **Nonce(一次性随机数)**4. **请求签名(如HMAC)**5. **HTTPS 安全Cookie**6. **幂等性设计****综合防御策略建议****注意事项…...
看懂roslunch输出
自编了一个demo 第一步:创建功能包 cd ~/catkin_ws/src catkin_create_pkg param_demo roscpp第二步:写 main.cpp 创建文件:param_demo/src/param_node.cpp #include <ros/ros.h> #include <string>int main(int argc, char*…...
如何查看Unity打包生成的ab文件
文章目录 前言AssetStudioab文件介绍1. 动态加载资源2. 资源分离与模块化3. 平台兼容性4. 资源压缩与加密5. 资源管理与更新6. 减少安装包大小7. 资源加载灵活性8. 资源打包与分发9. 实际应用场景10. 注意事项 总结 前言 问题来源于工作又回归到工作,当发现发布包里…...
从泛读到精读:合合信息文档解析如何让大模型更懂复杂文档
从泛读到精读:合合信息文档解析如何让大模型更懂复杂文档 一、引言:破解文档“理解力”瓶颈二、核心功能:合合信息的“破局”亮点功能亮点1:复杂图表的高精度解析图表解析:为大模型装上精准“标尺”表格数据精准还原 功…...
Python SciPy面试题及参考答案
目录 什么是 SciPy?它与 NumPy 有什么区别? 如何在 Python 中安装 SciPy? 如何导入 SciPy 库? SciPy 中有哪些子模块?简要介绍它们的功能。 如何使用 SciPy 进行数值积分?请举例说明。 SciPy 中提供了哪些求解微分方程的函数? 什么是插值?SciPy 中如何进行插值?…...
21.Excel自动化:如何使用 xlwings 进行编程
一 将Excel用作数据查看器 使用 xlwings 中的 view 函数。 1.导包 import datetime as dt import xlwings as xw import pandas as pd import numpy as np 2.view 函数 创建一个基于伪随机数的DataFrame,它有足够多的行,使得只有首尾几行会被显示。 df …...
【redis】数据类型之Stream
Redis Stream是Redis 5.0版本引入的一种新的数据类型,它提供了一种持久化的、可查询的、可扩展的消息队列服务。 它结合了Redis高性能的特性与持久化能力,支持: 多消费者组模式(Consumer Groups)消息回溯(…...
day17 周末两天偷懒没更新,今天炼丹加学习,完结STL常用容器部分
还剩下两个常用容器,一个是set(集合容器) , 一个是map容器 set/multiset 容器 set容器是关联式容器,该容器的特点是:所有元素都会在插入时被自动排序 set/multiset 都是关联式容器 ,其底层结构是使用二叉树实现的。…...
嵌入式开发场景中Shell脚本执行方式的对比
Shell脚本执行方式对比表 执行方式命令示例是否需要执行权限是否启动子Shell环境变量影响范围适用场景嵌入式开发中的典型应用直接执行脚本./script.sh是是子Shell内有效独立运行的脚本,需固定环境自动化构建脚本(…...
数据结构之多项式相加的链表实现
在计算机科学中,多项式的表示和运算经常会用到。使用链表来表示多项式是一种常见且有效的方法,它可以方便地处理多项式的各项,并且在进行多项式相加等运算时具有较好的灵活性。 多项式通常由一系列的项组成,每一项包含一个系数和…...
Java 实现将Word 转换成markdown
日常的开发中,需要将word 等各类文章信息转换成格式化语言,因此需要使用各类语言将word 转换成Markdown 1、引入 jar包 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version&g…...
IEEE PDF Xpress校验出现 :字体无法嵌入问题以及pdf版本问题
文章目录 问题描述一、字体嵌入问题首先查看一下,哪些字体没有被嵌入查看window的font文件夹里的字体下载字体的网站修复字体嵌入问题 二、pdf版本不对 问题描述 在处理IEEE的camera ready的时候,提交到IEEE express的文件没有办法通过validate…...
Sa-Token
简介 Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。 官方文档 常见功能 登录认证 本框架 用户提交 name password 参数,调用登…...
StarRocks 中 CURRENT_TIMESTAMP 和 CURRENT_TIME 分区过滤问题
背景 本文基于Starrocks 3.3.5 最近在进行Starrocks 跑数据的时候,发现了一个SQL 扫描了所有分区的数据,简化后的SQL如下: select date_created from tableA where date_createddate_format(current_time(), %Y-%m-%d %H:%i:%S) limit 20其…...
GithubPages+自定义域名+Cloudfare加速+浏览器收录(2025最新排坑)
前言 最近刷到一个小视频,讲述了选择域名选择的三宗罪,分别是 不要使用 .net,因为它价格贵,但是在顶级域名中的 SEO 效果却不是很好,也就是性价比很低不要使用 .cn,因为国外访问该网站可能会很慢…...
Canvas粒子系统终极指南:从基础运动到复杂交互的全流程实现
文章目录 一、粒子系统基础架构1.1 粒子数据结构设计1.2 粒子系统管理器 二、基础粒子效果实现2.1 重力场模拟2.2 弹性碰撞效果 三、高级交互实现3.1 鼠标吸引效果3.2 颜色渐变粒子 四、性能优化策略4.1 粒子池复用4.2 分层渲染 五、复杂效果实现5.1 烟花爆炸效果5.2 流体模拟 …...
【QT】新建QT工程(详细步骤)
新建QT工程 1.方法(1)点击new project按钮,弹出对话框,新建即可,步骤如下:(2) 点击文件菜单,选择新建文件或者工程,后续步骤如上 2.QT工程文件介绍(1).pro文件 --》QT工程配置文件(2)main.cpp --》QT工程主…...
详解Http:在QT中使用Http协议
目录 一、HTTP 概述 1、主要特点 2、HTTP 方法 3、HTTP 状态码 4、HTTP 头部 5、HTTP的工作原理 二、在Qt中使用HTTP 1、发送简单的HTTP请求 2、发送POST请求 3、处理异步请求 4、使用QSslConfiguration进行HTTPS 5、 处理JSON响应 6、处理错误 三、总结 一、HTTP…...
Next.js 中间件鉴权绕过漏洞 (CVE-2025-29927) 复现利用与原理分析
免责声明 本文所述漏洞复现方法仅供安全研究及授权测试使用; 任何个人/组织须在合法合规前提下实施,严禁用于非法目的; 作者不对任何滥用行为及后果负责,如发现新漏洞请及时联系厂商并遵循漏洞披露规则。 漏洞原理 Next.js 是一个…...
AI时代的数据底座:火山引擎多模态数据湖的设计与实践
资料来源:火山引擎-开发者社区 随着大模型的发展和应用,文本的边界被拓宽,图像、视频、语音各种模态涌现,并给数据管理、检索、计算带来巨大挑战。 火山引擎多模态数据湖 解决方案则可实现海量结构化、半结构化及非结构化数据的统…...
Numpy用法(二)
一.数组变维 1.1 reshape reshape() 可以改变数组维度,但是返回的是一个新的数组,原数组的形状不会被修改.reshape后产生的新数组是原数组的一个视图,即它与原数组共享相同的数据,但可以有不同的形状或维度,且对视图…...
STM32 IIC通信
目录 IIC简介硬件电路连接I2C时序基本单元IIC完整数据帧MPU6050封装硬件IIC内部电路 IIC简介 IIC(Inter-Integrated Circuit)是 IIC Bus 简称,中文叫集成电路总线。它是一种串行通信总线,使用多主从架构,由飞利浦公司…...
快速入门 JSON 数据格式
引言 JSON,全称 JavaScript Object Notion,类似于XML,YAML,Properties等,是一种数据交换格式,相比于XML,更简单,更轻量,更容易理解。 JSON vs XML 使用 JSON 目前被广…...
FFmpeg —— 中标麒麟系统下使用FFmpeg内核+Qt界面,制作完整功能音视频播放器(附:源码)
🔔 FFmpeg 相关音视频技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) 程序运行效果...
硬件测试工装设计不合理的补救措施
硬件测试工装设计不合理的补救措施主要包括重新评估设计需求、优化工装结构、强化工装校准与验证。其中,优化工装结构尤其重要,通过结构优化能够有效解决因设计不合理导致的测试准确性下降和可靠性不足的问题。根据工程实践数据,经过优化结构…...
任意文件读取漏洞
fofa语句:body"/vite/client" /fs/etc/passwd?import&raw?? https://35.175.173.157/fs/etc/passwd?import&raw?? http://geometer.dev.mvergely.com/fs/etc/passwd?import&raw??...
如何使用RK平台的spi驱动 spidev
RK平台spidev驱动读取RC522版本号示例 1. 硬件与驱动确认 确认SPI接口连接:RC522的SPI引脚与RK开发板的对应SPI控制器正确连接(CS、CLK、MOSI、MISO)检查内核配置: Bash # 内核需启用以下配置 CONFIG_SPIy CONFIG_SPI_MASTERy…...
网路传输层UDP/TCP
一、端口号 1.端口号 1.1 五元组 端口号(port)标识了一个主机上进行通信的不同的应用程序. 如图所示, 在一个机器上运行着许多进程, 每个进程使用的应用层协议都不一样, 比如FTP, SSH, SMTP, HTTP等. 当主机接收到一个报文中, 网络层一定封装了一个目的ip标识我这台主机, …...
1.2-WAF\CDN\OSS\反向代理\负载均衡
WAF:就是网站应用防火墙,有硬件类、软件类、云WAF; 还有网站内置的WAF,内置的WAF就是直接嵌在代码中的安全防护代码 硬件类:Imperva、天清WAG 软件:安全狗、D盾、云锁 云:阿里云盾、腾讯云WA…...
Dify 服务器部署指南
1. 系统要求 在开始部署之前,请确保你的服务器满足以下要求: 操作系统:Linux(推荐使用 Ubuntu 20.04 或更高版本)内存:至少 4GB RAM存储:至少 20GB 可用空间网络:稳定的互联网连接…...
从车间到数字生态:MES如何引领制造业智能化革命
在全球制造业加速迈向工业4.0的浪潮中,传统生产模式正经历颠覆性变革。制造执行系统(MES)作为连接物理车间与数字世界的核心纽带,正从“生产辅助工具”升级为“智能决策大脑”,推动制造业向数据驱动、柔性化与可持续化…...
Error:Flash Download failed
出现这个就是编译器要换...
Spring容器生命周期详解
Spring容器生命周期详解 Spring容器的生命周期从启动到关闭分为多个阶段,包括Bean的加载、实例化、初始化、使用和销毁。以下是详细流程和关键点: 1. 容器启动阶段 1.1 容器实例化 核心接口:BeanFactory(基础容器)或…...
革新测试管理 2.0丨Storm UTP统一测试管理平台智能化升级与全流程优化
承接上篇:从基础架构到深度协同 在首篇文章《革新测试管理 | 统一测试管理平台如何实现远程、协同、自动化?》中,我们探讨了Storm UTP如何通过云端协作、自动化测试框架和分布式执行能力打破传统测试壁垒。经过一年多的客户实践与技术迭代&a…...
将 char [] str = “hello,you,world” 改为 “world,you,hello“,要求空间复杂度为1
题目: 将 char [] str “hello,you,world” 改为 "world,you,hello",要求空间复杂度为1 (也就是使用的变量只能是单个字符或者常数,不能使用数组!!!!!) 解…...
运维规则之总结(Summary of Operation and Maintenance Rules)
运维规则之总结 在运维领域,经验和流程往往决定了系统的稳定性与可靠性。一个运维人,总结出了以下10条运维规则,涵盖了从基础管理到高级策略的全面内容,旨在帮助运维人员更好地应对各种挑战,确保系统的平稳运行。 1.…...
MongoDB 创建数据库
MongoDB 创建数据库 引言 MongoDB 是一款高性能、可扩展的 NoSQL 数据库,广泛应用于大数据领域。在 MongoDB 中,创建数据库是进行数据存储的第一步。本文将详细介绍 MongoDB 数据库的创建方法,包括手动创建和自动创建两种方式。 MongoDB 数…...
SpringSecurity OAuth2:授权服务器与资源服务器配置
文章目录 引言一、OAuth2基础概念与架构二、授权服务器配置三、令牌策略与存储方式四、资源服务器配置五、远程令牌验证与内省总结 引言 在现代分布式应用架构中,OAuth2已成为实现安全授权与认证的事实标准。Spring Security对OAuth2提供了全面支持,使开…...
Vue 2 探秘:visible 和 append-to-body 是谁的小秘密?
🚀 Vue 2 探秘:visible 和 append-to-body 是谁的小秘密?🤔 父组件:identify-list.vue子组件:fake-clue-list.vue 嘿,各位前端探险家!👋 今天我们要在 Vue 2 的代码丛林…...