游戏引擎学习第191天
回顾并制定今天的计划
最近几天,我们有一些偏离了原计划的方向,主要是开始了一些调试代码的工作。最初我们计划进行一些调试功能的添加,但是随着工作的深入,我们开始清理和整理调试界面的呈现方式,以便能够做一些更复杂、更精美的功能展示。
前几天,我们对全局变量进行了一些清理,并将状态进行了整合。今天,我们打算继续这个方向,进一步完善用户界面(UI),为调试和其他操作添加一些交互功能。我们已经为这个目标奠定了一些基础,所以今天我们计划推动这些功能的开发,争取实现一些按钮,使得我们能够快速地进行操作,比如开关性能分析器(Profiler)等。
目前,我们还没有完全确定最终的交互设计方式,因此我们将继续尝试,看看最终会达成什么效果。我有一些想法,甚至在考虑一些可能有些过于荒诞的方式去实现这些功能,但有时候正是这些看似荒诞的想法,可能会带来最好的解决方案。
总之,我们现在正在推进这些工作,探索不同的方式,看看哪些方法最适合我们实现目标。
试试一些极其荒唐的东西α
回顾一下代码,我们之前做了一些关于性能分析窗口的工作,主要是让我们能够控制性能分析窗口的大小。这是一个开始,涉及到了矩形的使用和一些概念化的工作,整体进展顺利。接下来的目标是继续推进这个方向,努力进一步优化和扩展当前的实现。
我们希望能够打开和关闭调试界面的不同部分
现在的目标是创建一些概念,类似于“控件”或者“组件”,这些控件是我们需要显示的不同部分,可以通过某种控制方式进行开关操作。例如,通过点击某个按钮或者使用菜单来控制这些调试代码的显示与隐藏。当我们不需要它们时,它们不会干扰我们,我们可以随时关闭它们。当需要时,又可以重新打开。这是我希望实现的目标,虽然非常简单,但对提升调试的灵活性和易用性非常有帮助。
昨天我们还确保了循环实时代码编辑功能可以与调试系统兼容,应该不会出现问题。我还需要检查实时代码编辑是否与调试系统完美配合,结果显示一切正常,这对我们来说是个好消息。这样的话,我们就能更方便地调试那些难以复现的bug。假如这些bug能够进入循环状态,我们还可以查看性能数据,也许甚至可以冻结这些数据进行深入分析,虽然我不确定这是否会完全可行。
总的来说,当前的重点是使调试功能更加灵活,方便随时根据需求打开或关闭调试项。
目前,实时循环代码编辑会覆盖我们的输入,因此会阻止与调试 UI 的交互
关于如何实现这个目标,我们面临一个有趣的两难问题。如果我们把循环实时代码编辑功能整合得更好,就会出现一个矛盾的情况——录制功能会记录所有输入,这样一来我们就无法与调试代码进行任何交互了。这种情况下会显得有些复杂,因此我们需要考虑如何解决这个问题。但在目前这个阶段,我只是想确保我们实现的循环插件代码能够与调试系统兼容,结果看起来一切正常。
所以,现在我们应该继续进行接下来的工作,尝试实现更多的功能,推动项目向前发展。
使用鼠标右键作为“离合”键
我想尝试将右键鼠标按钮设计成一种“离合器”(clutch)的功能,通常在UI设计中,这个词是指按下一个按钮后,进入一种模式,能够访问许多菜单和选项。我的设想是,游戏在运行时一切正常,但当我按下右键时,会弹出一个UI界面,让我可以快速选择内容。这个界面可以是一个放射状排列的菜单,里面有多个选项,允许我快速打开或关闭一些功能。
具体的操作方式是,右键按下时,UI界面会弹出来,用户可以从中选择;如果按住右键不放,可能会进入一个快速开关的状态,进行一些快速的操作。我希望能实现一个类似的功能。
接下来,我将开始在调试代码中进行这个功能的实现。
打开和关闭性能分析器
首先,我打算从一个非常简单的功能开始,主要是让我们能够控制是否启用性能分析(profile)。我并不想在一开始就过于复杂化,因此我添加了一个布尔变量,用来表示性能分析是否开启,即是否显示相关内容。
接着,我查看了处理覆盖内容的代码,并且注意到与性能分析相关的部分。于是,我决定将这部分代码用一个条件语句包裹起来,让它仅在性能分析开启时才执行。具体做法是,将这部分内容放入一个“如果性能分析开启”的判断中。这样,运行游戏时,如果性能分析没有开启,相关内容就不会显示出来。
接下来,我们的目标是添加一个UI元素,使得用户能够通过交互来切换这个布尔变量的状态,也就是开启或关闭性能分析功能。这是一个非常基本的功能,我需要收集一些关于交互的信息,例如如何检测用户的操作等。
从自己熟悉的地方开始编写代码
在编写代码时,通常会采取从已知的部分开始,然后逐步向未知的部分推进的方法。这种方法帮助将问题分解成更容易处理的部分,而不是从一开始就试图解决所有的问题。具体来说,如果面对一个复杂的任务(比如开发一个径向菜单),不必一开始就考虑所有细节或者画出复杂的图表或者设计文档,尤其是当这些内容并没有提供实际帮助时。相反,应该从自己最熟悉的部分入手。
例如,首先可以专注于那些已经知道如何实现的功能,像是如何绘制图形。这些已知的部分能够快速实现,也能确保第一步是稳妥的。然后,逐步推进到自己不确定或者不熟悉的部分。通过这种方式,从已知到未知的过渡变得更为自然和可控。
这不仅是一种思维方式,也是一种具体的编程方法。它的关键在于避免过早地陷入过多不必要的抽象和设计,更多地是从实际的实现出发,逐步解决问题。在解决一个复杂问题时,逐步推进和从已知着手,可以避免陷入困境,并更清晰地理解解决方案的每个步骤。
总之,这种从已知到未知的逐步推进方法,可以帮助有效地处理复杂问题,并将开发过程变得更加清晰和可管理。
绘制一个径向菜单
在绘制径向菜单时,首先考虑的是实现菜单的基础绘制功能,而不关注界面设计或复杂的用户交互。开始时,可以创建一个简单的函数,比如 DrawRadialMenu
,并将其用作一个草稿平台。在这个函数中,不需要考虑太多具体细节,例如函数名或其是否应该作为一个独立的功能,而是专注于实现基本的绘制操作。
首先,需要一个用于渲染的对象(比如 render_group
)来处理绘制任务。同时,也可以考虑添加调试状态(debug state
),以便在后续开发中能够方便地进行调试和测试。在这个初始阶段,并不需要过于关注 UI 的实现,只是简单地绘制一个菜单的框架。
此时,不要提前设定菜单的样式或布局,而是将菜单假设为总是显示在屏幕上的状态,暂时忽略其他界面元素。这样做的目的是从最基本的绘制开始,逐步向复杂的交互和界面设计发展。
通过这种方法,可以专注于基本的实现细节,不受过多复杂设计的限制。首先完成基础的菜单绘制后,再逐步改进和完善其他功能,直到最终达成预期的效果。
假设我们有一些菜单项
首先,需要考虑的是创建菜单项。对于菜单项的数量和具体内容,初期不需要过多思考,只需假设有一些菜单项可供选择。可以随便列出一些可能的菜单项,例如:切换个人资料、切换输出、切换帧率计数器、标记循环点、绘制实体边界、绘制世界块边界等。虽然这些菜单项是否需要在实际应用中实现还不确定,但在当前阶段,只需要有一些基本的菜单项作为参考。
一旦有了菜单项,就可以开始思考如何绘制这些菜单项。为了绘制菜单项,显然需要遍历所有菜单项。这是一个理所当然的步骤,因此需要为每个菜单项设置一个索引。通过这个索引,可以逐个处理菜单项的绘制和展示,从而实现一个基本的菜单界面。
此时,关键是将注意力集中在菜单项的创建和显示上,而不需要立即关心其具体的功能或交互。这种方法帮助在实现过程中先建立一个基础框架,之后再逐步添加更多的功能和细节。
绘制菜单项
在绘制菜单项时,需要获取每个菜单项的文本(例如菜单项名称或文本)。一旦有了菜单项的文本,就要决定在哪里显示这些文本。可以利用已有的显示文本功能,例如通过 debug text out at
方法,将文本显示在指定的屏幕位置。
接下来,首先需要确定显示文本的位置。假设我们正在创建一个径向菜单,菜单项围绕一个中心点排列,因此需要计算每个菜单项相应的位置。为了做到这一点,需要明确文本的位置坐标,通常是一个点(x, y)坐标。这些坐标将决定每个菜单项文本在屏幕上的位置。
通过这些步骤,文本能够正确地显示在界面上的指定位置,接着就可以继续处理如何根据位置将文本显示在菜单的适当地方,确保菜单的布局符合预期。
(黑板讲解)将文本字符串围绕圆形排列
在实现径向菜单时,首先需要确定菜单项的位置,这些位置沿着圆形排列。为了做到这一点,首先需要定义菜单的半径(即圆的半径),并且通过一个参数化的值来控制这个半径的大小。接着,需要知道菜单项的数量,这样才能将圆形均匀地分割成对应数量的部分。
例如,如果有 8 个菜单项,那么整个圆周就会被分成 8 个等分,每个分段的角度就是 τ 8 \frac{\tau}{8} 8τ,其中 τ \tau τ 是圆的总角度(即 360°)。这个角度将用来确定每个菜单项的位置。通过计算每个菜单项的角度,可以利用三角函数来获取它们在圆上的坐标:使用余弦函数计算 x 坐标,使用正弦函数计算 y 坐标,得到的坐标就是圆上的某一点。然后,将这些坐标乘以半径,得到对应的外部圆上实际的菜单项位置。
得到这些坐标后,下一步就是在这些位置绘制文本。但是,为了让菜单看起来更美观,我们还需要将文本居中。当前的方法是将文本从坐标点的基准位置开始绘制,但这可能导致文本显示不够对齐,菜单看起来会有些歪斜。不过,这个问题在初步实现时并不严重,稍后可以针对文本的居中问题进行修正。
为了实现这个过程,可以先简单地绘制文本并在界面上显示菜单项。在这之前,可以设置一个函数来计算菜单项的位置,以便能够在正确的位置上显示每个菜单项的文本。
实现 Arm2
为了方便计算和绘制径向菜单项的位置,可以创建一个名为 arm_two
的函数,这个函数接收一个角度作为输入,并返回一个二维向量。这个向量的 x 坐标是该角度的余弦值,y 坐标是该角度的正弦值。这个函数的作用是根据角度计算一个单位向量,并且可以通过该向量表示圆周上某一点的位置。
在实际应用中,如果知道菜单项的数量,可以根据数量将整个圆周分成若干等份,然后通过计算每个菜单项的角度来确定它们的位置。假设菜单有 n 个项,那么每个菜单项的角度间隔就是 2 π n \frac{2\pi}{n} n2π,根据菜单项的顺序,逐步计算当前菜单项的角度。
一旦知道了角度,就可以通过 arm_two
函数计算出相应的单位向量,然后再根据菜单的半径将单位向量放大,得到实际的屏幕坐标。这个半径可以是一个可调参数,比如 200 像素,控制菜单的大小。将半径与计算出的单位向量相乘,就能得到最终的位置,确保菜单项的文本能够正确显示在屏幕上。
这个过程非常简单,首先计算角度,然后利用 arm_two
函数得到单位向量,最后根据菜单的半径放大单位向量,得出文本应该显示的位置。通过这些步骤,就可以准确地将文本放置在菜单项的位置上,从而完成菜单项的布局。
进行测试,看起来不错,但标签是否居中?
现在已经实现了径向菜单的基本功能,但目前菜单的位置似乎没有完全居中。虽然看起来可能已经居中了,但从视觉上来看,仍然感觉有些偏差,可能是因为计算或者绘制的位置存在某些细微问题。这个问题可能与菜单项的文本对齐有关,或者是菜单项的起始位置没有完全与圆心对齐,导致看起来不太准确。
因此,尽管菜单已经显示出来,但仍需要进一步检查和调整,以确保菜单项能够正确居中显示,尤其是文本的对齐方式。
标记圆心
为了更好地理解和调试径向菜单的显示,决定先绘制一个圆形,这样可以帮助检查菜单项的位置是否正确。为了清晰地看到圆的范围,使用了 push rect
来绘制一个矩形区域,目的是明确圆的实际边界。
首先,定义了一个矩形区域,这个矩形区域的中心点与圆心对齐,并且给定了一定的宽度和高度(例如两像素或三像素)。这个矩形框架会帮助更直观地看到圆形的位置。颜色设置为白色,确保在屏幕上能够明显看到这个矩形框。
通过这种方式,能够确认圆形的确是按预期绘制的。虽然当圆形位于基准线时可能不太容易直观判断,但一旦绘制了矩形框,就可以清晰地看到圆形的位置和大小。这个步骤帮助确认了圆形的位置是正确的,并且菜单项应该围绕这个圆形进行布局。
接下来,可能需要解决一些细节问题,尤其是在显示和排列菜单项时,进一步调整和优化圆形的显示效果。
使文本居中
为了能够更好地处理文本的布局,决定为文本添加一个矩形区域,这样可以方便地获取文本的尺寸,并且使文本能够围绕某个点居中显示。首先,计划利用现有的 debug text out
函数,进行一些扩展,让它能够支持获取文本的矩形区域。这是因为,文本的显示不仅仅是将文本直接输出,还需要考虑其边界和尺寸,以便更精确地定位和排列文本。
接下来,考虑在输出每个文本字符时,同时计算文本的矩形区域。具体来说,应该在渲染每个字符时,将它的坐标和尺寸记录下来,最终得出整个文本的边界矩形。这就需要修改现有的渲染代码,在输出每个字形时,同时计算并更新这个矩形区域。
通过这种方式,文本就能够根据矩形的边界进行居中调整,使得文本能够准确地在预定的位置显示,而不会偏离预期的布局。
计算标签尺寸的代码与绘制标签的代码有许多相似之处。我们需要确保这些操作的实现不会失去同步
为了避免文本绘制代码和矩形计算代码不同步,决定将文本输出部分抽象成一个新的函数,称为 DebugTextOp
。这个新函数将允许根据不同的操作类型(比如绘制文本或者计算文本的尺寸)来决定要执行的操作。
具体来说,DebugTextOp
函数会接收文本相关的参数,例如字体信息等,但不会直接进行文本的绘制操作,而是根据操作码(例如 draw text
或 calculate text size
)来执行不同的任务。如果操作是绘制文本,函数将执行绘制操作;如果是计算文本尺寸,则它将计算并返回文本的矩形区域。
在文本渲染过程中,特别是在绘制位图时,需要知道位图的尺寸,以便正确计算文本的边界。这时,通过在 PushBitmap
调用中,获取实际位图的尺寸,就能得到准确的矩形区域,从而确保文本在界面中的位置和布局是正确的。
通过这种方式,可以更灵活地处理文本的绘制和尺寸计算,同时避免代码的重复和不一致性。
将位图尺寸计算从 PushBitmap
中提取出来,以便其他函数可以调用
为了避免在不同地方重复调用 PushBitmap
,决定将获取位图尺寸和对齐信息的代码提取为一个独立的函数。这个新函数将返回位图的尺寸、对齐方式和其他相关信息,而不再需要直接调用 PushBitmap
。通过这种方式,可以更加灵活地获取位图的相关信息,并在其他地方使用这些信息,而不需要重复执行绘制操作。
新的函数可能会被命名为类似 GetTextDimension
,它会接收所需的参数并返回位图的尺寸、对齐信息以及其他相关的计算数据。这样,代码就能在不直接绘制的情况下,通过返回的数据来了解位图的尺寸和位置。该函数会处理计算位图尺寸、对齐等相关逻辑,并返回所有必要的信息,如位图的大小和对齐基准。
为了确保代码的一致性和避免出错,所有计算位图尺寸和对齐的逻辑都会被统一管理。将这些逻辑从原来的代码中提取出来之后,可以通过新的函数来访问这些信息,确保代码路径的一致性,避免因为重复代码的不同步而产生问题。
通过这种方法,能够更高效、更灵活地处理位图的尺寸和位置,同时保持代码的清晰和一致。
定义 DebugTextOp
为了使文本处理更灵活,决定在代码中引入一个“文本操作”机制。在此机制中,首先定义一个 debug_text_op
,用于指定操作的类型,如“绘制文本”或“获取文本尺寸”等。这样,可以将操作作为参数传入,从而动态决定对文本的处理方式。
具体来说,可以引入一个类似 DebugTextOp
的函数,它接受一个操作类型参数以及其他必要的参数,然后根据传入的操作类型执行相应的逻辑。例如,若操作类型为“绘制文本”,则执行文本的绘制操作;若操作类型为“获取文本尺寸”,则执行获取文本尺寸的逻辑。
通过这种方式,可以统一管理文本相关的处理逻辑,并且根据不同的需求灵活地选择操作类型,从而提高代码的可扩展性和可维护性。同时,避免了重复的代码,使得文本处理更加简洁和高效。
PushBitmap
将忽略未加载的位图
为了能够正确地获取和处理位图,首先需要从 bitmap ID
获取实际的已加载位图。由于这个过程可能会失败,因此在尝试获取位图时,必须考虑到位图可能不存在的情况。如果无法加载位图,可以简单地跳过这部分文本处理,因为该位图不会在当前帧被渲染。
实现这个逻辑时,可以先尝试通过 GetBitmap
获取位图的尺寸。如果获取失败,则不处理该文本,因为该资产不会在当前帧渲染。这种方式简化了流程,避免了对不存在的位图进行多余的处理。
同时,为了使代码更加简洁和清晰,可以考虑对现有的代码进行一定的整理和清理,确保各个部分更容易理解和维护。通过传递必要的参数,如 debug state
、DebugState、
FontInfo和
String`,可以确保正确执行文本的绘制或尺寸获取操作。
在 DEBUGStart
时预先加载调试字体
在代码优化过程中,首先需要清理一些不必要的步骤,避免每次都重复相同的操作。特别是在调试状态处理时,确保在进行调试时不必重复执行每次渲染的逻辑。当处理渲染分组时,可以直接处理字体信息,避免每次都重新传递和加载。
具体来说,可以在开始渲染时(比如调用 BeginRender
)时,确定渲染分组已经有效,并且字体信息也已经加载。这时,我们只需将字体信息推送并获取,确保渲染过程中可以直接使用相关数据,而无需每次都重复此操作。
对于调试操作(DebugTextOp
),可以根据是否有有效的字体信息来决定是否继续执行。在没有字体信息的情况下,调试操作可以自动跳过相关步骤,而不需要再次传递这些数据。
进一步的优化方案是,将字体检查和加载步骤进行条件判断,只有在字体信息存在时,才会继续执行渲染操作。若条件满足,则可以顺利进入渲染过程,不需要重复进行设置和处理。这样可以减少不必要的重复代码,提升效率,并确保渲染逻辑更加简洁和清晰。
总体目标是通过改进调试和渲染的流程,使得每次渲染时不需要重复加载和处理相同的资源,只在必要时进行检查和更新,从而简化代码并提高运行效率。
修复一些编译错误
通过优化后,代码不再需要每次都检查和加载字体信息,避免了重复的操作。现在,只要确保字体已加载并且有有效的字体信息,就可以直接进行渲染操作,这样能大大简化逻辑,减少不必要的资源加载。
在具体的实现中,传递给调试状态的字体信息已经不再需要每次都传递,代码变得更加简洁。我们移除了原本不必要的字体信息传递,仅保留了加载字体和字体信息的相关操作。通过这种方式,代码更清晰,也符合个人的偏好,将字体信息的检查放在了前面。
在完成代码修改后,还对其进行了检查,确保没有破坏原有功能,所有操作和调试逻辑都正常运行,确保代码状态良好。最终,通过这些改进,不仅提高了代码的效率,也使得代码的可维护性和清晰度得到了提升。
通过计算每个字符的矩形并取其并集来计算字符串的尺寸
首先,通过获取字体的位图尺寸,可以轻松地获取所需的矩形区域,这个矩形区域描述了字体的空间大小。具体来说,位图尺寸(bitmap dim
)将包含所有需要的信息,如变换后的尺寸、对齐方式等。这些信息可以帮助我们确定字体在原始空间中的位置和大小。
在获取这些信息时,主要关注的是变换后的尺寸和位置(P),这两个信息会告诉我们字体的边界。在计算时,可以使用这两个信息来生成一个矩形,它将表示字形在输入空间中的边界。
接下来,可以通过创建一个初始的矩形,设定为无限大,以便在后续过程中逐步更新该矩形。每次处理字形时,都可以将当前的矩形与新的字形矩形进行联合,最终得到一个包含所有字形的完整矩形。
具体的实现中,矩形的计算是通过不断地进行“联合”(Union
)操作完成的。每次将当前字形的矩形与已有矩形进行联合,逐渐扩大矩形范围,直到最终覆盖所有字形的区域。
在实际代码中,通过这种方式,我们能够确保获取到包含所有字形的最小矩形区域。并且,由于这是一个不断更新的过程,因此可以确保最终得到的矩形是准确的,涵盖了所有需要的字形区域。
实现 rectangle2
版本的“倒置无限矩形”
在处理矩形时,需要一个特殊的矩形,即“反无限矩形”,它能够在初始化时覆盖整个空间。为了方便每次操作时都能使用这个矩形,决定为所有矩形创建一个类似的“反无限矩形”版本。这样,每次处理新的字形矩形时,可以用这个矩形作为初始值进行合并。
首先,创建一个新的矩形类型(例如rectangle - version
)并进行相应的初始化。通过将该矩形与现有的矩形进行联合(Union
)操作,逐渐扩大最终的矩形范围。这样可以确保每次都能正确地处理字形矩形,最后得到一个包含所有字形的最小矩形区域。
接着,发现有一些矩形类型(如v2
)缺少必要的转换方法,需要为其提供适当的转换功能。例如,缺少将v3
类型转换为v2
类型的转换函数。这个缺失显得有些疏忽,因为转换两个不同版本的矩形应该是一个常见的需求,解决此问题后可以更方便地处理各种矩形数据。
此外,对于template
的使用,考虑到有些问题可以通过模板来解决,模板在这种情况下确实能够简化代码,因为它允许在不同类型之间共享相同的操作,避免了重复的代码实现。通过这种方式,可以更高效地管理不同类型的矩形和相关操作。
总的来说,通过不断改进矩形处理的代码,确保了矩形的合并和转换操作更加灵活和简洁,避免了重复的逻辑,并为不同类型的矩形提供了必要的支持。
测试新代码,实现 DEBUGGetTextSize
经过调整,代码现在看起来应该是正常的。接下来,可以使用一个新的函数来验证这些修改是否有效,确保做出的改动没有引入问题。首先,检查文本输出是否正常,并确保通过调试功能计算文本大小。
为了实现这一点,设计了一个新的函数 DEBUGGetTextSize
,该函数的输入参数为文本内容(字符串)。由于不再需要传递额外的位置信息(如P
),所以可以简化调用,只需要提供字符串作为输入。然后,调用 DebugTextOp
函数,并将计算结果存储在result
中。为了简化调试,文本的位置设定为 (0, 0)
,这样可以确保在原点位置输出文本。
最后,不要忘记将调试状态(debug state
)传递给该函数,这样就能够确保调试信息正确记录并处理。这一系列的修改应该能够帮助确认程序是否按预期工作,避免引入任何不必要的复杂性。
绘制文本标签的边界以检查其位置
通过这一系列的改进,现在可以尝试绘制文本的边界并检查其准确性。为了做到这一点,首先需要创建一个矩形(rectangle
),它将表示文本的边界框。然后,使用之前的字符串来计算这个矩形的尺寸,确保文本的边界能够正确显示。
接下来,可以使用 push rect
函数来绘制矩形边界,暂时使用普通的矩形绘制方法(而不是边框绘制),因为当前还没有实现专门的矩形边框绘制功能。为了更好地查看文本位置,可以使用一种深蓝色的背景色,将其围绕文本绘制,方便进行视觉验证,确认文本是否位于正确的位置。
绘制矩形时,确保使用正确的文本位置。为了做到这一点,矩形需要根据文本的起始位置(P
)进行偏移调整。如果没有现成的偏移功能,可以检查是否已有类似的功能,虽然目前仅在rectangle3
类型中找到了偏移函数,但rectangle2
类型没有提供对应的重载。因此,可以考虑增加一个合适的偏移函数,将矩形根据文本的实际位置进行调整。
最终,通过这些操作,能够确保文本的显示和边界框正确无误,位置也精确无误。这种方法确保了文本的尺寸和边界准确性,从而可以有效验证文本布局的正确性。
标签的位置是精确的
通过这一步,文本的边界框成功绘制出来并且能够进行准确的验证。首先,创建了一个矩形来表示文本的边界框,并使用之前获取的字符串来计算文本的尺寸。这确保了能够正确地绘制出文本所占的空间。
为了验证文本的位置和大小,绘制了一个矩形,使用深蓝色背景将文本框围住,从而便于查看和确认文本是否放置在正确的位置。这样,通过视觉上确认边界框的位置和大小,能够确保文本的布局和位置准确无误。
此外,考虑到文本的位置需要进行偏移调整,使用了一个偏移函数来确保矩形的位置与文本的位置相符。尽管目前偏移函数只适用于某些类型的矩形,但通过适当的调整和添加,确保了矩形的正确位置。
最终,所有操作的结果证明是有效的,文本的边界框显示正常,且文本位置和尺寸都符合预期。
关于“操作”代码转换的一些想法
这段代码实现了一个有效的转换方法,允许在相同的代码块中生成不同的结果,特别是在不太关注性能的情况下(比如调试代码时)。这种方法通过将操作作为参数传递给函数,避免了重复编写类似的代码。这样做的好处是可以灵活地改变操作的内容,而不需要写两份几乎相同的代码。
这种方式也是一种绕过C语言的限制的解决方案,C语言不像某些功能更强大的语言(比如函数式编程语言)那样,提供了处理闭包等高级功能的机制。由于C和C++语言在设计上并未考虑到这些需求,因此开发者往往需要找到替代方案。尽管C语言的设计有其缺点,但通过将操作码作为参数传递的方式,仍然能够解决问题并且在实践中表现得更好。
总的来说,这种方法是通过简单的操作传递和参数化来实现灵活的功能扩展,而不是依赖于语言本身的高级特性。这使得代码保持简洁且可维护,同时能够应对多种场景。
使标签居中
目标是将文本居中,这个过程变得相对简单。现在已经能够根据已知的文本位置(TextP
)和文本的边界(bounds
)来轻松实现文本的居中。方法是首先获取文本边界的尺寸,然后将其一半的宽度从当前位置中减去,从而使文本居中。
通过这种方式,文本准确地围绕着目标点居中显示。原本绘制文本的边界框位置并不再需要,因此可以去掉这部分代码,使得实现更加简洁。现在,文本和图形都能够正确显示,并且菜单也正确地围绕中心点进行布局。
为了验证效果,可以测试多个菜单项,确保它们的显示位置正确。这一步骤验证了文本的居中和布局的准确性。虽然当前的实现已经满足基本要求,但可能还需要进一步调整布局,特别是对于顶部的菜单项,因为顶部的文本可能会遇到比底部更复杂的布局问题。因此,未来可能需要对文本的排列方式做一些进一步的优化。
总的来说,现在的代码已经能够有效地确保文本的居中,并且菜单的布局看起来正确,虽然还可能需要考虑更多的细节来优化显示。
找出最接近鼠标指针的菜单索引,以更改其颜色
为了实现菜单项的选择功能,我们可以利用鼠标位置来判断用户当前选择了哪个菜单项。首先,可以通过鼠标的位置和菜单项的位置计算距离,找到与鼠标位置最接近的菜单项。这一过程相对简单,只需要维护一个最短距离,并在遍历所有菜单项时更新它。
具体来说,首先初始化一个最佳菜单索引和最佳距离,然后通过计算鼠标位置与每个菜单项的位置的距离,找出距离最小的菜单项。可以使用距离的平方来避免计算开方,从而提高效率。每当遇到一个距离比当前最佳距离更小的菜单项时,就更新最佳菜单索引。
在找到鼠标当前选择的菜单项后,最后通过该索引来确定所选菜单项。在这过程中,我们还可以通过颜色来区分当前选中的菜单项。例如,可以将选中的菜单项的颜色改为黄色,而其他的保持默认颜色。这样用户就能够清晰地看到他们正在选择哪个菜单项。
此外,对于文本的颜色,可以添加一个颜色参数,允许在绘制文本时指定颜色。默认情况下,颜色为白色,但如果当前菜单项是用户鼠标悬停的目标,就可以改变颜色,突出显示选中的菜单项。
通过这种方式,可以很容易地实现一个带有选择功能的菜单,用户只需要根据鼠标位置即可快速选择菜单项。这个过程不仅简单,而且能够提供直观的交互效果。
激活菜单项
在结束当前的之前,可能需要先处理一些细节。为了确保所有功能都按预期工作,可以先将一些必要的内容处理完。比如,可以考虑是否需要询问前置条件或者其他相关的设置,并将这些内容暂时添加到当前的代码中,确保代码结构更加完善和清晰。
这一过程的重点是确保在继续开发之前,所有必须的前置条件已经正确设置好,并且代码中的每个部分都能正确地交互。通过整理和检查这些细节,可以减少之后开发过程中可能出现的问题。
这样做类似于 β
在处理这些功能时,首先需要处理鼠标点击事件。当用户按下鼠标按钮时,我们就能执行相应的菜单选项。可以设定某些条件,比如按下鼠标按钮时,检查菜单项并做出反应。此时,我们可以通过鼠标位置来确定用户选择的菜单项。
接着,检查鼠标按钮的状态。如果按钮已经释放,意味着用户已经完成了选择。此时,我们会处理他们选择的菜单项,执行相应的操作。这需要检查鼠标当前位置,确定用户点击了哪个选项。如果选择的选项与当前状态相关联(比如切换某个功能开关),那么就执行相关操作。
此外,可能需要处理一个“调试菜单”的显示逻辑,确保在需要的时候能够显示出来,并且根据用户的鼠标操作进行交互。在鼠标点击事件发生后,还需要更新一些状态信息,比如是否切换了配置或功能。这些操作都相对简单,核心是根据鼠标输入更新状态并执行相应的功能。
最终,通过这些步骤,可以实现用户交互逻辑,确保调试菜单能够根据鼠标操作正确反应并执行预期功能。
进行测试
此时,径向菜单已经开始运作,虽然还有一些不希望出现的情况需要调整。例如,在菜单显示的位置,当前是始终从屏幕中心开始显示,而不是鼠标点击的地方。因此,决定加入一个逻辑来根据鼠标按下的地方来调整菜单的显示位置,而不是默认的居中显示。
另外,还需要考虑调试过程中的操作,例如切换调试功能的按钮,执行某些调试操作时的暂停和状态切换,这些都需要通过鼠标事件进行控制。最终目标是让菜单在点击右键时弹出,并能正确响应用户的操作。
将径向菜单放置在鼠标点击的位置
在调试过程中,想要实现菜单的正确显示位置,特别是在鼠标点击时能够根据点击的位置动态移动菜单。首先,通过检查Input->MouseButtons[PlatformMouseButton_Right].HalfTransitionCount > 0
是否大于零,来判断是否点击了鼠标。当鼠标按钮按下时,记录下当时的鼠标位置,即“菜单位置(MenuP)”。然后,在处理文本的显示位置时,将菜单位置偏移到鼠标按下的位置,这样就能让菜单随着鼠标的移动而调整位置,从而模拟一个正常的径向菜单行为。
完成这个调整后,菜单就能够根据鼠标位置来显示,并且可以实现功能切换,比如“切换性能图”或“切换调试数据收集”。此时,虽然径向菜单已经可以正常工作,但界面还没有很漂亮,需要进一步优化。
预告未来的内容
目前的UI仍然需要大量改进,才能达到理想的状态。当前的菜单项显得有些零散,布局上不够紧密,功能虽然已经实现,但仍处于基础阶段,仅仅是实现了基本的使用代码。接下来的工作重点是将这些内容整理得更加连贯,使其结构更加合理,并提升整体的可用性和美观度。
在后续的优化过程中,将采用典型的“压缩导向编程”方法,对已经实现的功能进行提炼和优化,提高代码的组织性,使菜单更加清晰易用。在接下来的开发中,会进一步调整和完善径向菜单的表现形式,使其真正达到良好的用户体验。虽然目前只是完成了基础功能,但径向菜单的引入已经是一个不错的开始,未来会围绕这一系统继续优化和扩展功能。
我想你为 v3
到 v2
的转换添加了 v3.xy
在转换过程中,之前的实现方式实际上更加智能。这样可以更好地控制所获取的内容,确保选择的准确性和灵活性。相比之下,当前的方法可能没有之前的方法高效,因此更早的决策在这一点上显得更加合理。这样的方式能够更精准地决定需要提取的内容,提高代码的可维护性和可读性。
即使圆形菜单变得混乱,我们也可以让每个按钮指向另一个圆形菜单 / 层级 / 子集
即使圆形菜单中的选项变得混乱,也可以通过让每个按钮指向另一层子菜单的方式来优化布局。这样可以将选项分层组织,使菜单更易于使用。目前尚未完全确定最终的菜单布局方式,需要进一步观察有哪些选项需要包含在其中。
接下来的计划是深入研究如何合理安排这些选项,使其既直观又高效。未来会在实践过程中逐步探索合适的设计方案,并调整菜单的交互方式,以确保其灵活性和可用性。
你能更详细地解释 C 语言中的闭包以及如何实现吗?我不是很理解
在 C 语言中,无法直接实现像其他高级语言那样的闭包(Closure)机制,因此我们采用了一种“简化版”的方法来实现类似的功能。
我们面临的需求是,希望能够在某些复杂的迭代过程中,将代码的某个部分作为可替换的逻辑块,同时让它能够访问特定的数据。例如,在遍历世界块(world chunk)时,我们需要对每个块执行特定的操作,这部分逻辑应当是可变的。同样,在绘制矩形时,可能需要根据不同的着色方式来计算像素颜色,而不改变外围的迭代和计算逻辑。
在更高级的语言中,闭包允许代码块携带相关的变量环境,使其可以在不同的上下文中执行,而仍然能够访问原始数据。但 C 语言不具备这样的能力,所以我们需要另一种方式来实现类似的效果。
我们的方法是,在关键代码块中,使用一个变量来标识当前应执行的具体逻辑,而不是动态地传递函数或代码块。例如,在执行某些着色逻辑时,我们在代码内部插入了一个变量,根据该变量的值来决定执行哪种计算逻辑。这种方式避免了 C++ 中 lambda 表达式带来的复杂性,同时利用分支判断的开销相对较小,能够在性能和灵活性之间取得平衡。
虽然这种方法不能完全模拟闭包,例如它无法让不同代码块拥有各自独立的数据环境,也无法直接访问调用者的栈帧,但在实际使用中,它仍然是一种可行的解决方案。通过这种方式,我们能够在 C 语言中实现类似于闭包的功能,提升代码的可复用性和灵活性,同时避免引入过于复杂的语言特性。
在 C 语言中模拟闭包的示例
我们希望在不使用 C++ 的 lambda
或 std::function
,也不使用函数指针的情况下,在 C 语言里实现类似闭包的行为。
示例 1:使用 switch
变量模拟闭包逻辑
假设我们有一个绘制函数,需要在不同的模式下执行不同的着色逻辑,但外围的遍历逻辑保持不变。
#include <stdio.h>// 定义几种不同的着色模式
typedef enum {MODE_NORMAL,MODE_HIGHLIGHT,MODE_SHADOW
} ShaderMode;// 全局变量(或者局部静态变量)存储当前的着色模式
ShaderMode currentMode = MODE_NORMAL;// 处理像素的函数
void processPixel(int x, int y) {int color = 0;// 使用变量控制代码逻辑switch (currentMode) {case MODE_NORMAL:color = 255; // 正常模式break;case MODE_HIGHLIGHT:color = 128; // 高亮模式break;case MODE_SHADOW:color = 64; // 阴影模式break;}printf("Pixel at (%d, %d) -> Color: %d\n", x, y, color);
}// 遍历所有像素的函数
void drawRectangle(int width, int height) {for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {processPixel(x, y);}}
}int main() {// 先使用普通模式绘制printf("Normal mode:\n");currentMode = MODE_NORMAL;drawRectangle(3, 2);// 切换到高亮模式printf("\nHighlight mode:\n");currentMode = MODE_HIGHLIGHT;drawRectangle(3, 2);// 切换到阴影模式printf("\nShadow mode:\n");currentMode = MODE_SHADOW;drawRectangle(3, 2);return 0;
}
解释:
currentMode
充当一个“闭包变量”,控制processPixel
该如何计算颜色。drawRectangle
只负责遍历像素,它不关心具体的绘制逻辑。- 通过修改
currentMode
,我们可以改变processPixel
的行为,而不需要修改drawRectangle
。
示例 2:使用 struct
模拟闭包(携带数据的代码块)
如果我们需要在不同的模式下,使用不同的参数(比如亮度、对比度等),可以用 struct
来封装数据,使得“闭包”能够携带环境变量。
#include <stdio.h>// 定义一个闭包数据结构
typedef struct {int brightness;int contrast;
} ShaderContext;// 处理像素的函数,接收一个环境变量
void processPixel(int x, int y, ShaderContext *ctx) {int color = (255 * ctx->brightness) / 100;color = color * ctx->contrast / 100;printf("Pixel at (%d, %d) -> Color: %d\n", x, y, color);
}// 遍历所有像素的函数
void drawRectangle(int width, int height, ShaderContext *ctx) {for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {processPixel(x, y, ctx);}}
}int main() {// 定义不同的环境变量ShaderContext normal = {100, 100}; // 正常亮度和对比度ShaderContext highlight = {150, 120}; // 高亮模式ShaderContext shadow = {50, 80}; // 阴影模式// 传递不同的闭包数据,执行相同的遍历逻辑printf("Normal mode:\n");drawRectangle(3, 2, &normal);printf("\nHighlight mode:\n");drawRectangle(3, 2, &highlight);printf("\nShadow mode:\n");drawRectangle(3, 2, &shadow);return 0;
}
解释:
ShaderContext
结构体充当“闭包”,它携带了brightness
和contrast
两个参数,影响processPixel
的计算方式。drawRectangle
不需要知道具体的着色逻辑,它只是简单地调用processPixel
并传入ShaderContext
,实现了逻辑解耦。
总结
-
第一种方法(全局变量 +
switch
)- 适用于简单场景,只需要修改单一逻辑,而不需要额外参数。
- 缺点是
currentMode
变量是全局的,不适用于多线程或需要多个独立实例的情况。
-
第二种方法(
struct
传递环境变量)- 适用于更复杂的场景,比如不同的参数配置,或者在不同的地方同时应用不同的逻辑。
ShaderContext
允许processPixel
访问额外的上下文信息,类似于闭包中的“绑定变量”。
在 C 语言中,这种方法虽然不像真正的闭包那样灵活,但可以有效实现类似的功能,同时避免 C++ 复杂的 lambda
或 std::function
机制带来的额外开销。
你为什么选择径向菜单而不是列表或其他格式?
我们选择径向菜单(Radial Menu)而不是列表(List)或其他布局方式的原因主要是其快速选择的特性。
-
快速启用和关闭选项:
- 径向菜单适用于快速操作,用户可以通过鼠标或手柄的方向选择需要的选项,减少鼠标移动的距离,提高效率。
- 适用于开关类选项,比如“启用/禁用调试模式”等。
-
直观的方向选择:
- 由于菜单项是围绕中心点分布,用户可以用方向性思维快速定位所需选项,而不像列表那样需要上下滚动寻找。
- 适用于数量适中的选项(通常 6-8 个),不会占据过多屏幕空间。
-
减少视觉干扰:
- 列表或下拉菜单可能会占据较大面积,影响游戏界面的可视性,而径向菜单在短暂显示后可以快速消失,不干扰主要内容。
其他补充
- 不仅限于径向菜单:
- 也会加入列表菜单,因为有些情况下列表比径向菜单更适合,特别是当选项较多、需要精确选择时,比如详细的设置或高级选项。
- 可能会结合两种方式,径向菜单用于快速选择常见功能,而列表菜单提供更精细的控制。
总的来说,径向菜单适用于快速操作,而列表适用于更复杂或精细的选择,两者各有优劣,我们会在不同场景中使用合适的菜单类型。
如果菜单项与圆形边界对齐,那样文本碰撞的可能性会更小
我们计划对径向菜单进行一些优化,使其更直观和美观,同时减少文本重叠的问题。
优化方向
-
菜单项对齐圆形边界:
- 让菜单选项沿着圆的边缘排列,而不是随意分布,这样可以确保每个选项都均匀分布,减少视觉混乱。
- 这样做还能让玩家更容易判断每个选项的位置,提升交互体验。
-
优化文本显示,避免重叠:
- 目前如果选项过多,文本可能会发生重叠,导致阅读困难。
- 解决方案:
- 自动换行:当文本过长时,调整字号或进行换行,使其在有限空间内显示完整信息。
- 缩略文本:如果菜单空间有限,可以在 hover 或选中时显示完整文本,避免影响整体布局。
- 弧形文本对齐:让文本沿着径向菜单的曲线排列,使整体布局更加自然。
-
整体视觉调整:
- 增加一些 UI 细节,比如半透明背景、柔和阴影、悬停高亮等,让菜单更加现代化。
- 确保选项不会因过多的 UI 细节影响可读性。
这些改动可以让径向菜单既实用又美观,同时确保操作的流畅性和直观性。
你提到 C 语言不支持闭包。你是否尝试过通过“仿函数”或其他方法来“黑科技”实现它?
我们并没有真正尝试在 C 语言中实现闭包(closures),也没有用函子(functors)等方式去模拟它。不过,在一些情况下,我们确实会使用多重管理栈(multiple managed stacks)来达到类似闭包的效果。例如,在一些代码实现中,我们会创建多个受控的内存管理区域,并在其中存储数据,这在某种程度上涉及到了闭包的概念。
尽管我们没有真正“破解”C 语言的闭包问题,但在实践中,某些方法可以部分实现类似闭包的行为。例如,我们可以使用结构体(struct)来封装函数指针及其所需的上下文数据,并将这个结构体传递给其他函数,从而实现类似闭包的作用。不过,这种方法仍然需要手动管理内存和作用域,与真正的闭包相比要复杂得多。
总体而言,我们并没有深入尝试在 C 语言中模拟闭包,而是更倾向于使用已有的技术手段来实现类似的功能。
你是否考虑过在直播中添加一个调试输出,以显示你的按键?
我们并没有考虑过在调试输出中显示按键记录,比如在屏幕底部显示按下的组合键(例如按下 Ctrl + G
时在屏幕底部显示 Ctrl + G
)。主要原因是我们并不特别关注是否要向观众展示具体的按键操作,也不认为这是当前工作的重点。
此外,我们并不是 Emacs 的狂热用户,因此并不特别在意观众是否能从操作中学习到如何使用 Emacs。我们的主要关注点仍然是功能实现和代码逻辑,而不是特定工具的使用教学。当然,如果未来有更好的可视化方式来辅助调试和开发,我们也可以考虑相应的改进方案。
预测 的编辑器 4coder
未来可能会考虑使用更好的编辑器,或许在某个编辑器(如 Fourth-Dimension)完善并适应代码需求之后,我们可以直接切换到那个环境。不过目前仍然在使用现有的工具,没有特别着急去更换编辑器的计划。
当然,如果有更高效、更适合当前开发需求的编辑器出现,并且能够满足我们的工作流,我们会考虑尝试并逐步过渡到新的工具,以提升开发效率和使用体验。
经常在使用他人的 API / 引擎时,会不得不“与之斗争”才能实现自己想要的功能。你认为这是否是一个信号,表明应该放弃该 API,尝试另一个,或者自己编写一个,而不是一直“斗争”?
使用他人的 API 或引擎时,常常会面临与其不兼容或不符合需求的情况,这时是否应该换一个 API 或自己编写代码,这个问题是一个非常复杂的决策,答案因情况而异。
通常情况下,使用像 Unity 这样的引擎时,可能会经常遇到与引擎本身的架构不匹配的情况。在这种情况下,你会做出一个权衡决策,即虽然 Unity 不能完美满足需求,但它提供了大量的现成代码,能帮助你节省开发时间。相比自己从头开始编写代码,尽管可能会遇到一些挑战或问题,使用 Unity 仍然可能是一个更好的选择,特别是对于经验较少的开发者来说。所以,有时候与其“战斗”,也许就意味着接受这类牺牲,节省其他时间来进行开发。
然而,若是 API 造成的困扰非常大,导致需要不断的工作去弥补其不足,甚至你最终不得不重写大部分代码,这时就需要认真考虑是否还要继续使用该 API。在这种情况下,继续使用 API 可能会适得其反,浪费更多时间。
对于一个新手游戏开发者来说,这种决策非常难做。因为没有经历过所有工具的使用,很难判断哪个是最适合的工具。像 Unreal Engine、Unity、Game Maker 等,都可能在不同的项目中面临一些“不适合”的问题。因此,选择一个合适的工具并不容易,开发者只能通过多次尝试和实践,才能理解各种工具的优劣和适用范围。
当开发者与 API 进行“斗争”时,这通常意味着该 API 无法以期望的方式工作,尽管如此,并不一定意味着应该放弃。更重要的是要意识到,每个决定背后都有成本和收益的权衡。如果最终判断自己编写某个组件或功能从头开始更为合适,那就应当去做。然而,这种判断往往需要积累一定的经验,因为对一个新手来说,很难清楚地判断在一个 API 下战斗是否值得,或者写自己的代码是否更高效。
另外,在某些情况下,问题并不完全在 API 上,而是开发者对 API 的理解不足。比如,当年不懂某些 API 时,可能会觉得它们设计得很糟糕或者难用,但随着对编程知识和多线程等概念的理解加深,开发者可能会发现这些 API 实际上是非常优秀的。因此,很多时候,API 的设计问题并不是设计本身有问题,而是开发者未能充分理解它的工作原理。
总的来说,选择是否换掉 API、使用其他工具或自己动手写代码,都是基于经验和项目需求的综合判断。
由于 draw_rectangle_quickly
代码位于单独的翻译单元中,你会用纯汇编编写它,而不是 C / 内联汇编吗?
讨论了是否将某些代码从C内建函数(intrinsics)转换为其他方式实现。提到,虽然将代码转为其他方式是一项有趣的练习,但在游戏发布之前并不会去做这样的改动。计划是尽量保持当前的实现方式,确保游戏顺利发布,而不是在此阶段进行复杂的重构。
你能详细讲解一下 I/O 完成端口(IOCP)吗?你刚才提到它了
讨论了I/O完成端口(I/O Completion Port)API,提到这个话题需要花费较长时间进行详细讲解,因此决定将其留到另一天再讨论。因为时间有限,无法在当前的讨论中深入解释为何I/O完成端口API是一个好的API。
相关文章:
游戏引擎学习第191天
回顾并制定今天的计划 最近几天,我们有一些偏离了原计划的方向,主要是开始了一些调试代码的工作。最初我们计划进行一些调试功能的添加,但是随着工作的深入,我们开始清理和整理调试界面的呈现方式,以便能够做一些更复…...
Git撤回操作全场景指南:未推送与已推送,保留和不保留修改的差异处理
一、未推送到远程仓库的提交(仅本地存在) 特点:可直接修改本地提交历史,不会影响他人 1. 保留修改重新提交 git reset --soft HEAD~1 # 操作效果: # - 撤销最后一次提交 # - 保留工作区所有修改 # - 暂存区内容保持…...
Java 贪吃蛇游戏
这段 Java 代码实现了一个经典的贪吃蛇游戏。玩家可以使用键盘的上下左右箭头键控制蛇的移动方向,蛇会在游戏面板中移动并尝试吃掉随机生成的食物。每吃掉一个食物,蛇的身体会变长,玩家的得分也会增加。如果蛇撞到自己的身体或者撞到游戏面板…...
QT图片轮播器(QT实操学习2)
1.项目架构 1.UI界面 2.widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>#define TIMEOUT 1 * 1000 QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent n…...
mac m1/m2/m3 pyaudio的安装
google了很多方法,也尝试了 issue68的方法, 但是均失败了,但是问deepseek竟然成功了,下面是deepseek r1给出的方法。在M3 pro芯片上可以成功运行. 安装homebrew /bin/bash -c "$(curl -fsSL https://raw.githubusercontent…...
Appium中元素定位的注意点
应用场景 了解这些注意点可以以后在出错误的时候,更快速的定位问题原因。 示例 使用 find_element_by_xx 或 find_elements_by_xx 的方法,分别传入一个没有的“特征“会是什么结果呢? 核心代码 driver.find_element_by_id("xxx") drive…...
《深入探索 Python 数据分析:用 Pandas 高效处理与可视化大型数据集》
《深入探索 Python 数据分析:用 Pandas 高效处理与可视化大型数据集》 引言:从零到分析高手 数据是当代社会最宝贵的资源,而数据分析技能是现代职业人不可或缺的一部分。在数据科学的领域中,Python 已成为当之无愧的“首选语言”,其强大的生态系统和简洁的语法让人如虎添…...
[GWCTF 2019]我有一个数据库1 [CVE phpMyAdmin漏洞]
扫出来一些东西 访问/phpmyadmin 发现界面 这里用到了CVE-2018-12613,光速学习 出现漏洞的代码是: $target_blacklist array (import.php, export.php );// If we have a valid target, lets load that script instead if (! empty($_REQUEST[targe…...
spring 常用注解区别及使用场景
1. 组件注册注解 Bean 作用:用于方法上,表示该方法返回的对象由Spring容器管理。通常用于配置类(Configuration)中,注册第三方库或自定义的Bean。 使用场合: 当你需要将非Spring管理的类(如第…...
【后端】【Django】信号使用详解
Django post_save 信号使用详解(循序渐进) 一、信号的基本概念 Django 的 信号(Signal) 允许不同部分的代码在发生某些事件时进行通信,而不需要直接调用。这种机制可以解耦代码,让不同的模块独立工作。 …...
ML算法数学概念
交叉熵损失(Cross-Entropy Loss) 是机器学习和深度学习中常用的一种损失函数,主要用于衡量两个概率分布之间的差异。它在分类问题中(尤其是多分类问题)被广泛使用,因为它能够有效地评估模型预测的概率分布与…...
wps 怎么显示隐藏文字
wps 怎么显示隐藏文字 》文件》选项》视图》勾选“隐藏文字” wps怎么设置隐藏文字 wps怎么设置隐藏文字...
Vue3 其它API Teleport 传送门
Vue3 其它API Teleport 传送门 在定义一个模态框时,父组件的filter属性会影响子组件的position属性,导致模态框定位错误使用Teleport解决这个问题把模态框代码传送到body标签下...
亚马逊玩具品类技术驱动型选品策略:从趋势洞察到合规基建
一、全球玩具电商技术演进趋势 (技术化重构原市场背景) 数据可视化分析:通过亚马逊SP-API抓取2023年玩具品类GMV分布热力图 监管技术升级: 美国CPSC启用AI质检系统(缺陷识别准确率92.7%) 欧盟EPR合规接口…...
SpringBoot3+EasyExcel通过WriteHandler动态实现表头重命名
方案简介 为了通过 EasyExcel 实现动态表头重命名,可以封装一个方法,传入动态的新表头名称列表(List<String>),并结合 WriteHandler 接口来重命名表头。同时,通过 EasyExcel 将数据直接写入到输出流…...
PHY——LAN8720A 寄存器读写 (二)
文章目录 PHY——LAN8720A 寄存器读写 (二)工程配置引脚初始化代码以太网初始化代码PHY 接口实现LAN8720 接口实现PHY 接口测试 PHY——LAN8720A 寄存器读写 (二) 工程配置 这里以野火电子的 F429 开发板为例,配置以太网外设 这里有一点需要注意原理图 RMII_TXD0…...
HTML5和CSS3的一些特性
HTML5 和 CSS3 是现代网页设计的基础技术,它们引入了许多新特性和功能,极大地丰富了网页的表现力和交互能力。 HTML5 的一些重要特性包括: 新的语义化标签: HTML5 引入了一些重要的语义化标签如 <header>, <footer>, <articl…...
sass报错,忽略 Sass 弃用警告,降级版本
最有效的方法是创建一个 .sassrc.json 文件来配置 Sass 编译器。告诉 Sass 编译器忽略来自依赖项的警告消息。 解决方案: 1. 在项目根目录创建 .sassrc.json 文件: {"quietDeps": true }这个配置会让 Sass 编译器忽略所有来自依赖项&#x…...
DeepSeek+Kimi:PPT制作的效率革命
摘要:传统PPT制作面临模板选择困难、内容逻辑混乱、设计排版能力有限以及反复修改等问题。DeepSeek和Kimi两款AI工具的组合为PPT制作提供了全新的解决方案。DeepSeek擅长内容生成与逻辑推理,能够快速生成高质量的PPT大纲和内容;Kimi则专注于长…...
transformers中学习率warmup策略具体如何设置
在使用 get_linear_schedule_with_warmup(如 Hugging Face Transformers 库中的学习率调度器)时,参数的合理设置需要结合 数据量(dataset size)、批次大小(batch size) 和 训练轮数(…...
linux实现rsync+sersync实时数据备份
1.概述 rsync(Remote Sync) 是一个Unix/linux系统下的文件同步和传输工具 2.端口和运行模式 tcp/873 采用C/S模式(客户端/服务器模式) 3.特点 可以镜像保存整个目录和文件第一次全量备份(备份全部的文件),之后是增量备份(只备份变化的文件) 4. 数…...
CTF类题目复现总结-[MRCTF2020]ezmisc 1
一、题目地址 https://buuoj.cn/challenges#[MRCTF2020]ezmisc二、复现步骤 1、下载附件,得到一张图片; 2、利用010 Editor打开图片,提示CRC值校验错误,flag.png应该是宽和高被修改了,导致flag被隐藏掉;…...
『Linux』 第十一章 线程同步与互斥
1. 线程互斥 1.1 进程线程间的互斥相关背景概念 临界资源:多线程执行流共享的资源就叫做临界资源临界区:每个线程内部,访问临界资源的代码,就叫做临界区互斥:任何时刻,互斥保证有且只有一个执行流进入临界…...
【数据结构】队列
目录 一、队列1、概念与结构2、队列的实现3、队列的初始化4、打印队列数据5、入队6、销毁队列7、队列判空8、出队9、取队头、队尾数据10、队列中有效元素个数 二、源码 个人主页,点击这里~ 数据结构专栏,点击这里~ 一、队列 1、概念与结构 概念&#x…...
【导航定位】GNSS数据协议-RINEX OBS
RINEX协议 RINEX(Receiver INdependent EXchange format,与接收机无关的交换格式)是一种在GPS测量应用中普遍采用的标准数据格式,该格式采用文本文件形式(ASCII码)存储数据数据记录格式与接收机的制造厂商和具体型号无关。目前RINEX版本已经发布到了4.x…...
Qt中的事件循环
Qt的事件循环是其核心机制之一,它是一种消息处理机制,负责处理各种事件(如用户输入、定时器、网络请求等)的分发和处理。Qt中的事件循环是一个持续运行的循环,负责接收事件并将它们分发给相应的对象进行处理。当没有事件需要处理时࿰…...
Android并发编程:线程池与协程的核心区别与最佳实践指南
1. 基本概念对比 特性 线程池 (ThreadPool) 协程 (Coroutine) 本质 Java线程管理机制 Kotlin轻量级并发框架 最小执行单元 线程(Thread) 协程(Coroutine) 创建开销 较高(需分配系统线程资源) 极低(用户态调度) 并发模型 基于线程的抢占式调度 基于协程的协作式调度 2. 核心差异…...
吴恩达深度学习复盘(2)神经网络的基本原理轮廓
笔者注 这两节课主要介绍了神经网络的大的轮廓。而神经网络基本上是在模拟人类大脑的工作模式,有些仿生学的意味。为了便于理解,搜集了一些脑神经的资料,这部分是课程中没有讲到的。 首先要了解一下大脑神经元之间结构。 细胞体࿱…...
【redis】集群 数据分片算法:哈希求余、一致性哈希、哈希槽分区算法
文章目录 什么是集群数据分片算法哈希求余分片搬运 一致性哈希扩容 哈希槽分区算法扩容相关问题 什么是集群 广义的集群,只要你是多个机器,构成了分布式系统,都可以称为是一个“集群” 前面的“主从结构”和“哨兵模式”可以称为是“广义的…...
计算机组成原理笔记(六)——2.2机器数的定点表示和浮点表示
计算机在进行算术运算时,需要指出小数点的位置,根据小数点的位置是否固定,在计算机中有两种数据格式:定点表示和浮点表示。 2.2.1定点表示法 一、基本概念 定点表示法是一种小数点的位置固定不变的数据表示方式,用于表示整数或…...
将树莓派5当做Ollama服务器,C#调用generate的API的示例
其实完全没这个必要,性能用脚后跟想都会很差。但基于上一篇文章的成果,来都来了就先简单试试吧。 先来看看这个拼夕夕上五百多块钱能达到的效果: 只要对速度没要求,那感觉就还行。 Ollama默认只在本地回环(127.0.0…...
MYSQL数据库(一)
一.数据库的操作 1.显示数据库 show databases; 2.创建数据库 create database 数据库名; 3.使用数据库 use 数据库名; 4.删除数据库 drop database 数据库名; drop database if exists 数据库名; 二.表的操作 1.显示所有表 show tables; 2.查看表结构 des…...
Python Cookbook-4.15 字典的一键多值
任务 需要一个字典,能够将每个键映射到多个值上。 解决方案 正常情况下,字典是一对一映射的,但要实现一对多映射也不难,换句话说,即一个键对应多个值。你有两个可选方案,但具体要看你怎么看待键的多个对…...
IDEA 终端 vs CMD:为什么 java -version 显示的 JDK 版本不一致?
前言:离谱的 JDK 版本问题 今天遇到了一个让人抓狂的现象:在 Windows 的 CMD 里输入 java -version 和在 IntelliJ IDEA 终端输入 java -version,居然显示了不同的 JDK 版本! 本以为是环境变量、缓存或者 IDEA 设置的问题&#x…...
Flask登录页面后点击按钮在远程CentOS上自动执行一条命令
templates文件夹和app.py在同一目录下。 templates文件夹下包括2个文件:index.html login.html app.py代码如下: import os import time from flask import Flask, render_template, request, redirect, session, make_response import mysql.con…...
深度解析:文件夹变白色文件的数据恢复之道
在数字化时代,数据的重要性不言而喻。然而,当我们在使用计算机时,偶尔会遇到一些棘手的问题,其中“文件夹变白色文件”便是一个令人困惑且亟待解决的难题。这一现象不仅影响了文件的正常访问,更可能隐藏着数据丢失的风…...
【Matlab】-- 基于MATLAB的飞蛾扑火算法与反向传播算法的混凝土强度预测
文章目录 文章目录 01 内容概要02 MFO-BP模型03 部分代码04 运行结果05 参考文献06 代码下载 01 内容概要 本资料介绍了一种基于飞蛾扑火算法(Moth Flame Optimization, MFO)与反向传播算法(Backpropagation, BP)的混凝土强度预…...
【Python实例学习笔记】图像相似度计算--哈希算法
【Python实例学习笔记】图像相似度计算--哈希算法 一、哈希算法的实现步骤:二、对每一步都进行注解的代码 一、哈希算法的实现步骤: 1、缩小尺寸: 将图像缩小到8*8的尺寸,总共64个像素。这一步的作用是去除图像的细节,…...
2025DevSecOps标杆案例|智能制造国际领导厂商敏捷安全工具链实践
某智能制造国际领导厂商是涵盖智能家居、楼宇科技,工业技术、机器人与自动化和数字化创新业务五大业务板块为一体的全球化科技集团,连续入选《财富》世界500强,每年为全球超过4亿用户、各领域的重要客户与战略合作伙伴提供产品和服务。 数智化…...
【YOLOv11】目标检测任务-实操过程
目录 一、torch环境安装1.1 创建虚拟环境1.2 启动虚拟环境1.3 安装pytorch1.4 验证cuda是否可用 二、yolo模型推理2.1 下载yolo模型2.2 创建模型推理文件2.3 推理结果保存路径 三、labelimg数据标注3.1 安装labelimg3.2 解决浮点数报错3.3 labelimg UI界面介绍3.4 数据标注案例…...
第十七章:Python数据可视化工工具-Pyecharts库
一、Pyecharts简介 资源绑定附上完整资源供读者参考学习! Pyecharts是一个基于百度开源可视化库ECharts的Python数据可视化工具,支持生成交互式的HTML格式图表。相较于Matplotlib等静态图表库,Pyecharts具有以下优势: 丰富的图表…...
解决【vite-plugin-top-level-await】 插件导致的 Bindings Not Found 错误
解决【vite-plugin-top-level-await】 插件导致的 Bindings Not Found 错误 环境设置 操作系统: macOS硬件平台: M1 Pro前端框架: Vue 3Node.js 版本: 20 在使用 Vue 项目时,我们尝试集成 vite-plugin-top-level-await 插件以支持顶层 await 语法。然而ÿ…...
《八大排序算法》
相关概念 排序:使一串记录,按照其中某个或某些关键字的大小,递增或递减的排列起来。稳定性:它描述了在排序过程中,相等元素的相对顺序是否保持不变。假设在待排序的序列中,有两个元素a和b,它们…...
六十天前端强化训练之第三十七天之Docker 容器化部署实战指南(大师级详解)
欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗,谢谢大佬! 目录 一、Docker 核心知识体系 1.1 容器革命:改变开发方式的技术 1.2 Docker 三剑客 1.3 Docker 生命周期管理 1.4 关键命令详解 二、前端容器化实战案例ÿ…...
RabbitMQ--延迟队列事务消息分发
目录 1.延迟队列 1.1应用场景 1.2利用TTL死信队列模拟延迟队列存在的问题 1.3延迟队列插件 1.4常见面试题 2.事务 2.1配置事务管理器 3.消息分发 3.1概念 3.2应用场景 3.2.1限流 3.2.2负载均衡 1.延迟队列 延迟队列(Delayed Queue),即消息被发送以后, 并…...
列表,元组,字典,集合,之间的嵌套关系
在 Python 中,列表、元组、字典和集合的嵌套关系需要遵循各自的特性(如可变性、可哈希性)。以下是它们之间的嵌套规则、示例和典型应用场景的详细梳理: 1. 列表(List)的嵌套 特性: 可变、有序…...
【行驶证识别】批量咕嘎OCR识别行驶证照片复印件图片里的文字信息保存表格或改名字,基于QT和腾讯云api_ocr的实现方式
项目背景 在许多业务场景中,如物流管理、车辆租赁、保险理赔等,常常需要处理大量的行驶证照片复印件。手动录入行驶证上的文字信息,像车主姓名、车辆型号、车牌号码等,不仅效率低下,还容易出现人为错误。借助 OCR(光学字符识别)技术,能够自动识别行驶证图片中的文字信…...
鸿蒙HarmonyOS NEXT设备升级应用数据迁移流程
数据迁移是什么 什么是数据迁移,对用户来讲就是本地数据的迁移,终端设备从HarmonyOS 3.1 Release API 9及之前版本(单框架)迁移到HarmonyOS NEXT(双框架)后保证本地数据不丢失。例如,我在某APP…...
MCP从零开始
MCP简介 MCP,全称是Model Context Protocol,模型上下文协议,由Claude母公司Anthropic于去年11月正式提出。MCP解决的最大痛点就是Agent开发中调用外部工具的技术门槛过高的问题。 能调用外部工具,是大模型进化为智能体Agent的关…...
Three.js 快速入门教程【十九】CSS2DRenderer(CSS2D渲染器)介绍,实现场景中物体或设备标注标签信息
系列文章目录 Three.js 快速入门教程【一】开启你的 3D Web 开发之旅 Three.js 快速入门教程【二】透视投影相机 Three.js 快速入门教程【三】渲染器 Three.js 快速入门教程【四】三维坐标系 Three.js 快速入门教程【五】动画渲染循环 Three.js 快速入门教程【六】相机控件 Or…...