游戏引擎学习第88天

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

调查碰撞检测器中的可能错误

在今天的目标是解决一个可能存在的碰撞检测器中的错误。之前有人提到在检测器中可能有一个拼写错误,具体来说是在测试某个变量时,由于引入了一个新的变量而没有正确地使用它,导致出现了问题。为了验证这一点,需要检查 testP 变量的用法,看看它是否在代码中得到了合理的应用。

目前的代码显示,testP 并未被有效使用,而碰撞检测部分采用了直接查询的方式。怀疑原本的计划是希望将 testP 用于"SpeculativeCollide"中,但最终并没有这样做。因此,计划将 testP 纳入实际的碰撞检测流程中,并确保它能够与其他的检测逻辑正确配合。

另外,EntityGroundPoint 已不再是独立的变量,而是已经被整合到系统中,假设每个实体都应有一个地面点。因此,虽然可以传递 testP,但需要重新审视 GetEntityGroundPoint 的工作原理,以确保它能正确处理 testP。这就意味着需要对代码做进一步的调整,确保所有的碰撞测试都能按照预期工作。

将 TestP 传递给 SpeculativeCollide,并且将 MoverGroundPoint 基于 TestP

如果决定将 testP 传递给 SpeculativeCollide,目的是在此处检查是否可以执行相应的操作,那么就需要确保在代码中将 testP 正确使用。特别地,moveOrGroundPoint 这个函数应该基于 testP 来处理,这样可以确保在进行碰撞检测时能够得到准确的地面点。

如果之前的设计允许实体根据自己的需求返回地面点,那么就需要审视是否继续沿用这种方式,或者改为使用 testP 来进行统一的处理。这样做的目的是简化和标准化地面点的计算方式,并确保碰撞检测逻辑的一致性。

创建一个版本的 GetEntityGroundPoint,接受 ForEntityP

关于 GetEntityGroundPoint 函数的设计,目的是确保该函数能够正确返回实体在特定位置的地面点。在某些情况下,可以传入一个参数(比如四元组)来计算地面点的位置。这样做虽然目前不会引起明显的错误,但考虑到未来可能需要更多的灵活性,采用这种方式可能会更好。此外,为了确保现有代码仍能正常运行,可以保持旧有的代码方式,让它继续通过简单的调用来实现功能。这种设计既能兼顾新需求,也能确保不影响原有的逻辑,尽管目前尚未完全测试或完成检测功能,因此难以确认这种改动是否已完全起作用。

上次有的警告报的错误也解决一下

在游戏中走动

目前尚不清楚是否需要立即进行某些改动,因此可能需要先重新加入楼梯部分。虽然还没有完全准备好,但可以考虑开始进行这部分的工作。然而,考虑到目前尚未实现多层地面渲染,这一功能还未完成,因此还需要进一步的开发。

把楼梯放回去,尝试与它们碰撞

现在需要重新加入楼梯部分,并检查其效果。虽然有很多地面补丁出现在屏幕上,且由于缓存的限制,某些地面层会偶尔闪烁,但目前很难确定是否已经修复了之前的 bug。因为没有办法清晰地呈现出这个 bug,所以无法直接验证是否已经解决。

将 GroundBufferCount 从 32 增加到 64

为了做好准备,应该稍微增加地面缓存的大小,将其设置为64,以便能够处理楼梯部分。因为目前生成过程非常慢,所以希望在生成完成后,地面保持稳定,直到能引入更高效的生成方式。尽管如此,如果 bug 真正存在,应该已经能够看到它的表现。现在的修复更多是为了避免潜在问题,但实际上 bug 并没有明显显现出来,所以无法直接进行测试。

思考如何排序渲染列表中的实体并缩放位图

为了准备下次的工作,可能需要将渲染的对象放入一个渲染列表中,以便进行排序并处理其他相关操作。目前,所有内容都是随机绘制的,无法看到排序的效果。为了更好地处理这些问题,需要实现一些排序机制。另一个问题是,当前没有办法缩放位图,这使得在不同高度层级之间进行处理时会很困难。可能需要暂时回到纯正交投影模式,以确保绘制的物体大小保持一致,避免物体在远离时变小。不过,这个问题会在实现实际渲染器后得到解决。

看一下 TODO 列表

当前的待办事项包括Z轴处理、碰撞检测、每帧多个模拟区域的处理、调试代码以及音频流媒体等内容。然而,今天可能需要选择一个小而实用的任务。将渲染对象提取到渲染列表中似乎是一个合适的选择,这样可以对渲染进行排序,并且这是一个可行的步骤。因此,可能会优先做这个任务。

创建 game_render_group.h

对于当前的任务,考虑将现有的渲染代码进行重新整理。目标是开始将渲染功能拆解并组织成更有用的模块。将渲染相关的代码进行拆分,以便能够更清晰地管理和扩展,确保最终能够创建一个高效的渲染结构。

考虑排序并转换坐标系统

在渲染方面,首先需要开始缓冲物体,以便能够对其进行排序。除了排序,还需要进行一些其他的操作,比如将坐标系统从米转换为像素,并处理翻转等问题。重要的是,游戏代码不应该关注这些渲染细节,应该尽量在世界单位下工作,而渲染器则负责处理所有与渲染相关的内容。这样,即使更换渲染器或改变渲染方式、分辨率等,也不会影响到游戏代码。游戏代码应保持一致,渲染器负责处理所有必要的渲染调整。

看一下当前的缓冲区是如何工作的

目前已经有了一些缓冲机制,具体来说,已经有了"实体可见片段组"(entity_visible_piece_group)这样的结构。这些片段组会被推送到缓冲区中,逐步积累。当所有片段都被推送完后,会触发一个事件,处理后续的渲染工作。

将 entity_visible_piece 和 entity_visible_piece_group 拆分到 game_render_group.h 中

可以将当前的片段组概念提取出来,将其作为一个独立的渲染组件来处理。目标是让这个组件记录所有片段,并负责渲染这些片段。这样做几乎就完成了渲染缓冲的功能,只需要稍微进一步调整即可。接下来,将从实体可见片段组中提取相关代码,并将其作为渲染处理的一部分,进一步改进现有的渲染流程。

在 game.cpp 中包含 game_render_group.h 和 game_render_group.cpp

引入了相关的文件,如 game_render_group.hgame_render_group.cpp,并进行了编译。

扩展 PieceGroup 的概念,使其跟踪所有内容

首先尝试扩展"PieceGroup"的概念,并确保它不会被清零,而是跟踪所有的内容。为了实现这一点,需要解决的问题是"PieceCount"会太低,无法满足渲染的需求。作为初步解决方案,可以暂时将"PieceCount"设为一个非常大的值。接下来,将其从循环中提取出来,并从 TransientArena 中获取一个 PieceGroup,这将会放入临时内存区域,确保其在需要时能够被自动刷新,不需要额外处理。通过这种方式,PieceGroup 将作为一个指针返回,这样在代码中不需要每次都获取它的地址。

将渲染移到外部

在进行渲染时,如果将渲染逻辑移出原来的位置,可能会遇到一些问题。具体来说,循环遍历所有实体并将它们推入 PieceGroup 后,在后续使用时会发生编译错误。这个问题与作用域有关,原本应该局部作用域内的内容,在当前情况下无法做到,虽然可以通过调整代码结构来使编译器接受,最终的目的是使渲染过程顺利进行。

现在所有的片段都在一个巨大的缓冲区中,但我们不知道哪个与哪个实体对应

当前遇到的问题是,当所有渲染的元素被放入一个巨大的缓冲区后,无法明确每个元素对应的是哪个实体。这就导致在渲染过程中无法确定每个元素的基准点,也就是无法知道每个元素应该相对于哪个位置进行渲染。因此,需要一种方法来跟踪每个元素的原始位置,以确保渲染时能正确对应和定位。

引入 render_basis

为了在渲染时正确处理元素的位置,将引入一个"渲染基准"作为参考点。这将使得其他元素能够相对于该基准来调整坐标系统。这样做的原因是,虽然在初始渲染时没有存储实体的基准位置,但可能需要在构建渲染状态后调整实体的位置,因此需要一种方法来间接处理这个调整。通过这种方式,即使后续调整发生,也能保持渲染的正确性,虽然这种调整并不是长远的解决方案。

启用在移动实体后设置 render_basis

在渲染过程中,主要的目标是处理基准坐标系统的设置和调整。具体步骤如下:

  1. 渲染基准的引入 :为了支持渲染中物体位置的调整,计划引入一个渲染基准 render_basis。这个基准允许不同的物体根据同一坐标系进行渲染,确保渲染的统一性。

  2. 基准的设置与延迟 :在渲染过程中,每个物体的渲染都需要依赖于某个基准坐标系。为了灵活控制基准,决定在渲染的最后一步设置基准。初步思路是通过在 PieceGroup 上执行 PushBasis 调用来设置当前基准。这意味着渲染时使用的是该基准系,而在最后通过 PopBasis 来恢复之前的基准。

  3. 延迟基准设置的实现:目标是实现延迟设置基准点,即在渲染过程结束后才真正设定物体的位置。这样做的目的是在物体渲染后才对其坐标系统进行调整,使得基准的设置不会在渲染时立即生效,而是推迟到之后。

  4. 基础实现和推送机制 :每次进行 PushPiece 操作时,都需要将当前的基准坐标系推送到渲染栈中。渲染时,所有的物体都会相对于该基准进行渲染。当渲染结束时,通过恢复基准系来确保后续渲染不受到影响。

  5. 后续改进的考虑:目前的做法是直接移植已有的代码,并且暂时不关心性能优化。未来可以进一步分析,是否需要调整基准管理的方式,或者采取不同的策略来优化性能,尤其是针对间接引用可能带来的性能问题。

总的来说,这是一个逐步优化和调整基准坐标系管理的过程,目标是提高渲染灵活性并解决现有的渲染问题。

设置 DefaultBasis 并指向它

在渲染过程中,基准坐标的管理需要进一步完善。具体步骤如下:

  1. 引入默认基准 :在初始化 PieceGroup 时,设置一个默认的基准坐标系,初始值为 (0, 0, 0),这意味着默认情况下不会对物体的渲染位置进行任何位移。通过这种方式,所有物体默认都会根据这个基准进行渲染。

  2. 基准坐标的灵活设置:在某些情况下,如果需要改变渲染基准,可以进行调整。在这些场景下,通过设置新的基准坐标系,渲染物体时会相对于新的基准进行渲染。

  3. 使用临时内存存储 :每次需要创建新的 PieceGroup 时,可以从临时内存中分配内存,并将其默认基准设置为当前基准。这使得所有后续推送的物体都会根据这个基准坐标系进行渲染。

  4. 不需要检查基准 :每个 piece 在初始化时都会被赋予一个有效的基准坐标系(即使是默认的 (0, 0, 0) 基准)。因此,不需要每次渲染时检查一个 piece 是否具有有效基准,默认假设每个 piece 都有基准坐标。

总结来说,通过这种方法,渲染基准的管理变得更加简单且灵活,能够确保每个渲染对象都有一个有效的基准坐标系进行渲染。

在游戏中查看结果

在渲染过程中,进行了调整以确保所有物体能在第二次渲染通过中正确渲染。主要步骤如下:

  1. 基础清理:在渲染开始时,意识到必须清理默认的基准,以避免旧状态影响新的渲染结果。因此,清除了基准并确保正确初始化。

  2. 默认基准处理:确保了默认的基准坐标在每次渲染时都能正确设置,所有渲染物体相对于这个基准坐标进行渲染。

  3. 渲染验证:在渲染后,检查结果是否与预期一致。经过调整后,渲染效果与之前相同,说明调整成功。

  4. 排序问题:虽然当前没有处理排序,但有考虑在未来解决物体渲染顺序的问题,特别是在解决像"物体遮挡"这样的问题时,通过排序Z值等方法来确保物体正确的渲染顺序。

  5. 后续优化:计划在后续进一步调整渲染过程,优化性能和渲染效果,虽然目前排序等优化不会立即进行,但这是未来的改进方向。

总结来说,渲染流程已经按照预期工作,接下来可以考虑进一步优化细节,尤其是在渲染顺序和性能方面。

将PieceGroup提升并为其分配内存

这段工作主要集中在将渲染流程组织得更加清晰和可操作,具体步骤如下:

  1. 渲染骨架的构建:目标是将所有的渲染操作集中处理,逐步接近构建一个完整的渲染器,至少是一个渲染器的框架。这样可以帮助更好地理解渲染过程并进行后续的扩展和优化。

  2. 调整PieceGroup位置 :将PieceGroup移到更高的位置并为其分配独立的内存,这样它能够独立运作,进行渲染工作。

  3. 临时内存的使用:当前使用的是临时内存来处理渲染任务,虽然这不是长久之计,但在此阶段足够使用。临时内存的管理包括将其包裹在一个渲染内存结构中,确保操作不会互相干扰。

  4. 没有效果的变化:尽管进行了一些内存位置的调整,目前在实际运行中没有明显变化,渲染效果仍然和之前一样,说明这些变动只是为后续的渲染流程做准备,并不会立刻产生可见的结果。

总结来说,这些调整是为了将渲染过程模块化,并为更复杂的渲染功能提供基础。虽然目前没有看到显著变化,但这些操作为未来的渲染优化和功能扩展奠定了基础。

更改所有绘制调用,使其基于 PieceGroup

在这段工作中,重点在于更新渲染过程中的绘制调用,将现有的绘制操作改为基于PieceGroup的绘制操作。具体步骤如下:

  1. 绘制调用转换 :目标是将所有的绘制调用(例如PushRect)转换为基于PieceGroup的绘制调用。PushRect用于通过PieceGroup绘制矩形,当前将它逐步转换成新的绘制方式。

  2. 特殊绘制调用的处理 :某些调用(如清除操作)可能需要特别处理,因此在转换过程中,决定保留一些特殊调用,而不是将它们直接转为PieceGroup形式。清除操作被视为一个例外,可能会作为一个单独的真实调用进行处理。

  3. 位图调用的转换 :对地面缓冲区进行位图绘制时,使用PushBitmap调用。这个操作中需要传递偏移量,通常是GroundXGroundY,但由于这些值本身已经是向量,因此可以直接传递。传递时可能涉及对齐问题,但目前可以先不处理对齐,或者以简单的方式处理。

  4. 对齐的处理:对于对齐问题,目前不确定是否需要特别处理。可以选择暂时忽略对齐,或者在更精确的处理下,将其作为一个复杂的对齐参数传递。

通过这些更新,绘制调用的管理更加统一和清晰,为未来的扩展和优化打下基础。

在游戏中检查效果

在这段过程中,出现了一个错误:最初的位图绘制操作并没有正确地将地面缓冲区的位图推送到正确的位置。经过检查,发现原本设置的位置不符合预期,因此决定撤回这个操作。虽然最开始的目标是将位图绘制推送到适当位置,但这个方法并不适用,需要重新调整位置或者选择其他方法来进行正确的绘制。

反转和缩放仍然错误地发生

在这一段中,发现之前的代码仍然执行了坐标转换、缩放和单位转换等操作,但这些操作已经不再需要,因为这些任务已经在渲染器中处理了。因此,决定将这些多余的操作移除,以避免不必要的重复计算。为了更好地检查并理解代码的执行过程,复制了相关代码并将其放在一个可以查看的地方,方便进一步分析。

查看 Push 函数在做什么

在这一段中,意识到渲染代码中存在许多不必要的翻转、坐标转换和单位处理。这些操作本应已在渲染器中处理,但现在却以一种不规范的方式执行。决定逐步清理这些繁琐且不规则的渲染操作,目标是将其简化并规范化,避免这种不一致的做法影响整体渲染流程。希望通过整理代码,清除不必要的复杂性,使渲染过程更加整洁和高效。

尝试规范化渲染的工作方式

意识到不再需要将米转为像素,也不再需要对Y轴进行翻转。对齐方式应基于像素,而不是米,因此需要将对齐字段调整为像素单位,且设置为图像宽高的一半。此时,位置偏移(DeltaX 和 DeltaY)应该直接反映地面的位置信息。接着,检查渲染代码中的错误,发现传递给函数的参数类型不匹配(例如,参数类型是整数而非预期的浮动值)。还需要处理位移的Z轴信息,并修正缺失的偏移量。

查看游戏中的效果

发现当前绘制的瓦片位置不正确,导致接缝问题出现。这可能是由于绘制顺序的问题,尤其是在两个集合绘制时,其中一个集合可能在Z轴上被缩小。为了解决这个问题,决定暂时关闭楼梯渲染功能,避免影响当前调试进度。经过调整后,回到一个已知的状态,可以继续进行后续的调试和修改。

调查为什么位图没有绘制在正确的位置

在进行渲染时,发现瓦片没有正确绘制到预期的位置。检查后发现,虽然Y值的偏移已经被正确地消除,并且其他参数看似也都正确,但最终绘制的结果仍然不符合预期。回顾代码,特别是屏幕中心点和偏移量的计算,确认它们应该给出相同的值。尽管如此,问题仍然存在,可能是在某个细节处理上出了问题,尤其是在Z轴的偏移上。

因此,决定进一步简化代码,减少列表中的内容,逐步检查每个步骤,以便更清楚地看出哪里可能出现了错误。虽然在理论上应该得到正确的结果,但实际渲染中却表现出一些异常,需要深入排查。

顿悟:与其传递整个加载的位图,我们其实是使用了栈上的一个

问题出在绘制时使用了相同的位图,导致所有绘制操作都显示相同的图像。为节省内存,原本并没有直接传递完整的加载位图,而是使用了栈上的位图指针。这种方式本来是为了优化内存使用,但当尝试推迟渲染时,发现由于只是指向相同的栈位图,而不是保存加载的实际位图,导致每次绘制时都使用了相同的位图,从而产生了这个错误。

考虑如何传递正确的位图

为了避免每次渲染时都推送整个位图,考虑到地面缓冲区的数量相对较少,可以考虑存储位图的大小信息和位图本身的引用,这样每次渲染时只需处理这些信息,而不必每次都加载完整的位图。这种方法可能比每次渲染时都推送完整位图更加高效,同时避免了内存浪费。

将整个位图传递给 ground_buffer

通过恢复使用完整的位图而不是指向位图的指针,解决了渲染时的问题。之前使用指针导致无法正确处理渲染,因为位图只是一个堆栈上的临时指针,无法正确引用实际的位图。现在通过直接存储位图并去除之前的缓存机制,可以简化处理并确保渲染正确进行。

看是否能解决问题

问题已经解决,现在所有的渲染都通过了延迟路径。虽然仍然有一些DrawRectangle的调用存在,但其他的绘制操作都已经统一通过正确的渲染路径进行。这样,渲染流程已经变得更加规范化和一致。

将 entity_visible_piece_group 重命名为 render_group

将"entity_visible_piece_group"重命名为"render_group",并通过搜索和替换的方式在代码中进行更新。这是为了使代码中的命名更加清晰和一致,避免不符合实际含义的命名。

考虑如何扩展 render_group 的概念,以便能够将多个事物推入渲染栈

考虑到如何扩展渲染堆栈以支持多个项目的推入,决定暂时以一种方式进行实现,看看效果如何。此时尚不确定是否需要固定大小的堆栈,并且在选择推入方式时,要考虑如何确定跳过的距离。

考虑链表与打包集合

在决定渲染组的结构时,经历了一番犹豫,考虑了使用链表或打包集合。最终,选择先做出一个决定并实施,尽管并没有明确的最佳选择,通常这种情况意味着没有完美的决策,实践中能看到效果之后再调整。

为 render_group 添加 PushBufferBase,PushBufferSize 和 MaxPushBufferSize

决定采用推送缓冲区(pushbuffer)作为渲染数据的存储方式,这种方式通常用于渲染系统中,允许将渲染数据连续推送到一个缓冲区中。此前,已经在代码中自然而然地形成了类似的结构,即将可见实体片段存储到缓冲区中,然后统一绘制,绘制完成后直接丢弃整个缓冲区的内容。因此,计划进一步扩展这种方式,使推送缓冲区成为一块独立的内存区域,在需要存储渲染数据时,按照一定的大小推送到缓冲区,并提供相应的机制来管理缓冲区的大小和使用情况。例如,增加一个变量用于记录缓冲区的当前大小,并定义一个最大限制,以便在存储渲染数据时进行管理。

在 game_render_group.cpp 中引入 AllocateRenderGroup

当前的修改引入了 AllocateRenderGroup,用于创建一个渲染组,并分配 PushBuffer 供其使用。这个函数需要传入 MaxPushBufferSize以确定最大缓冲区大小,并且从指定的memory_arena 获取内存。具体来说,AllocateRenderGroup` 的作用如下:

  1. 分配渲染组对象 :在 memory_arena 上推送一定空间,用于存储渲染组的数据结构。
  2. 分配推送缓冲区 :在 memory_arena 上继续推送指定大小的缓冲区,用于存储后续的渲染数据。
  3. 使用 PushSize 进行内存管理 :采用 PushSize 来确保分配正确的大小,使 PushBuffer 能够正确存储数据。
  4. 调整 PushSize 逻辑 :当前的 PushSize 逻辑有所调整,以适应新的 PushBuffer 需求,并确保数据的存储方式符合预期。

这项改动的核心思想是优化 PushBuffer 的使用,使渲染数据能够以高效的方式存入连续的内存块,并避免使用复杂的数据结构(如链表)。后续可能需要进一步调整 PushBuffer 的管理方式,例如提供回收机制或动态扩展能力,以支持不同规模的渲染需求。

定义 PushSize 宏

当前的修改引入了一个宏来处理 PushSize,而不是直接通过函数调用。引入宏的原因主要是为了调试方便,之后可以通过宏更好地控制和追踪内存分配的过程。具体来说,宏的作用是:

  1. 避免直接调用分配器函数:通过宏来间接调用分配器,从而减少直接使用函数调用的复杂性,便于调试和排查问题。
  2. 增强调试能力:宏可以提供更强的调试信息,能够在运行时捕捉到分配和内存管理的细节。
  3. 统一内存分配逻辑:通过宏实现内存分配,确保所有相关的内存操作都通过相同的接口进行,从而提高代码的可维护性。

此做法的重点是通过宏来替代函数调用,使内存分配过程更加灵活且易于调试。

为 MaxPushBufferSize 分配并存储在 PushBufferBase 中

这里的修改目的是为 MaxPushBufferSize 分配内存,并将其存储在 PushBufferBase 指针中。具体步骤包括:

  1. MaxPushBufferSize 分配内存:根据需要的最大缓冲区大小,进行内存分配。
  2. 存储内存指针 :将分配的内存地址存储在 PushBufferBase 指针中,以便后续使用和管理。

这样做的目的是为后续的渲染操作提供一个稳定的内存区域,从而提高渲染过程的效率和可维护性。

初始化 render_group 的所有字段

在这一步中,主要目标是初始化 render_group 中的所有字段,方法与之前的做法相同。这种做法基于压缩优化的思路,通过将之前已经使用的处理方式提取出来并在新的地方进行复用,从而提高代码的效率和整洁度。最终,这一改进将使渲染流程更加高效和模块化。

调用 AllocateRenderGroup 并将 MaxPushBufferSize 设置为 4MB

在这一部分中,决定将 PushBuffer 大小设置为 4MB,虽然这个值是随机选择的,并没有特别的理由或依据。RenderGroup 将从临时内存区(TransientArena)中分配空间,具体的分配过程通过调用 AllocateRenderGroup 完成。

使 DefaultBasis 分配并初始化

在这部分中,PushBuffer 大小被初始化,并且一个默认的 render_basis 结构体也被分配和初始化。这个 render_basis 结构体会被推送到 PushBuffer 中,并且按照指定的方式进行初始化,同时也会关联到游戏状态。

存储 MetersToPixels 而不是 GameState,并将其传递给 AllocateRenderGroup

在这段代码中,游戏状态(GameState)本来是需要传递的,但经过考虑后,决定不再存储游戏状态,而是临时存储 MetersToPixels 转换的比例。这是为了更清晰地管理和定位相关的转换操作。这个临时的存储方式可能会在后续的开发中被移除。

初始化 MaxPushBufferSize 和 PushBufferSize

在这一部分代码中,首先将推送缓冲区大小 (PushBufferSize) 初始化为零,而最大推送缓冲区大小 (MaxPushBufferSize) 则是根据传入的值来设置。接着,初始化了渲染器,并确认了元素计数(PieceCount)。

现在我们遇到问题:当我们执行 PushPiece 时,无法获取它,因为我们现在不知道那个片段在哪里

在此部分中,出现了一个问题:当调用 PushPiece 时,无法获取元素,因为不再拥有相关的 pieces 结构。

处理其他问题

在这部分中,进行了调试,解决了一些宏的错误,并修正了标识符的问题。通过更改函数参数和标识符(如将 PieceGroup 更改为 RenderGroup),最终修复了编译错误,并确保代码能够正常运行。

整理 PushBuffer 情况

在这部分代码中,讨论了如何通过推送渲染元素来管理推送缓冲区。通过添加 PushRenderElement 函数来检查当前缓冲区是否足够容纳新的渲染数据。如果当前缓冲区的大小加上待推送的数据大小小于最大缓冲区大小,则可以将数据推送到缓冲区中并更新缓冲区的大小。如果缓冲区溢出,则会标记为错误,但不会导致崩溃,只是一个警告,表明渲染命令无法再存储。

接下来,设置了一个遍历这些渲染数据的流程,暂时通过基地址和缓冲区大小来处理这些数据。这部分代码为遍历和处理推送数据做了初步设计,虽然具体实现细节可能稍后再补充。

查看游戏中的效果

在这部分,讨论了推送实体可视数据的过程,并对之前的工作做了回顾。简单总结了所做的工作。通过在黑板上重新讲解,帮助理清了之前进行的操作。由于当天的进展较快,开发进程有些仓促,因此进行了这次回顾,目的是帮助更好地理解和整理已经完成的部分。

黑板:PieceGroup

回顾之前的工作,最初有一个叫做"PieceGroup"的结构,它用于存储多个与同一点相关的游戏角色部分。比如一个角色可能有头、披风、躯干和影子,这些都是分开的"piece",但它们是相对于同一个基点进行偏移的。然而,最初并不知道基点的位置,直到执行完相关代码后,才可以得知这个基点。因此,创建了一个"PieceGroup",它会记录每个部分的位置和偏移量。一旦得知基点的位置,就可以通过这些记录来绘制这些部分。

后来,决定将"PieceGroup"转化为"RenderGroup",为渲染器做好准备。这个变化将帮助更好地组织和管理渲染过程。

黑板:压缩导向编程

为了避免一次性进行太大范围的修改,决定逐步进行调整,确保每一步都能清晰地看到决策是否正确,且各个部分是否能够顺利协作。这种逐步推进的方式有助于控制进度,并确保代码在改动过程中始终能够保持有效的互联性和一致性。编程的难点往往不在于单个模块的实现,而是在于不同模块之间的关系和交互,因此保持各部分的协调性是关键。

黑板:render_group

今天的工作主要是将"piece_group"转变为"render_group",即将其作为一个内存缓冲区,用于存储渲染命令。每个渲染项(如角色的头部、披风、躯干等)被写入到这个缓冲区中。这一过程已经完成,但目前这些项目仍然是同质的(即只包含可见的"piece")。接下来计划将其转化为一个异质的推送缓冲区,允许其中包含不同类型的渲染命令,例如绘制位图、矩形、清除屏幕等。

这个"render_group"将成为渲染器需要执行的命令组,处理多个渲染项,而不仅仅是一次渲染一个角色。还需要考虑如何将排序功能集成进来,但这部分还没有深入思考。总的来说,今天的工作是对代码进行了一些调整和优化。

渲染组中会包含像过滤器这样的东西吗?

有提到渲染组(render group)中是否会包含像过滤器这样的东西,答案是可能会。渲染组将会是我们传递给实际渲染器的内容,基本上,所有渲染相关的内容都会包含在其中。即使是过滤器,如果它们是可以开关并具有不同速率的,可能也会包含在渲染组中,以确保渲染器能够处理这些内容。渲染组需要封装所有这些渲染相关的信息,以便传递给不同的渲染器,比如OpenGL或软件渲染器。

如果某些内容在运行时不需要指定,且是已知的,那么就不需要放入渲染组中。

渲染器需要多久才能完成?

在关于渲染器的讨论中,提到两种可能的方向。第一种是将渲染器作为一种教育体验,重点是写出一个完整的渲染器,确保它按最教育化的方式执行,不太关心其速度,这种情况下可以依赖硬件加速。第二种是让游戏能够在软件渲染下运行,达到足够快的速度以便实际使用。

目前的想法是采取一种谨慎的优化方法,模拟图形卡的工作方式,确保渲染器的速度合理,同时也展示如何优化代码,但不会进行与GPU完全不同的算法优化,因为这样可能失去教育意义。具体的优化方向和时间长度将取决于优化目标的远近。

能否给我们一些渲染组中简单事物的例子?

渲染组中的一些简单命令可能包括清除缓冲区。例如,清除缓冲区将是一个命令,它会在渲染过程中将当前缓冲区的内容清空。

有没有适用于 C / Visual Studio 的静态代码分析工具,可以帮助发现像未使用的变量之类的错误?

对于C语言的静态代码分析工具,Visual Studio是否内置了类似功能尚不明确,但确实有一些第三方工具可以帮助检测未使用的变量等问题。对于Linux平台,Clang提供了一些内置的静态和动态分析工具,比如地址清理工具(Address Sanitizers)。如果不确定Visual Studio是否有类似功能,可以通过在线搜索了解更多信息,尤其是Visual Studio 2013或更高版本可能已经支持这些功能。

另外,一种可行的方案是将代码移植到Linux或Mac平台,利用Clang的分析工具进行静态分析,甚至可以在没有完全移植的情况下,通过运行游戏循环并使用地址清理工具,检测潜在问题。这种方法既不需要购买额外的工具,也能利用现有的分析工具进行代码检查。

那么你是否打算将渲染器抽象为一个层,就像我们将平台和游戏代码分离一样,让不同的渲染器可以在相同的数据结构上工作?

计划通过将渲染抽象化为一层,类似于将平台与游戏代码分离的方式,使得不同的渲染器能够在相同的数据结构上操作。最终,渲染系统将拥有一个"渲染组规范",定义一个内存块的格式,表示如何绘制内容,任何希望实现渲染器的人都可以根据这个规范进行开发。这样,不同的渲染器可以被自由地插件化,社区中的开发者也能轻松地为该系统实现其他渲染器,而不需要对核心系统进行额外的工作支持。例如,社区成员可以为Direct3D或Mantle等实现渲染器,而这些工作不会直接出现在当前的流中。

如果你要写一个算法来生成数独谜题,它会是怎样的?

这是一个与当前工作的内容无关的问题,询问如果要编写一个算法来生成伪计数谜题,它该如何运作。

未使用的变量在我的编译器中是一个警告。你是否关闭了那个警告?我在想忽略变量时会用一个 allow local 宏

编译器中关闭了一个警告,并且使用了一个宏来忽略某些变量的警告。对于静态分析工具的问题,假设提问者是想了解更复杂的静态分析工具,而不是编译器能自动捕获的简单警告。这类工具可能涉及函数调用等复杂的检查,而Visual Studio默认可能不会捕捉这些。对于检查未使用的变量,只需要开启相应的警告即可。

渲染组中是否包含渲染组本身是否合理?

提到在工作过程中,可能会遇到将渲染组包含在渲染组中的情况,虽然目前不确定是否会这样做,但这完全是一个合理的方案。渲染组可能会最终呈现出类似树形的结构,因此是否需要这种方式尚不清楚。

我的意思是,你是否打算使描述渲染所需的数据格式化,使其适用于软件和硬件渲染

计划将数据格式化为适合软件和硬件渲染的形式,唯一不会优化的地方是硬件吞吐量,因为游戏是2D的,硬件吞吐量远高于所需,因此不需要特别关注这一点。在硬件渲染中,如果追求极致性能,可以使用硬件格式的推送缓冲区,而在软件渲染中,会使用中间缓冲区格式,这会增加一定的处理时间,但不会影响帧率。除去这一点,渲染的处理方式将适应两种渲染方式。

我们的英雄是否总是比树顶高?

游戏中的角色尺寸与树木的尺寸并不完全成比例,角色现在显得比树木高大,但最终可能会稍微低于树木,但不会像现实生活中那样差距大。游戏中的艺术风格是表现性的,并非完全按比例呈现,因此人物与树木的比例并不严格按照现实尺度来定义。

相关推荐
m0_748230441 分钟前
Java进阶学习之路
java·开发语言·学习
m0_7482384223 分钟前
C++ 学习:深入理解 Linux 系统中的冯诺依曼架构
linux·c++·学习
weixin_376934632 小时前
mysql学习笔记-MySql事务日志
笔记·学习·mysql
码农白衣2 小时前
前端八股CSS:盒模型、CSS权重、+与~选择器、z-index、水平垂直居中、左侧固定,右侧自适应、三栏均分布局
前端·css·学习
柠檬味的薄荷心3 小时前
【Unity2D 2022:UI】创建滚动视图
笔记·unity·c#·游戏引擎
chimchim663 小时前
【starrocks学习】之将starrocks表同步到hive
hive·hadoop·学习
学编程的闹钟3 小时前
55【ip+dns+域名关系】
学习
web150850966414 小时前
Java进阶学习笔记18——接口的注意事项
java·笔记·学习
潜龙在渊灬4 小时前
学前端框架之前,你需要先理解 MVC
前端·学习·程序人生·职场和发展·前端框架·mvc