游戏引擎学习第153天
仓库:https://gitee.com/mrxiao_com/2d_game_3
回顾
目前正在进行的是一个比较大的系统调整,原本预计今天会继续深入这个改动,但实际上在昨天的开发中,我们已经完成了大部分的代码编写,并且运行之后几乎一切都能正常工作,只出现了一些小问题。因此,今天的主要任务是进行一些优化和调整,以确保所有部分都能顺利运行。
当前的工作重点是迁移到统一的资源文件系统。之前,游戏是直接从磁盘上随机加载位图(bitmap)和音频(wav)文件,采用的是较为简单的资源管理方式。而新的目标是将这些资源整合到一个统一的资产包中,每个资产包可以包含任意数量的游戏资源,同时包含它们的元数据,例如资源的用途、加载时机等信息。
在游戏启动时,它会扫描指定目录下的所有资源文件,并将它们合并为游戏运行所需的最终资源集合。这种方式的优势在于,游戏的所有资源都可以被打包在一起,便于分发和管理,同时还能提高加载效率。
昨天的主要进展是完成了这一资源管理方式的基础架构,原本的文件 API 只能一次性读取完整的文件,现在已经调整为可以分块读取文件的内容。这部分工作已经全部完成,接下来的任务是进行一些调整和修正,以确保整个系统能够稳定运行。
今天的计划
现在要做的事情是回到代码中进行一些修正,因为在资源管理的过程中,资产数据的排列方式可能存在一些问题,同时可能还存在一些小的 bug。虽然之前的代码在第一次运行时基本上能够正常工作,但仍然有部分逻辑可能不太正确,因此需要深入调试,确保所有内容都按照预期运行。
目前还没有真正使用调试器进行逐步检查,因此今天的主要任务是回到代码中,清理并修正所有可能的问题,确保所有部分都能够顺利运行。完成这些调整后,下一步的目标是进行更深入的测试,具体来说,就是测试多个资源文件的合并。
现在已经有一个测试程序,它能够写入一个测试用的资源文件。接下来的改进是在这个测试程序的基础上,使其能够写入多个独立的资源文件,并确保游戏能够正确地加载和合并这些资源文件,而不会出现任何错误。这是当前系统优化的下一步逻辑目标,因为虽然代码已经写好了,但在实际运行之前,无法确定它是否真正可行,因此需要进行验证。
猜测上次流的错误来源
目前的情况是,运行游戏时发现了一些异常,虽然整体上看起来是正常的,声音也能够正确播放,但仍然有一些细节不太对劲。比如,当游戏运行时,声音数据能够被正确地流式加载和播放,所有音效似乎都正常工作。然而,画面上却出现了一些奇怪的现象,例如阴影完全消失了。
根据现象进行推测,可能的原因是加载资源文件时,忽略了 asset 0 这个特殊的空资源(null asset)。通常情况下,asset 0 作为一个特殊标识,不会被正常使用,而阴影可能被错误地分配到了 asset 0 这个位置。因此,当程序请求阴影资源时,返回的是 asset handle 0,但系统知道 asset 0 是空资源,因此不会返回任何东西,导致阴影无法显示。
除了阴影的问题,其他资源似乎都能正确加载并识别,包括角色的头部、躯干、腿部,以及地面的贴图和树木等所有位图资源。因此,目前的主要问题就是确定阴影丢失的具体原因,并修正 asset 0 的分配错误。
从昨天的运行情况来看,初步判断可能确实是因为忽略了 asset 0,但仍需进一步确认并修复问题。接下来的步骤是深入检查资源加载流程,并确保所有资源都能正确分配到合适的索引位置。



检查测试资产构建器
接下来的工作是重新检查 test_asset_builder,重点是回顾资产文件的创建过程,特别是 asset 0 的处理方式。目前对之前的代码细节没有完全记忆清楚,因此需要快速浏览一遍,确保一切按照预期执行。
从代码来看,当前的实现是将 AssetCount 设为 1,这意味着会预留 asset index 0 的位置,实际上不会存放任何资源数据。这就导致 asset 0 为空资源(null asset)。不过,这样的设计是否合理需要进一步思考。
最初的想法是,不应该让资源打包器(asset bundler)保留 slot 0 为空,因为资源打包器本身并不需要这样做。然而,考虑到文件内部的资源引用机制,如果某些地方需要引用 null 资源,它们必须有一个明确的 null 位置,所以 asset 0 的空缺可能是合理的。因此,当前的结论是,这个位置应该继续保留,而 asset 0 需要被明确标记为一个无效资源,不能被加载或使用。
这也意味着,在 game_asset 相关的代码中,需要确保正确处理 asset 0,不能误将其当作正常资源使用。同时,所有逻辑都要意识到 asset 0 是一个特殊的无效资源,以免错误地加载或引用它。
回到代码分析,从 test_asset_builder 的实现来看,可以大致推测当前存在的问题:
- 资源计数错误:资产文件的
AssetCount设置可能存在问题,导致asset 0位置的处理不当。 - 加载逻辑缺陷:在资源加载时,可能没有正确考虑
asset 0,导致某些资源错误地被存放在这个位置,例如阴影资源丢失的问题。
接下来的步骤是深入检查 test_asset_builder 的实现,确认 asset 0 的位置是否被正确跳过,并调整 game asset 代码,确保加载资源时能够正确识别并处理 asset 0。

资产数量少了 1,且没有加载空资产
当前代码中的一个问题是,在处理资产文件时,实际加载的资源数量比预期的少了一个,这个问题已经可以明确原因,无需进一步调查。
问题分析
-
资源数量计算偏差
在读取.HHA资产文件时,程序会计算AssetCount,但这个AssetCount实际上是 实际资产数量 +1,因为它包含了asset 0这个特殊的空资源(null asset)。例如,如果游戏中实际有 50 个资源,那么AssetCount会显示为 51,因为asset 0也被计入其中。 -
资产加载过程中跳过
asset 0
在真正加载资源时,程序会遍历所有资源,但只加载那些具有有效类型的资源(valid asset types)。合并资产时,asset merge逻辑默认会忽略无效类型的资源,而asset 0并没有被归类到任何有效的资源类型中,因此它不会被加载。 -
导致的结果
由于asset 0在合并过程中被跳过,最终加载的资源数量总是比AssetCount少 1。换句话说,如果AssetCount是 51,那么最终加载的资源数只有 50,而assert语句会检测到这个不匹配,并导致断言失败。
解决方案
修正 AssetCount 的计算,确保预期加载数量与实际加载数量匹配。
- 在
asset merge逻辑中,需要明确跳过asset 0,但同时调整AssetCount计算方式,使其反映真正应该加载的资源数量。 - 或者,在执行
assert检查时,考虑asset 0的特殊情况,避免误报错误。
接下来的任务是修改 test_asset_builder 以及资源加载逻辑,以确保 asset 0 被正确处理,同时不会影响其他资源的加载。
修正资产数量
问题分析与修正方案
在当前的 HHA 资产管理逻辑中,asset count 计算方式存在问题,导致加载的资源数量与预期不符。这主要源于 asset 0(空资源,null asset)的处理不当。
问题 1:文件中的 asset count 需要调整
- 由于
HHA资产文件的第一项始终是null asset,所以asset count计算时应当减去 1,以排除asset 0。 - 例如,若
HHA文件声明了 51 个资产,其中asset 0是无效的空资源,那么实际需要加载的资源数应为 50。
修正方案:
- 在计算
asset count时,明确减去 1,以确保最终合并计算出的asset count只包含有效资源。 - 这样,如果存在多个
HHA文件,就不会因为每个文件都包含一个null asset而导致最终计数错误。例如,如果有 3 个HHA文件,错误的计算方式会导致asset count多 3 个,而正确的方法确保null asset只计算一次,不随文件数量增加。
问题 2:资产加载起始索引错误
- 代码中当前的加载逻辑是从
asset 0开始加载,但asset 0是空资源,不应被加载。 - 结果导致每个
HHA文件都额外添加了一个null asset,从而导致资源数组出现多个null asset,而非只保留一个。
修正方案:
- 调整索引起点:在处理资产加载时,从
asset 1开始,而不是asset 0,确保null asset仅存在一个,不随HHA文件的数量增加。 - 保证唯一性:不管有多少个
HHA文件,都确保只存在一个null asset,避免多个null asset影响资源管理。
最终修正措施
- 调整
asset count计算方式,从HHA文件读取asset count时减去 1,以排除asset 0。 - 修改资源加载逻辑,确保
null asset只出现一次,而不是随着HHA文件数量增长而增加多个null asset。 - 调整索引起点,从
asset 1开始加载有效资源,避免误加载asset 0。
预期结果
- 资源数量计算准确,避免因
null asset额外增加导致计数错误。 - 资源数组中仅包含 一个
null asset,而不是每个HHA文件都产生一个。 - 资源加载顺序正确,不会因错误的索引导致资源丢失或排列错误。
接下来,需要修改代码并进行测试,确保修正后的 asset count 计算和资源加载逻辑符合预期。


构建空资产
当我们真正开始加载资源时,会遇到一个问题:资源计数从零开始,但实际上我们需要保留这个位置给空资源(null asset)。因此,我们首先需要创建一个空资源。具体来说,我们应该在这里生成一个空资源,执行一个零初始化操作。
我们的目标是确保第一个资源,即 assets[asset + asset_count],被清零。无论该资源是什么,我们都要将其内容全部清除,使其变成一组全零的数据。理论上,程序运行时不应该真正访问这个空资源,但为了安全起见,我们确保它是空白的。
在完成清零之后,我们需要将资源计数 asset_count 增加 1,以保证所有后续资源的索引正确对齐。因此,我们要明确地保留一个空资源作为占位符,确保所有资源在文件中的排列符合我们的预期。这将解决之前的问题,使资源的存储和访问更加一致、合理。


一切正常
现在阴影已经恢复显示,并且不会再触发断言错误,因此当前的状态相比之前更稳定,可以继续进行后续的开发和测试。
之前在运行时,阴影缺失的问题让我们意识到资源加载过程中可能存在索引偏移的错误。经过分析,我们发现每个 HHA 资源文件的第一个槽位是预留的空资源(null asset),但在合并多个资源文件时,我们错误地计算了资源索引,导致阴影资源被错误地映射到空资源的位置。
为了解决这个问题,我们调整了资源计数和索引的计算方式:
-
正确计算资源总数
- 由于每个 HHA 文件的第一个槽位是空资源,因此在计算总资源数时,我们需要减去这些额外的空槽位,以确保正确分配资源索引。
-
正确初始化空资源
- 在加载资源之前,我们手动创建一个空资源,并确保它被正确清零,以防止程序访问时出现未定义行为。
- 这样做的目的是保证空资源始终存在,但不会干扰实际游戏资源的加载和使用。
-
修正资源索引偏移
- 在合并多个 HHA 资源文件时,我们确保只使用一个全局的空资源,而不是每个文件都保留一个空资源,这样可以避免索引错位的问题。
经过这些调整后,阴影资源得到了正确加载,所有其他资源也都能正确地识别和使用。这样,我们就可以更加顺利地继续开发,确保游戏的资源系统运行稳定。
测试资产合并
现在资源系统的代码看起来是正常工作的,但目前仅加载了一个 HHA 资源文件,因此我们还不能确定资源合并机制是否真的稳定可行。一旦增加第二个 HHA 文件,合并过程可能会出现问题,甚至可能完全崩溃。因此,接下来的重点是测试多个 HHA 资源文件的合并,以确保系统能够正确处理多个文件,而不会导致数据错乱或加载失败。
目前的资源合并逻辑仍然比较基础,实际上它仍然是按照原来的方式逐个加载资源,而没有进行真正的合并操作。因此,如果在多个 HHA 文件之间存在相同资源的情况,我们需要检查系统是否能够正确处理这些重复资源,确保资源不会被错误地覆盖或遗漏。
此外,在使用开发设备时,意外触碰到了 Wacom 数位板的触控区域,导致滚动异常。可能需要考虑禁用该功能,以避免开发过程中发生类似的误操作,从而影响工作效率。
什么是Wacom 数位板
Wacom 数位板是一种用于数字绘画、设计和手写输入的专业输入设备。它通过一个平板和一支专用的触控笔来实现高精度的手写或绘画操作。Wacom 数位板常被艺术家、设计师、插画师等创意专业人士使用,因为它提供了比传统鼠标更自然的绘图体验。
Wacom 数位板的工作原理是,当触控笔在板面上移动时,它会感应到笔尖的位置和压力变化,从而实时转换成数字信号。这使得使用者能够在屏幕上进行精确的绘图、上色、修图等操作,同时也可以用来进行文字输入等任务。Wacom 的产品通常具有非常高的精度和响应速度,适合需要高精度输入的创意工作。

考虑空标签
在处理资产时,遇到了类似的情况,即需要为标签保留一个“空标签”。与处理资产文件类似,需要确保第一个标签槽位是空标签(即保留槽位),并且在计算标签数量时,不计算这个空标签。因此,标签的计数应该从1开始,而不是从0开始。
在文件处理过程中,发现需要首先为每个标签生成一个空的标签,并将其清零,确保它不会被实际加载。然后,在处理标签时,标签数量需要减去1,因为第一个标签实际上是一个空标签。此时,代码中的处理逻辑并不完全正确,因为标签的总数在实际加载时应该是减去空标签的数量。
为了修正这个问题,需要做出几个修改:
- 标签计数的调整:首先确保标签的计数不包括空标签,因此标签计数需要减去1。
- 跳过空标签:在读取标签时,需要跳过第一个标签,即空标签。
- 标签重定位:当标签的基址被调整时,空标签的特殊情况需要额外处理。如果第一个标签是空标签,则将所有标签映射到空标签的位置;如果不是空标签,则根据标签基址重新定位标签。
由于这种复杂性,标签的处理在加载时可能会有一些不太直观的情况,但这在数组合并时是常见的,需要特别注意处理空标签的特殊位置。
总体来说,虽然这种处理方法有些复杂,但它在加载时不会影响正常的操作,而且只需要在加载时仔细验证和测试就可以确保其正确性。因此,虽然在实现上有些繁琐,但整体来说这是可以接受的,并且在后续的测试中可以进一步验证其稳定性。

将测试资产文件拆分以测试合并过程
在进行资产测试时,首先决定将一个HHA文件拆分成两个文件,以便验证是否能够正确处理和合并它们。为了实现这一点,首先提取了写文件的代码,并将其抽象为一个函数,这样就可以在需要时多次调用它。通过这种方式,可以将重复的文件写入操作提取成函数,简化后续的测试。
接下来,将文件拆分为两个部分,分别处理其中不同的资产。例如,将某些资产分配到一个文件中,而将其他资产分配到另一个文件中。特别地,有人提议将所有声音资源放在一个文件中,因此决定将所有声音放入一个单独的资产文件中,而将其他资源(如音乐)分配到不同的文件中。
这样做的目的是测试资产合并的过程,确保多个资产文件能够正确加载和合并。在此过程中,还对一些初始化操作进行了抽象,使得代码更加整洁和可维护。通过初始化资产的功能,简化了每次测试所需要的准备工作。
完成代码编写后,运行了测试资产构建器,生成了三个测试文件(test1, test2, test3)。然后,删除了原有的测试文件,并运行了新生成的代码,检查是否能够成功生成这些文件。最后,验证这些生成的文件是否能够正确加载,并进行资产合并测试,确保功能按预期工作。
通过这些步骤,确保了拆分和合并资产文件的功能正确实现,并为后续的调试和验证提供了有力支持。

只加载其中一个文件
首先,决定不进行第一次完整的测试,而是仅加载其中一个文件进行测试。目标是检查在该文件中加载的资产是否能正常显示,而其他文件中的资产不应显示。具体来说,在加载的文件中包含英雄的头部、披风、上身和四个方向的模型,因此期望只看到英雄及其相关部分(如披风、上身)显示,而不显示其他内容,如环境或阴影。






test_asset_builder.cpp中未初始化 变量读出来可能是随机值
测试合并代码。仍然存在错误
现在,尝试调试合并代码。表面上看似一切正常,但并没有完全确认,特别是在加载声音的过程中出现了问题。出现了一个无效的ID,这意味着在资产文件中,程序认为有一个有效的ID,但是实际并没有这么多的资产,显然是加载代码中的问题。
问题发生在加载声音的预取过程中,错误显示的ID完全无效,可能是因为音乐资产没有被正确处理。分析后认为可能的原因有两个:首先,在合并资产时,相关的ID并没有更新。其次,问题可能出在创建声音资产时,相关的ID没有正确初始化。虽然从代码中看,NextIDToPlay->NextIDToPlay 值似乎已经正确初始化,但在添加声音时,代码中其他部分的初始化可能出了问题。
接下来,重点检查加载资产文件的部分。特别是,声音的“NextIDToPlay”这个值的映射问题。因为在合并多个文件时,这个ID需要重新映射,而当前系统并没有办法处理这种情况。通过这个分析,确认问题确实正如最初所猜测的那样,错误的值正是由于在合并过程中未正确处理ID导致的。





重新映射下一个播放 ID 以考虑多个资产文件
在处理多个资产文件时,标签(tags)数组会被合并,原本在不同文件中的标签可能会映射到不同的编号。例如,标签0的内容可能会在合并后变成多个标签0、标签1、标签2,依此类推,这就涉及到了标签的重新映射。合并后,资产中的标签会对应新的编号,确保资产指向正确的标签位置。合并操作能够确保所有资产指向正确的标签。
然而,问题在于资产之间的引用没有完全被考虑到。虽然标签映射是正确的,但如果一个资产指向另一个资产,这种引用关系并没有得到处理。比如,当资产A引用资产B时,经过合并后,可能会指向错误的位置。特别是对于声音(sound)资产,它们有一个指向下一个声音ID的指针,这个指针也需要被重新映射,否则会发生错误。
在原始文件中,声音ID可能是指向一个有效的声音资源,例如ID为16的声音指向ID17的声音。合并后,这些ID会重新分配,因此它们需要进行映射,以确保指针指向新的有效位置。如果没有重新映射,就会出现指向错误资源的问题,可能会加载一个完全不同的资产(如图像或其他类型的资产),这就是加载时出现垃圾值的原因。
为了修复这个问题,需要在加载资产时进行指针的重新映射。加载代码必须能够判断一个资产是否为声音类型,并且知道它的指针指向哪个新的资产。例如,通过检查资产类型ID,确定当前资产是否属于声音类型,并根据这一信息对“下一声音ID”进行重新映射。
具体来说,代码可以通过检查一个资产类型ID是否属于声音类别来决定是否需要重新映射。如果是声音资产,那么就需要根据新的基础偏移量对“下一个声音ID”进行调整,以确保它指向正确的声音资源。这样,所有的声音资源就会按照正确的顺序加载,而不会出现错误指向。
然而,这种方法存在一个局限性。如果一个资产的“下一声音ID”指向的资产属于其他类型(如图像),这种重新映射就会失效。为了更好地处理这种情况,可以引入一个资产映射表,将不同类型的资产映射到正确的位置。通过这个映射表,在加载过程中根据类型进行准确的指针调整,避免了指向错误资源的问题。
尽管这种方法相对简单,但它可能带来性能问题,特别是在需要频繁查找映射表时,这种额外的查找操作会增加复杂性。因此,虽然这个方法可以解决问题,但并不是最佳的解决方案。


决定不重新映射下一个播放 ID,而是显式地循环和链接声音
在处理声音资产时,为了避免复杂的指针重映射,可以采用一种更简单的方式来管理声音的播放和循环。原本通过“NextIDToPlay”来处理声音的播放链,但这会引入复杂的重映射过程。为了解决这个问题,可以设计一个新的方法,使得“NextIDToPlay”变得更为简洁和专用。
新的方法不再使用复杂的ID重映射,而是通过一个枚举值来指定声音操作的类型。这个枚举值可能包括几种操作:例如“无操作”(none)、“循环播放”(loop)、“继续播放下一声音”(advance)等。这样,在声音播放时,根据这些枚举值来决定如何播放下一个声音,避免了以前那种复杂的指针重映射。
具体来说,在文件格式中,可以替换“NextIDToPlay”为一个新的枚举值,比如“HHA->Sound.Chain”。通过这种方式,声音播放操作可以更直接地表达为播放、循环或继续播放下一个声音。例如,当音乐播放完成时,如果需要播放下一个音效,可以通过设置操作为“advance”来指示播放下一个声音。这样,代码的结构更加简洁,也避免了复杂的资产间引用问题。
此外,为了实现这个功能,程序可以检查当前播放的声音是否为最后一个音效。如果不是最后一个音效,就通过设置操作为“advance”来播放下一个音效。这种方式可以使得声音的处理更加流畅,并且在不需要复杂重映射的情况下实现连续播放的效果。
在实现过程中,可以简化音乐和声音处理的混合器(mixer)。如果声音链操作指示需要“继续播放”或“循环”,程序可以根据枚举值来进行处理,而无需依赖复杂的ID映射。通过这种方式,声音的管理变得更加直观和清晰,代码也变得更加易于维护。
总之,改进后的方法通过使用枚举值来简化声音播放的控制,避免了原本复杂的ID重映射过程,使得系统更加高效且容易管理。




实现 GetNextSoundInChain

现在需要做的主要工作是修复一个函数,使它能够返回一个声音 ID。具体来说,程序会接受一个声音 ID,然后获取该 ID 对应的声音信息。如果没有找到对应的声音,可以假定返回值为空,表示没有声音跟随当前的声音。如果提供的 ID 无效,程序会验证这个 ID 是否有效,然后获取该 ID 对应的声音信息。
对于获取到的声音信息,接下来会根据不同的链式操作类型进行处理。使用条件判断(如 if 或 switch)来根据不同的链式操作类型(如 “无操作”、“循环” 或 “前进”)来决定返回何种操作。如果是“无操作”,程序什么都不做;如果是“循环”,则返回当前的声音;如果是“前进”,则将当前的 ID 增加 1,返回下一个声音 ID。
通过这种方法,程序就不再需要处理复杂的资产重映射。声音加载和播放将按链式规则正常工作。接下来需要调试这一部分代码,确保不会出现新问题。由于数据结构发生了变化,需要重新运行导出器(exporter),以确保新的数据结构能够正确导出并设置。
修复这些问题后,程序变得更为简洁和高效,不再需要大量的手动书写和复杂的操作,避免了不必要的繁琐工作。现在,程序能够按预期进行工作,避免了此前资产重映射所带来的复杂性。
接下来计划的是进行 QA 测试,确认程序是否按预期运行,并且没有出现更多的问题。QA 测试会检查程序的稳定性和是否存在其他潜在的问题。至于接下来的工作,会集中在实现一个新的功能,处理 Win32 系统中的文件操作,这对于文件管理和操作非常重要。由于这部分工作相对复杂,开发人员希望以合适的方式实现它,因此需要更多的准备和规划。






可以先做advance然后再做循环声音吗?
可以实现“advance”和“循环”声音的组合。每个声音都可以独立设置这些值,因此可以自由组合,不存在任何限制。如果没有特定的需求,单纯地想要尝试一些新的功能,完全可以进行实验。比如,在进行学习时,不必总是专注于完成具体的任务或目标,反而可以通过尝试不同的功能,探索更多的可能性,这有助于提高自己的编程能力。
一种可能的做法是,可以将“链式操作”更具体化,而不仅仅是用一个 uint32 值表示。可以将其拆解成两个部分:一个是命令类型(比如“无操作”、“循环”或者“前进”),另一个是计数器。例如,如果设置为“循环”,则可以设置一个循环计数器,在声音播放的代码中,每次播放相同的声音时,都会增加计数器的值。每次播放时,检查声音的当前循环次数,如果超过预定次数,则执行“前进”操作。
通过这种方法,可以实现更复杂的功能,比如“前进-前进-循环-循环-前进”这样的音频序列。虽然这样做可以实现一些非常有趣的功能,但通常在实际开发中并没有太多的实际需求。这种实现方式更多是为了实验和探索。如果只是为了学习和理解编程原理,这样的尝试是非常有价值的。
如果你做 id.value += 1,你最终不会循环到最后一个声音资产吗?
这个问题的关键是防止在播放完最后一个音频资源时出现无限循环。实现的思路是:在播放每个音频时,先设置为“前进”状态,不断播放下一个音频,直到最后一个音频。当播放到最后一个音频时,状态会被设置为“无操作(none)”,从而结束循环。这样可以避免无限循环播放。
如果出现循环未结束的问题,那么可能是资产构建时出现了bug,因为音频播放控制逻辑是设置为“前进”直到最后一个音频,最后才结束。而如果存在多个bug,可能会导致播放逻辑没有正确终止,尤其是当音频播放速度加快时,问题可能更容易复现。
检查在音调偏移的声音中,当 SamplesPlayed 超过 SamplesCount 时引发的断言
目前遇到的问题是关于声音播放结束时的精度问题。样本播放的位置设置为结束样本位置,但实际上样本位置并没有准确到达音频的结尾,通常会稍微短一些。经过查看,发现这是由于在计算总混音块时,使用了浮点数精度问题,导致在某些情况下音频没有完全播放完毕。具体来说,当计算音频的总混音块时,存在一些舍入和截断的误差,导致最终结束的位置稍微提前了。
这个问题尤其在进行音高调整时更为明显,但通常不会对音效产生太大影响。尽管如此,它还是带来了一些小的瑕疵,可能会导致音频播放的结束点不完全正确。为了解决这个问题,可能需要回头重新审视和优化音频块的计算方式,确保在每次播放时都能精确地处理这些细节,避免任何潜在的误差。
此外,当迁移到新的资源文件处理系统时,也引入了一些新的小故障,尤其是音频结束处理的部分。这些问题还需要进一步修复和优化,以确保音频处理的稳健性,并确保音效的结尾部分正确无误。因此,接下来可能需要更多时间来清理这些问题,确保声音系统的每个环节都能够无误地运行。

下一步计划是什么?
接下来计划做的事情主要集中在两个方面。在处理完资产系统之后,接下来的工作将是实现调试功能和灯光系统。
首先,调试功能是目前任务列表中的一个重要内容。随着游戏复杂性的增加,调试功能变得非常必要。这包括能够绘制调试信息、显示调试输出以及开发调试用的HUD(用户界面显示)。这些调试基础设施将帮助开发过程中更容易地发现和解决问题。
第二个重要的技术任务是实现灯光系统。灯光对于游戏的视觉效果和氛围非常重要,因此需要在此阶段加以完善。
完成这两项任务后,团队将进行一些清理工作,修复现有的bug,并进一步巩固和优化引擎。这将确保引擎已经足够稳定,可以开始游戏原型的开发。接下来就可以着手进行游戏的核心内容开发,例如游戏的介绍部分、地图生成、战斗系统等功能的实现。
一旦这些基础工作完成,便可以进入更具体的游戏开发阶段,准备好开始制作游戏的各个部分。
你如何决定是修复一个 bug 还是继续前进?
在处理是否修复一个bug或者继续前进的问题时,实际的答案并不完全如在直播中展示的那样。最理想的做法是“立即修复bug”,这是生产环境中的最佳实践。通常在生产编程中,一旦发现bug,就应立即修复,除非:
- 代码正在被删除:如果你正处于删除某段代码的过程中,修复bug就没有意义,因为这段代码即将被移除。
- 正在修复其他bug:如果你正在处理另一个更紧急的bug,可能暂时无法解决当前的bug,但这是唯一可以不立即修复bug的情况。
除了这两种情况外,其他情况下应该始终立即修复bug。这是一个明确的规则。如果决定不修复bug,必须确保这段代码不再使用,或者会被删除。
然而,有时为了教学需要,会做出一些妥协。由于时间和内容的限制,有时会将一些事情推迟处理,而不遵循最佳的编程实践。直播的目标是教学,但也需要考虑如何更好地安排每一部分的内容。因此,有时候虽然发现了bug,但为了不打断教学进程,可能会暂时不修复,而选择将其放在稍后的时间处理。
例如,在音效代码中,虽然发现了bug,但为了继续教学资产管理代码的部分,暂时不会处理音效的bug。教学过程中需要做出一些妥协,以确保观众能够跟得上当前的教学进度,但这并不意味着这种做法是最佳的开发实践。
如果我决定将来攻读博士学位,能否获得你写关于压缩导向编程(Compression-Oriented Programming)的祝福?
对于未来的博士研究,关于压缩或编程相关的内容,完全不需要我的许可。压缩导向编程是我非常希望更多人去做的事情,我很高兴听到有人愿意在这方面写作并进行研究。希望更多人能从事这类工作。
相关文章:
游戏引擎学习第153天
仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾 目前正在进行的是一个比较大的系统调整,原本预计今天会继续深入这个改动,但实际上在昨天的开发中,我们已经完成了大部分的代码编写,并且运行之后几乎一切都能正常工作&#x…...
理解C语言中的extern关键字
在C语言编程中,extern关键字是一个非常重要的概念,尤其在多文件编程和全局变量的使用中。本文将详细解释extern的作用、用法以及常见的应用场景。 1. extern关键字的作用 extern关键字用于声明一个变量或函数是在其他文件中定义的。它告诉编译器&#x…...
【MyBatis Plus 逻辑删除详解】
文章目录 MyBatis Plus 逻辑删除详解前言什么是逻辑删除?MyBatis Plus 中的逻辑删除1. 添加逻辑删除字段2. 实体类的配置3. 配置 MyBatis Plus4. 使用逻辑删除5. 查询逻辑删除的记录 MyBatis Plus 逻辑删除详解 前言 MyBatis Plus 是一个强大的持久化框架…...
latex问题汇总
latex问题汇总 环境问题1 环境 texlive2024 TeXstudio 4.8.6 (git 4.8.6) 问题1 编译过程有如下错 ! Misplaced alignment tab character &. l.173 International Conference on Infrared &Millimeter Waves, 2004: 667--... I cant figure out why you would wa…...
基于Redis实现限流
限流尽可能在满足需求的情况下越简单越好! 1、基于Redsi的increment方法实现固定窗口限流 Redis的increment方法保证并发线程安全窗口尽可能越小越好(太大可能某一小段时间就打满请求剩下的都拿不到令牌了)这个原理其实就是用当前时间戳然后除窗口大小 在这个窗口大…...
力扣练习之确定两个字符串是否接近
目录 题目: 题解: 详细题解 题目: 如果可以使用以下操作从一个字符串得到另一个字符串,则认为两个字符串 接近 : 操作 1:交换任意两个 现有 字符。 例如,abcde -> aecdb 操作 2࿱…...
大三下找C++开发实习的感受分享
目录 找实习的过程 阶段一:投简历 阶段二:准备面试 阶段三:面试中 阶段四:面试结束后 面试真题 总结 找实习的过程 阶段一:投简历 第一次找实习还是使用BOSS这个软件进行投简历,这个过程其实挺难说…...
基于hive的电信离线用户的行为分析系统
标题:基于hive的电信离线用户的行为分析系统 内容:1.摘要 随着电信行业的快速发展,用户行为数据呈现出海量、复杂的特点。为了深入了解用户行为模式,提升电信服务质量和精准营销能力,本研究旨在构建基于 Hive 的电信离线用户行为分析系统。通…...
Makefile——make工具编译STM32工程
一、Makefile相关指令 1.1、变量 符号含义替换追加:恒等于 1.2、隐含规则 符号含义%.o任意的.o文件*.o所有的.o文件 1.3、通配符 符号含义$^所有依赖文件$所有目标文件$<所有依赖文件的第一个文件 1.4、编译器指令常用参数功能说明 符号含义举例-E预处理,…...
Java EE 进阶:SpringBoot 配置⽂件
什么是配置文件 “配置文件”是一个用来保护程序或者系统设置信息的文件,它的作用是让程序在启动或者运行中,能够读取这些设置并按预期进行工作,而不需要手动的设置。 Spring Boot 配置文件 设置服务器端口、编码格式配置数据库连接控制日…...
【redis】五种数据类型和编码方式
文章目录 五种数据类型编码方式stringhashlistsetzset查询内部编码 五种数据类型 字符串:Java 中的 String哈希:Java 中的 HashMap列表:Java 中的 List集合:Java 中的 Set有序集合:除了存 member 之外,还有…...
基于Python的电商销售数据分析与可视化系统实
一、系统架构设计 1.1系统流程图 #mermaid-svg-Pdo9oZWrVHNuOoTT {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Pdo9oZWrVHNuOoTT .error-icon{fill:#552222;}#mermaid-svg-Pdo9oZWrVHNuOoTT .error-text{fill:#5…...
色板在数据可视化中的创新应用
色板在数据可视化中的创新应用:基于色彩感知理论的优化实践 引言 在数据可视化领域,色彩编码系统的设计已成为决定信息传递效能的核心要素。根据《Nature》期刊2024年发布的视觉认知研究,人类大脑对色彩的识别速度比形状快40%,色…...
EB-Cable许可管理中的数据安全与隐私保护
在数字化时代,数据安全与隐私保护已成为企业关注的重中之重。作为专业的电缆管理软件,EB-Cable许可管理不仅在功能丰富和操作便捷方面表现出色,更在数据安全与隐私保护方面为用户提供了坚实的保障。本文将详细介绍EB-Cable许可管理在数据安全…...
解决ubuntu(jetpack)系统下系统盘存储不够的
以下是可以安全清理的内容及操作步骤,按优先级和风险从低到高排序: 1. 清理日志文件(低风险) /var/log/syslog (7.1G) # 清空syslog文件(不删除文件本身) sudo truncate -s 0 /var/log/syslog# 或限制sys…...
【无人机路径规划】基于麻雀搜索算法(SSA)的无人机路径规划(Matlab)
效果一览 代码获取私信博主基于麻雀搜索算法(SSA)的无人机路径规划(Matlab) 一、算法背景与核心思想 麻雀搜索算法(Sparrow Search Algorithm, SSA)是一种受麻雀群体觅食行为启发的元启发式算法࿰…...
STM32_GPIO系统外设学习
按照STM32MCUWIKI、参考手册的外设介绍----->CubeF4的软件包中相关的Exmple代码----->CubeMX设置截图加深理解记忆 资料链接:嵌入式开发_硬软件的环境搭建 我的飞书文档-GPIO篇 如果觉得内容不错,欢迎给我的飞书文档点赞。同时如果有什么意见或…...
使用Java爬虫根据关键词获取衣联网商品列表:实战指南
在电商领域,通过关键词搜索商品并获取商品列表是常见的需求。衣联网作为知名的电商平台,提供了丰富的服装商品资源。本文将详细介绍如何使用Java编写爬虫程序,根据关键词获取衣联网商品列表,并确保爬虫行为符合平台规范。 一、环…...
【操作系统安全】任务1:操作系统部署
目录 一、VMware Workstation Pro 17 部署 二、VMware Workstation 联网方式 三、VMware 虚拟机安装流程 四、操作系统介绍 五、Kali 操作系统安装 六、Windows 系统安装 七、Windows 系统网络配置 八、Linux 网络配置 CSDN 原创主页:不羁https://blog.csd…...
下载安装启动 VMware 个人免费版本
一、进入官网并登录账号下载软件 进入官网 [ https://www.vmware.com ],点击Products,将页面划到最底下,点击 “SEE DESKTOP HYPERVISORS”按钮。 然后点击 Desktop hypevisor ,会出现如下界面,可以根据自己的操作系…...
C#+AForge 实现视频录制
C#AForge 实现视频录制 在C#中,使用AForge 库实现视频录制功能是一个比较直接的过程。AForge 是一个开源的.NET框架,提供了许多用于处理图像和视频的类库。 开发步骤 安装AForge库 首先,确保你的项目中已经安装了 AForge.Video和AFo…...
doris:外表统计信息
外表统计信息的收集方式和收集内容与内表基本一致,目前支持对 Hive,Iceberg 和 Hudi 等外部表的收集。 自 2.0.3 版本之后,Hive 外表支持了自动和采样收集。 注意事项 HMS 类型的 Iceberg 和 Hudi 外表,以及 JDBC 外表只支持手…...
SAP SD学习笔记31 - 销售BOM
上一篇讲 前受金处理(预付款处理)。 SAP SD学习笔记29 - 前受金处理(预收款处理)_fplt 付款申请与sd 数据表的关联关系-CSDN博客 本章继续讲SAP SD模块的其他知识:销售BOM。 销售BOM在现场还是会用到的。 目录 1,销售BOM概要 2,受注BOM的…...
大数据学习(63)- Zookeeper详解
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦🤞 …...
嵌入式八股C语言---面向对象篇
面向对象与面向过程 面向过程 就是把整个业务逻辑分成多个步骤,每步或每一个功能都可以使用一个函数来实现面向对象 对象是类的实例化,此时一个类就内部有属性和相应的方法 封装 在C语言里实现封装就是实现一个结构体,里面包括的成员变量和函数指针,然后在构造函数中,为结构体…...
Android UI性能优化
Android UI性能优化 一、UI性能优化基础 1.1 UI渲染原理 Android系统的UI渲染是通过一个被称为"UI线程"或"主线程"的单线程模型来完成的。系统会以16ms(约60fps)的固定时间间隔发送VSYNC信号,触发UI的渲染流程。如果一帧的处理时间超过16ms,就会出现丢…...
C# ListView设置标题头背景颜色和字体颜色
一、向ListView 添加数据 for (int i 1; i < 5; i) {ListViewItem litem new ListViewItem("data:"i);lv_WarnList.Items.Add(litem); }如果需要在ListView中绑定实体类对象的话,需要将数据放在Tag属性里 for (int i 1; i < 5; i) {AngleData …...
数字统计(信息学奥赛一本通-1096)
【题目描述】 请统计某个给定范围[L, R]的所有整数中,数字2出现的次数。比如给定范围[2, 22],数字2在数2中出现了1次,在数12中出现1次,在数20中出现1次,在数21中出现1次,在数22中出现2次,所以数…...
嵌入式 ARM Linux 系统构成(6):应用层(Application Layer)
目录 一、应用层概述 二、应用层的核心组成 2.1 主应用程序(Main Applications) 2.2 系统服务(System Services) 2.3 用户界面(User Interface) 2.4 脚本与自动化工具 2.5 第三方库与框架 2.6 通信…...
【HTML】一、基础标签
文章目录 1、开发环境准备2、html介绍3、html基本骨架4、标签的关系5、常用标签5.1 标题5.2 段落5.3 换行与水平线5.4 文本格式化标签5.5 图像标签5.6 超链接标签5.7 音频标签5.8 视频标签 6、路径7、网页制作 1、开发环境准备 在编辑器中写代码,在浏览器中看效果 …...
