回顾并为当天的工作设定阶段目标
今天的工作方向是集中在用户界面(UI)代码的实现,虽然这个项目本身并不涉及大量的UI设计,但我们会暂时偏离原来的任务,进行一个小小的探索。主要是因为在之前的工作中,UI部分并没有太多涉及,而这对于那些希望了解如何在自己的项目中实现UI代码的人来说,可能会有帮助。虽然这个项目本身不是一个以UI为主的游戏,比如像模拟城市、文明或者模拟人生那样有大量的UI界面,但我们仍然可以花点时间来讨论UI的实现,特别是如何实现调试UI,这对于游戏开发中调试和监控来说是非常重要的。
目标是让UI的实现更加简洁和灵活,以便以后可以快速增加新功能和界面,而不会给程序员带来过多的负担。在调试UI的实现上,重点是让它尽可能的简便和高效,这样即便后续需要扩展,也能通过简单的代码修改和调整来实现新的功能,而不需要大规模重构。
在昨天的工作中,我们已经抽象了交互代码,这让我们能够更好地管理和复用这些代码。接下来的工作是让UI绘制部分更加顺畅,确保调试UI的绘制能够正确显示。今天,我们会重点关注调试UI的绘制实现。这个任务可以分为两个部分:一部分是继续改善UI绘制的方式,另一部分是关于如何处理对象的深拷贝问题。当前我们有一个问题,就是当复制一个对象(如渲染器)时,它只是做了引用复制,导致多个对象间的数据完全同步,这样可能会产生不希望的效果,我们需要解决这个问题。
不过,考虑到时间安排,我们决定暂时专注于调试UI的绘制部分,确保它能够正常工作,等这个部分完成后再考虑其他改进措施。因此,今天的任务主要是让绘制部分的代码运行起来,并确保它符合我们的期望。
查看我们当前如何绘制内容 (game.debug.cpp)
今天的目标是改进目前的绘制代码,避免它显得过于临时和零散。当前的绘制方式是通过一个简单的 for
循环,遍历所有需要绘制的类型,并在循环中直接编写绘制代码。虽然这种方式在早期阶段能快速实现需求,但随着功能的增加,代码变得越来越杂乱,维护和扩展变得更加困难。
最初在处理交互时,虽然我们也使用了类似的方法,但之后我们创建了一个 debug interaction
类型来封装交互的内容。这样做的好处是可以将交互逻辑抽象出来,避免重复代码,同时也方便了对交互状态(例如高亮)进行管理。通过这种方式,代码变得更加整洁、清晰,也提高了灵活性和可维护性。
接下来,我们计划采用类似的方式重构绘制代码。目前的绘制代码也比较零散,涉及到计算各个元素的位置、大小等操作,并且这些操作的实现较为分散。为了避免重复和冗余的代码,我们打算将这些绘制逻辑抽取到一个共享的地方,这样可以统一管理并保证代码的整洁性,同时也便于后续的修改和扩展。
通过这种重构,绘制代码将变得更加模块化,可以复用的部分被提取出来,代码的可读性和可维护性也会大大提高。此外,这种改进也能让我们在添加新的绘制元素时,不需要重复编写相似的代码,而是可以直接调用已经封装好的方法,保持代码的简洁性和一致性。
引入布局结构体以包含局部变量 (game_debug.cpp)
今天的工作重点是将现有的重复代码提取出来,进行简化和重构,以便更高效地管理和修改。在当前的代码中,有多个地方在处理类似的功能,例如绘制位图和计数器线程列表等。这些功能大多数情况下都从相同的位置开始,而这些位置通常都是相同的,然而目前的代码在不同地方重复了相似的内容。举个例子,如果我们想要修改这些内容的缩进方式,必须在多个地方进行修改,这样就暴露了代码重复的问题。
为了避免这种冗余和潜在的维护问题,我们决定将这些重复的部分提取出来,创建一个共同的结构体来管理这些变量。具体来说,我们从当前的代码中提取了深度(depth)、行进量(line advance)以及相关的坐标等变量,将它们放入一个结构体中。通过这种方式,所有需要使用这些变量的函数,都可以通过传递该结构体来访问这些数据。
这意味着,我们不仅仅是将这些变量放入结构体中,而是希望通过这种方式将它们封装起来,使得这些数据能方便地传递给其他函数。这样做的好处是,原先的变量分散在不同的地方,现在我们只需要通过结构体传递一次,就可以让函数知道这些数据的状态。通过这种方式,我们实现了类似闭包(closure)一样的效果,使得代码更加模块化和简洁。
总之,今天的工作是将一些重复的逻辑抽取出来,封装成结构体,这样不仅避免了代码重复,还为以后可能的修改和扩展提供了便利。这种做法让后续的功能扩展和修改变得更加灵活和高效。
引入 Advance
目前,我们正在优化和整理现有的绘制逻辑,使代码更具条理性,同时减少冗余部分。当前的代码存在一些问题,比如在处理边界(bounds)时有些不必要的复杂性。这种复杂性主要来源于我们在尝试复用代码路径,以确保推进逻辑(advancement code)的一致性。然而,这种方式并不必要,因为实际上没有理由强制所有对象都使用相同的边界推进方式,每个对象甚至可能有多个边界(bounds)。
因此,我们的目标是重新组织代码,使边界的处理更加直观。首先,我们将边界的计算逻辑移入局部作用域,确保它们在局部函数内部处理,而不是分散在整个代码块中。这样,我们可以减少边界处理逻辑的分散性,使代码更易维护。
在文本绘制方面,之前的实现方式较为麻烦,因为文本的边界需要特定处理,而这种处理方式导致代码在多个地方做了额外的适配工作。现在,我们正在调整代码结构,使这些适配工作更自然地融入整个系统,不再需要额外的"弯曲"代码逻辑来适配不同的情况。
接下来,我们引入了一种新的推进方式,即创建一个 Advance
函数,并将其应用到布局(layout)对象上。这个 Advance
函数的核心思想是让对象自己负责边界推进,而不是在外部显式地计算和更新 Y 坐标。具体来说,我们在 Advance
函数中接收 layout
和 bounds
,然后直接更新 layout
的 Y 坐标,使其等于 bounds
的最小角(min corner)。这样,我们在主代码逻辑中不再需要手动逐步更新坐标,而是只需调用 Advance
函数即可完成相应的推进操作。
随着 Advance
函数的引入,我们在代码的各个地方逐步替换了原本的手动推进逻辑,使代码更加清晰、模块化。例如,所有涉及 layout
相关的变量,如 layout.addx
、layout.aty
、layout.spacingX
、layout.spacingY
、layout.depth
以及 layout.lineAdvance
,现在都可以通过 layout
结构体进行集中管理,而不需要在各处分别维护。这种方式减少了重复代码,提高了可维护性,并为未来可能的修改和扩展提供了便利。
在代码迁移的过程中,我们逐步处理了编译错误,确保所有相关变量都正确引用 layout
结构体。这样一来,代码逻辑更加清晰,绘制与布局推进的逻辑分离,使得后续的开发更加灵活和高效。
整体而言,我们的工作重点是简化边界计算逻辑、引入 Advance
机制,并将所有相关变量集中到 layout
结构体中进行管理。这样不仅减少了代码重复,还使得布局推进的逻辑更加直观和易于维护,从而优化整个 UI 系统的开发流程。
运行游戏并发现它和之前一样
目前,我们已经完成了对代码的重构,使其在逻辑上保持与之前相同的功能,同时优化了代码的结构和可读性。从目前的运行结果来看,代码的行为与之前保持一致,这表明我们的调整是合理的,未引入额外的错误或影响原有功能的变动。
具体来说,我们引入了一种新的 Advance
机制,使得 layout
结构体可以更自然地管理和推进布局状态,避免了之前代码中手动计算和更新坐标的繁琐操作。通过将 layout.addx
、layout.aty
、layout.spacingX
、layout.spacingY
、layout.depth
和 layout.lineAdvance
等变量集中管理,我们减少了代码冗余,提高了可维护性。
此外,我们调整了边界(bounds)的计算方式,使其局部化,避免了边界计算逻辑在代码各处分散的情况。现在,边界的计算逻辑封装在 Advance
函数内部,确保了每个对象能够独立管理自己的边界,而不是依赖全局代码进行推进。这种改进不仅减少了代码重复,还使得逻辑更加直观。
目前,所有的调整已经完成,并且测试结果表明代码功能仍然保持一致,这说明我们的改进没有破坏原有的功能。接下来,我们可以继续优化代码,使其更具扩展性和可读性。
引入 PlaceRectangle (game_debug.cpp)
现在,我们已经有了 layout
结构体,并且所有相关的代码都开始使用它。接下来的目标是进一步优化代码,提取出明显重复的部分,使其更加模块化和可复用。
在检查 counter thread list
相关的代码时,我们发现其中的 dimension(尺寸) 计算逻辑是手写的,但实际上这个计算逻辑是通用的,并不需要每个地方都单独实现。因此,我们可以将其提取出来,使任何具有 dimension
属性的对象都能够复用这一计算方法。
改进方案
我们计划实现一个 PlaceRectangle
函数,它的作用是根据 dimension
的信息,返回适合绘制的 矩形区域(rectangle) 。这样,我们就可以避免手动计算 minCorner
和 maxCorner
的重复操作,使代码更加简洁和模块化。
-
原始代码的问题
- 代码中多次手动计算
minCorner
和maxCorner
,然后再基于它们创建rectangle
,导致重复逻辑。 - 这些计算完全依赖于
layout
的信息,如layout.atX
、layout.atY
,没有必要在多个地方重新计算。
- 代码中多次手动计算
-
新的
PlaceRectangle
设计PlaceRectangle
函数接收dimension
作为参数,并结合layout
的当前状态,自动计算矩形的位置。- 该函数会返回
minCorner
和maxCorner
,提供完整的矩形定义。 - 这样,调用者无需关心具体的计算细节,而是直接得到结果,提高了代码的可读性和复用性。
代码优化过程
- 我们在
layout
结构体中增加PlaceRectangle
方法,使其能够根据传入的dimension
自动计算minCorner
和maxCorner
。 - 计算逻辑保持与之前相同,但被封装在一个函数中,以便复用。
- 代码中所有手动计算
minCorner
和maxCorner
的地方,现在可以直接调用PlaceRectangle
,避免重复代码,提高代码整洁度。 PlaceRectangle
方法的返回值是rectangle(minCorner, maxCorner)
,这样我们可以直接使用它,而不需要手动计算每个角的坐标。
最终优化效果
- 减少代码重复 :所有的
minCorner
和maxCorner
计算逻辑被封装在PlaceRectangle
中,不需要每次手动写这些计算。 - 提高代码可读性 :现在代码逻辑更直观,调用
PlaceRectangle
即可获得正确的矩形区域,而不需要手动计算边界。 - 增强可维护性 :如果以后需要修改
rectangle
的计算方式,只需要修改PlaceRectangle
,而不需要到每个调用位置进行修改。
通过这些优化,我们成功消除了大量冗余代码,使 layout
结构更加清晰和易用,同时提升了整个代码的模块化程度。
运行游戏并发现它正常工作
理论上,现在所有的 性能分析(profile) 相关代码都已经经过优化后的 layout
处理,并且应该可以正常工作。经过测试,结果符合预期,所有功能都如预期运行,没有出现问题,这证明了优化方案的可行性。
这意味着,我们成功地将 绘制布局的逻辑 进行了提取和封装,使得 layout
结构能够更高效地管理界面元素的 尺寸计算、对齐方式以及位置确定 。同时,所有涉及 性能分析 的界面绘制代码现在都使用了这一改进后的 layout
,避免了手动计算 边界(bounds),大幅减少了冗余代码,提高了可维护性和可读性。
这是优化的第一步,接下来可以考虑进一步改进,使得 layout
机制更加通用和灵活。
使用 PlaceRectangle 计算元素的布局
接下来,我们的 第二步优化 目标是复用刚刚提取的 layout
代码,使其适用于 位图(bitmap) 的布局计算,从而减少代码重复,提高整体一致性。
当前在 位图缩放(bitmap scale) 计算过程中,我们进行了一些 尺寸修正(dimension correction) ,比如 bitmap_display_dimx
之类的计算。然而,这些计算并不真正依赖 位图的具体位置(Min corner) ,而是仅仅需要确定位图的 尺寸(size) 。换句话说,我们之前传递 Min corner 其实是多余的,因为最终的布局计算才决定了它的实际位置。
因此,我们可以在 尺寸修正 之后,直接调用 layout
相关代码,让 layout
机制帮我们完成 位图的布局计算 。这样可以减少重复的逻辑,让所有布局相关的计算保持一致,并确保代码更加 简洁、可维护且易于扩展。
在这一优化过程中,我们还需要 正确处理 size P(位图尺寸) ,并使用 layout
计算出来的 bounds(边界) 来确定 位图最终的位置 。这样,绘制位图时就可以直接使用 GetMinCorner(bounds)
,而不再需要手动计算位置。
这样优化后,我们的 位图布局 逻辑将会更加规范化,并与其他界面元素的布局计算保持一致,使整个代码结构更加清晰合理。

运行游戏并显示使用相同布局的元素
我们成功压缩了代码,这是一项很好的改进。现在,无论是 计数器线程列表(counter thread list) 还是 位图(bitmap) 的布局计算,都已经使用了相同的 layout 例程(layout routine)。这正是我们期望的结果。
通过这样的优化,任何需要类似布局功能的代码,都可以直接调用这一 layout 机制 ,自动完成 边界计算(bounds calculation),而不需要再手动计算具体位置。这不仅减少了代码重复,还大大降低了维护成本,使整个代码结构更加清晰、易用、可扩展。
接下来,我们希望进一步优化和调整代码,使其更加通用化和易用化。
考虑提取 SizeP 计算的难度 (game_debug.cpp)
我们希望进一步扩展 layout 机制 ,使其可以自动处理 SizeBox(尺寸框) 。在之前的实现中,我们发现 计数器线程列表(counter thread list) 和 位图(bitmap) 视图的 SizeBox 逻辑是相同的。因此,如果 layout 机制 能够统一处理 SizeBox,那么在以后添加类似的功能时,我们就能直接复用代码,而不需要手动实现这些细节。
目标
在一个 良好的 UI 设计 中,开发者应该能够 方便地 选择现有组件,而不需要关心底层的实现细节。因此,我们希望做到:
- 任何组件 只要需要 SizeBox ,都可以 直接声明,而不需要额外处理布局逻辑。
- 自动计算 SizeBox 的边界(bounds),确保布局系统能够正确放置它们。
- 减少代码重复 ,让 layout 机制 统一管理这些 SizeBox。
当前遇到的问题
-
SizeBox 交互冲突:
- 在布局中,我们发现 SizeBox 可能会与 bitmap 组件的交互区域发生冲突。
- 例如,当我们尝试拖动 SizeBox 时,系统可能会 误认为 我们正在操作 bitmap,从而触发错误的交互行为。
-
Hit Testing(点击检测)问题:
- 目前,我们的 点击检测 (hit test)是按顺序执行的,先检测到的对象优先级较低 ,而 后检测到的对象优先级较高。
- 这意味着,如果 bitmap 的 hit test 发生在 SizeBox 之前,那么 SizeBox 的交互可能会被覆盖。
-
可能的解决方案:
- 引入 Z 轴深度(Z-value) :通过给不同的 UI 组件分配 不同的 Z 轴层级,让更高层级的组件(如 SizeBox)可以正确拦截交互事件。
- 优化交互检测机制 :改进 hit test 逻辑,确保 鼠标指针位于 SizeBox 内部时,优先触发 SizeBox 的交互,而不是被底层的 bitmap 误捕获。
- 调整架构 :确保 layout 机制 在计算 SizeBox 的边界 时,同时考虑 交互优先级,避免错误的 UI 行为。
下一步计划
- 优化 layout 机制,使其可以自动计算 SizeBox 的布局信息。
- 调整 hit testing 逻辑,确保 SizeBox 能正确拦截交互事件。
- 测试新的布局机制,观察是否能解决 UI 交互问题。



简化并剥离 DEBUGDrawMainMenu 中的计算 (game_debug.cpp)
我们正在尝试优化 布局系统(layout system) ,让其更加模块化、简洁,并减少代码重复。我们的目标是创建一个 通用的布局组件(layout element),能够自动处理大小调整(sizable)、交互(interaction)等行为,从而简化 UI 代码。
核心思路
-
定义通用的布局元素(element)
- 通过
begin_element(Rectangle, dimensions)
创建一个矩形布局元素。 - 通过
EndElement()
结束该元素,并返回是否处理了鼠标交互。
- 通过
-
支持可调整大小(sizable)
- 通过
、MakeElementSizable()
让当前元素支持尺寸调整。 - 这样,任何 UI 组件只需调用这一方法,就能具备尺寸调整能力,而无需额外逻辑。
- 通过
-
优化交互处理(interaction handling)
EndElement()
返回一个布尔值,指示当前元素是否捕获了鼠标交互。- 这样可以避免引入 Z 轴排序(Z-value sorting) 这类复杂的机制,同时保证交互处理的正确性。
实现步骤
-
改进布局结构
- 目前的代码中,布局信息(如尺寸、边界)需要在 元素结束后(EndElement) 才能确定。
- 我们调整逻辑,使其能够在元素创建时就传递尺寸地址,确保后续计算能正确使用这些尺寸信息。
-
消除多余代码
- 通过新的
element
机制,可以消除大量 手动处理尺寸 和 交互 的代码。 - 例如,原本需要手动计算
SizeBox
和interaction
,现在这些都可以由 layout 机制自动管理。
- 通过新的
-
优化文本布局
- 现在可以使用
begin_element()
直接传递文本尺寸(text_bounds.width
和layout_line_advance
),避免手动计算宽高。 - 交互逻辑也可以通过 默认交互参数(default_interaction) 直接传递,进一步减少代码量。
- 现在可以使用
当前优化成果
- 代码结构更清晰 :
- 现在的 UI 代码更紧凑,只需要调用
begin_element()
和EndElement()
,其余逻辑都交给 layout 机制 处理。
- 现在的 UI 代码更紧凑,只需要调用
- 减少代码重复 :
- 之前手动计算
SizeBox
、interaction
、layout_spacing
等的代码已被移除,全部由 layout 组件 统一管理。
- 之前手动计算
- 交互逻辑更可靠 :
- 通过
EndElement()
返回的交互信息,我们避免了 错误的鼠标捕获,无需额外的 Z 轴管理。
- 通过
下一步计划
- 测试新布局系统的兼容性 ,确保所有 UI 组件都能正确使用
begin_element()
和EndElement()
。 - 优化默认行为 ,例如是否需要显式传递
layout_spacing
,还是让layout 机制
自动管理这些值。 - 进一步精简代码 ,尝试将 文本渲染 也纳入
element
系统,以减少 UI 代码的复杂度。
引入 BeginElementRectangle、MakeElementSizable、DefaultInteraction 和 EndElement (game_debug.cpp)
-
函数概览
BeginElementRectangle
:返回一个布局元素,接收布局对象和指向矩形尺寸的指针。- 使元素可调整大小:
、MakeElementSizable
通过添加size_box
使元素可调整大小。 - 设置默认交互方式:
DefaultInteraction
将默认交互方式设为调试交互。 - 结束元素定义:
EndElement
标志着元素定义完成。
-
核心逻辑
目前所有逻辑都可以内联处理,无需推迟到
EndElement
调用后再执行。这种方式更简洁直观。- 变量
V2 dim
代表尺寸,并作为指针传入。 layout
变量存储布局信息。size
、interaction
、bounds
都初始化为空值,以便后续计算。
- 变量
-
计算元素的总尺寸
TotalDim
初始值为用户指定的dim
。- 若元素可调整大小,则在
TotalDim
基础上扩展一个额外区域(即size_box
)。 size_box
的尺寸可设定为固定值,如4px
。
-
计算矩形的边界
TotalMinCorner
和TotalMaxCorner
代表整个元素的最小/最大角点。InteriorMinCorner
和InteriorMaxCorner
代表元素的内部矩形区域。element_bounds
存储的是返回给用户的最终可用区域。
-
计算元素的交互区域
size_box
仅作用于矩形右下角,允许用户拖动调整大小。- 计算
size_box
位置时,InteriorMaxCorner
作为起点,TotalMaxCorner
作为终点。
-
渲染边框
- 通过绘制四条边框矩形来实现,分别是左边、右边、顶部和底部。
left_edge
和right_edge
通过TotalMinCorner
与InteriorMinCorner
定义。top_edge
和bottom_edge
通过TotalMinCorner
与InteriorMaxCorner
计算。
-
修正调试状态传递
debug_state
直接存入layout
,避免每次传递参数时重复操作。MouseP
也放入layout
,确保所有函数都能正确获取鼠标位置。
-
优化代码
- 移除
add_radius_to
,直接对dim
进行加法运算,提高简洁性。 getWidth
相关调用修正为GetDim x
和GetDim y
,确保计算正确性。push_rect
统一使用ElementBounds
作为绘制区域。
- 移除
整体来看,这部分代码主要完成了 UI 元素的初始化、尺寸调整、边界计算、交互区域定义以及边框绘制,并进行了部分优化以提高可读性和执行效率。
运行游戏并发现错误
我们目前实现了几个关键的 UI 相关函数,其中包括 begin_element_rectangle
,它接受一个布局(layout)和指向矩形尺寸的指针,返回一个布局元素。此外,我们还实现了 make_element_sizable
,用于添加可调整大小的功能,即为元素添加尺寸框(SizeBox)。同时,我们提供了 set_default_interaction
作为默认交互方式,并通过 EndElement
来结束元素的创建,表示该 UI 元素的所有属性已定义完成。
实现细节
我们决定将所有逻辑内联实现,而不是推迟到 EndElement
调用时再进行整合。这样可以在调用 EndElement
时直接构造 UI 元素,并确保所有计算在此时完成。
在 begin_element_rectangle
里:
layout
直接赋值。dim
赋值为传入的尺寸指针。size
指针初始化为空。interaction
设为默认交互方式(null
)。bounds
设为null
。
当 EndElement
被调用时:
- 计算元素的总尺寸(total dim),如果该元素是可调整大小的,则需要在原始尺寸的基础上增加一个尺寸框(SizeBox)。
- 计算总边界(total bounds),包括 UI 组件的整体区域。
- 计算内部边界(interior bounds),即实际内容区域,不包括尺寸框。
- 计算
minCorner
和maxCorner
,分别表示总边界和内部边界的最小、最大角坐标。 - 计算尺寸框的矩形区域,即
SizeBox
位置。
对于尺寸框(SizeBox):
- 计算它的具体位置,使其出现在元素的右下角。
- 确保其交互逻辑能够正确处理用户输入(如鼠标拖动等)。
在 EndElement
里,还需要处理 mouseP
(鼠标位置):
- 这需要从
layout
结构中获取,并确保它能够被正确传递到 UI 组件中。 - 这样可以确保
SizeBox
可以正确响应鼠标交互。
绘制边框
为了更清晰地展示 UI 组件,我们决定绘制元素边框:
- 左边框 :从
total minCorner
到interior minCorner
。 - 右边框 :从
interior maxCorner
到total maxCorner
。 - 上边框 :从
total minCorner Y
到interior minCorner Y
,在X
方向上覆盖整个宽度。 - 下边框 :从
interior maxCorner Y
到total maxCorner Y
,同样覆盖整个宽度。
此外,我们还计划在 SizeBox
处绘制一个额外的交互区域,使其更加清晰可见,并便于用户操作。
调试和优化
- 修正
get_width
和get_dim_y
等函数的调用,以匹配实际的数据结构。 - 通过
debugState
传递 UI 组件的调试信息,并优化其访问方式,以减少多次传递。 - 代码中可能存在一些小错误,例如
add_radius_to
计算不准确,我们优化了这部分逻辑,使其直接在dim
上进行简单的加法运算,而不使用add_radius_to
。
当前问题
- 可能在
SizeBox
计算时没有正确添加条件检查,导致某些情况下SizeBox
被错误绘制。 - 需要进一步测试
EndElement
内部的交互逻辑,确保尺寸调整功能能正确响应用户输入。
接下来,我们将进行调试,查看最终的 UI 渲染效果,看看是否还有逻辑问题需要修正。
仅当元素具有大小时才推送矩形,并停止让文本元素变得可调整大小 (game_debug.cpp)
在实现 UI 元素的过程中,我们遇到了一些问题,并进行了修正和优化。
问题分析与修正
我们发现,所有的 UI 计算(如边界计算、尺寸调整等)只有在元素具有 SizeBox
时才会执行。这意味着,如果某个元素本身并未被标记为可调整大小(sizable),那么所有这些计算都不应该发生。因此,我们需要确保:
- 只有在元素具有
SizeBox
时,才执行相关的计算和操作。 - 避免不必要的计算,以提高性能和代码的清晰度。
之前的问题在于,某些计算没有被正确地限制在 SizeBox
存在的情况下执行,导致了一些意外的行为。因此,我们需要确保:
- 如果
SizeBox
存在,则计算边界、尺寸和SizeBox
位置。 - 如果
SizeBox
不存在,则跳过这些计算,以避免错误。
错误定位
在排查过程中,我们发现了几个关键错误:
-
错误地将某些 UI 计算应用到了没有
SizeBox
的元素上- 这导致了一些无意义的计算,甚至可能影响其他 UI 元素的布局。
- 需要确保
SizeBox
相关代码只运行在sizable
的元素上。
-
未正确生成 UI 元素
- 在
element size
计算时,我们需要正确地检测SizeBox
是否存在,并据此决定是否推送矩形边界数据。 - 之前的代码错误地执行了
push all the wrecks
(推送所有矩形数据),即使元素本身并不支持尺寸调整,导致了一些意外行为。
- 在
-
逻辑错误:忘记正确标记元素为
sizable
- 代码逻辑上,我们需要正确地在元素创建时指定
sizable
属性,否则即使代码流程是正确的,仍然不会触发尺寸调整功能。 - 这一点属于操作失误(operator error),即手动设置时的疏忽。
- 代码逻辑上,我们需要正确地在元素创建时指定
修正方案
- 修正
if
语句,确保SizeBox
计算只在sizable
元素上执行。 - 优化
EndElement
逻辑,避免对非sizable
元素执行不必要的计算。 - 检查
element size
计算流程,确保它正确地推送矩形数据,仅在sizable
元素存在时执行推送。 - 在元素创建时正确设置
sizable
,以避免后续计算出现错误。
后续优化
- 增加调试输出,以便更快定位
SizeBox
相关的计算错误。 - 进一步优化
EndElement
逻辑,减少不必要的计算步骤,提高运行效率。 - 确保所有 UI 组件的
SizeBox
逻辑保持一致,以避免不同组件间出现不兼容问题。
接下来,我们将测试修改后的代码,确保 SizeBox
的计算和 UI 渲染逻辑正确无误。


添加 DefaultInteraction (game_debug.cpp)
在 UI 交互逻辑的实现过程中,我们现在需要添加默认的交互行为。
目标
- 为元素添加默认交互行为。
- 检测元素是否具有交互类型(interaction type),并根据该类型进行交互处理。
- 如果元素交互区域(bounds)包含当前交互位置,则更新调试状态(debug state)。
- 确保
SizeBox
在交互优先级上高于普通元素,即便它们不重叠,也要在交互优先级上优先处理SizeBox
。
实现方案
-
检测元素是否具有交互类型
- 通过
interaction.type
判断当前元素是否被赋予了交互类型。 - 如果
interaction.type
具有特定值,说明该元素支持交互,需要进一步处理。
- 通过
-
判断交互位置是否在元素的
bounds
内- 采用
IsInRectangle
方法,检测当前交互点是否落入元素的bounds
范围内。 bounds
指的是元素的内部区域,而非整个 UI 组件。
- 采用
-
更新调试状态
- 如果
bounds
内检测成功,则将debugStates.nextHotInteraction
设置为当前交互的元素。 - 这样可以跟踪当前正在被交互的 UI 组件,便于调试和优化。
- 如果
-
处理
SizeBox
交互优先级- 确保
SizeBox
交互优先级最高 ,即便SizeBox
与普通元素没有重叠,也要优先处理它的交互逻辑。 - 这样即使
SizeBox
和其他交互元素发生重叠,SizeBox
仍然能够正确响应交互事件。
- 确保
问题与解决
-
交互元素优先级管理
需要确保
SizeBox
在交互处理时优先级更高。这样即便某个普通 UI 元素有交互,SizeBox
仍然能正确捕获交互事件。 -
交互边界检测的优化
bounds
的检测逻辑需要保证足够精准,以避免误判交互区域。- 可以优化
IsInRectangle
逻辑,使其更高效地判断当前鼠标或触摸点是否落在bounds
内部。
-
调试工具的改进
debugStates.nextHotInteraction
需要正确更新,以便后续在调试工具中能够直观显示当前交互的元素。- 可以考虑加入额外的日志或 UI 提示,方便跟踪交互状态的变化。
后续优化方向
- 优化
SizeBox
与普通交互元素的优先级管理,使其适用于更复杂的 UI 场景。 - 改进
IsInRectangle
逻辑,提高交互检测的精确度和性能。 - 增加可视化调试工具,帮助开发者快速定位交互问题。
- 测试
SizeBox
在不同 UI 组件层级下的交互表现,确保它能够始终正确响应交互事件。
接下来,我们将进行测试,确保 SizeBox
交互的优先级正确,并观察 debugStates.nextHotInteraction
是否能准确反映当前交互状态。


运行游戏并看到一切正常工作
目前来看,一切都运行得相当顺利,比预期的效果要好很多,整个流程比预想中更顺畅,基本上没有遇到需要额外调试的问题,这点让人非常满意。
当前进展
- 交互逻辑和 UI 处理工作已经顺利完成。
size box
组件能够正常工作,并且整体布局逻辑已经独立拆分,使得代码结构更加清晰。- 所有 UI 元素的交互行为都能正确响应,测试结果良好。
进一步的扩展
目前已经具备了足够的基础来进行扩展,可以考虑以下方向:
-
改进
size box
的可操作性- 目前
size box
只是简单的框架,但可以进一步优化,比如:- 让
size box
拥有更丰富的操作方式,比如可以直接从不同的边或角进行调整大小。 - 甚至可以将其变成一个厚边框(thick frame),允许用户从多个方向调整尺寸。
- 让
- 目前
-
增加 UI 组件的通用属性
- 现在创建新 UI 组件的代码量已经大幅减少,可以轻松扩展 UI 元素的能力。
- 例如,可以为 UI 组件添加:
- 可移动(movable) 属性,使组件可以被拖拽。
- 可调整(resizable) 属性,使组件大小可变。
- 可旋转(rotatable) 属性,使组件支持旋转操作。
- 通过这种方式,可以实现一个更加模块化和灵活的 UI 系统。
-
优化布局逻辑
- 目前所有的布局代码已经完全拆分,使得扩展新的 UI 组件变得非常简单。
- 后续可以进一步优化,使得添加新 UI 组件时不需要修改已有的代码结构,只需增量添加新功能即可。
下一步计划
- 暂缓进一步开发,留待明天继续。
- 当前进度是一个很好的停顿点,因为基础架构已经搭建完成。
- 明天可以专注于增加新功能,比如让 UI 元素更具互动性,并支持更多自定义操作。
整体来说,整个系统已经变得非常灵活,现在可以非常轻松地添加新的 UI 组件,并赋予它们新的交互属性,使其具备更丰富的功能。