惠州网站建设制作建设网站怎么挣钱
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:50
当前位置: 首页 > news >正文
惠州网站建设制作,建设网站怎么挣钱,湖北seo推广,厦门广告公司电话回顾并为今天的内容做准备
目前代码的进展到了声音混音的部分。昨天我详细解释了声音的处理方式#xff0c;声音在技术上是一个非常特别的存在#xff0c;但在游戏中进行声音混音的需求其实相对简单明了#xff0c;所以今天的任务应该不会太具挑战性。
今天我们会编写一个…回顾并为今天的内容做准备
目前代码的进展到了声音混音的部分。昨天我详细解释了声音的处理方式声音在技术上是一个非常特别的存在但在游戏中进行声音混音的需求其实相对简单明了所以今天的任务应该不会太具挑战性。
今天我们会编写一个非常基础的声音混音器首先确保它能正常工作接着思考一下它的接口应该是什么样的。接下来可能会在下周进行一些优化调整并使其更加符合实际需要。我们可能会进行一些简化以确保它在运行时的表现良好但不会对其进行过多优化因为我们更关注的是实现它能够正常运行。
回顾一下我们上次停下的地方。昨天我们详细讨论了声音处理但在之前的日子里我们加载了声音文件并能够播放我们选择的任何一个文件。现在代码中声音播放的是一段钢琴音频采样。
现在我们有一个当前的设置方式可能不是最好的选择。我们平台代码的设置是如果没有达到目标帧率音频会发生撕裂现象。举个例子如果没有达到帧率音频会跳过一些帧在调试模式下我们就能看到音频中的跳过然而如果在优化模式下音频播放就平滑了。问题的原因是我们目前没有做任何处理来避免这些问题。我们假设总是能保持目标帧率并尽可能填充音频样本以补偿任何可能的帧率缺失。
当前如果屏幕刷新频率是165Hz的话
让从电脑获取刷新频率来计算
(debug模式运行程序音频有撕裂的现象) (release模式正常)
接下来我们要考虑做的工作是我们现在有一个播放音频的循环功能很简单就是将加载的声音样本复制到输出缓冲区中。而我们的目标是将这个简单的操作扩展成可以在任意时间播放多个声音的系统同时将它们混音成一个合理的音频输出这样我们就能够同时播放多个声音而无需担心它们之间的冲突。
为了实现这一目标首先要清楚我们目前的代码结构。在当前的设置中播放音频样本是通过单独的调用来实现的之所以这么做是因为我们最开始想让它与游戏状态的其他部分分开考虑到将来可能会将其单独放到线程中运行。虽然目前还没有决定是否将其放在不同的线程中但我们首先假设声音样本的生成和游戏的其他部分是同步进行的。
当然未来可能会考虑将它拆分到独立线程中但现在我们假设声音和游戏的其他部分同步运行先不考虑线程和锁的问题之后再看是否需要对这一点做出改变。
game.h: 引入 playing_sound 并在 game_state 中添加一个指针
首先我们需要了解当前正在播放的所有声音因此在 game.h 文件中我们可能需要在 game_state 中添加一些内容来记录当前播放的所有声音。这里有多种方式可以实现这一点。一种合理的方法是使用链表我们可以将正在播放的声音添加到链表中然后假设链表中的每个节点代表一个正在播放的声音。另一种方法是使用固定数量的槽数组我们只允许在这些槽中播放声音。如果所有槽都已满新的声音就无法播放。
为了解决这个问题首先我决定使用链表因为它是最简单直观的方式。我们可以创建一个名为 playing_sound 的结构体并使它具有链表功能使每个声音都有一个指向下一个声音的指针。然后我们会在 game_state 中添加一个指针指向链表的开头命名为 PlayingSounds。
在 playing_sound 结构体中我们希望每个声音有以下信息 声音ID每个声音都会有一个唯一的标识符这是为了区分不同的声音类似于我们之前在渲染中使用的ID。通过这个ID我们可以从音频资源中提取正确的声音样本进行播放。 音量我们需要一个音量值表示当前声音的音量大小这个值介于0和1之间0表示静音1表示原始声音的最大音量。这个值可以控制声音的强度。 左右声道音量因为我们使用的是立体声音频我们还需要分别控制左右声道的音量。因此我们可能需要存储左右声道的音量值允许分别调整左右声道的音量。 播放位置我们还需要跟踪当前声音播放的位置也就是已经播放了多少个音频样本。这是为了确保在播放声音时我们知道已经播放了多少以免重复播放同样的样本。每个声音会有一个“播放游标”来记录当前播放到的样本位置。
基于这些需求我们的 playing_sound 结构体大致会包含以下字段
sound_id ID当前播放的声音的ID。real32 Volume[2]声音的整体音量0到1之间。left_volume 和 right_volume分别控制左右声道的音量。SamplesPlayed记录已播放的音频样本数量。
这些信息可以帮助我们管理音频的播放并确保能够正确地混合多个声音。如果需要支持声音的循环播放或更多功能后续可以继续扩展。
接下来我们将通过链表管理这些 playing_sound 实例并在每个更新周期遍历链表播放当前的声音。
game.cpp: 设置 PlayingSound 循环
在这个循环中首先需要做的是从 game_state 中获取到第一个正在播放的声音。接着检查这个声音是否有效。如果有效就对这个声音进行处理。每次处理完当前声音后就移动到链表中的下一个声音继续处理所有正在播放的声音直到遍历完所有声音。
除此之外还可以考虑提前排队一些样本数据。例如我们可以允许设置已播放的样本数为负数这样就能够实现延迟播放的功能。通过这种方式可以将某个声音的播放推迟一段时间比如延迟半秒钟再开始播放。虽然这种延迟播放功能不一定是必要的但如果需要实现可以通过负值来标记推迟的播放时间。
接下来对于每个需要输出的音频样本我们的目标是将这些声音样本输出到最终的音频缓冲区。最初的循环仅仅是将16位的样本数据复制到16位的输出数据中这种方式虽然简单但可能会导致音频样本值的裁剪clipping问题。如果我们直接使用16位的值作为中间缓冲区来累加音频样本数据可能会遇到溢出或裁剪问题因为累加的音频值可能超出了16位的表示范围。
为了避免这种问题我们可以考虑使用更高精度的缓冲区比如32位或更高位深的缓冲区来存储音频样本的中间累积值。这样做可以减少裁剪的概率并且在最终将这些累积值转换成16位输出时能够更好地控制音频的动态范围避免失真。
总结来说在输出音频样本时需要确保处理链表中的所有播放中的声音并在处理过程中考虑延迟播放、避免裁剪等问题。最终的目标是通过更高精度的缓冲区避免音频失真并将处理后的样本正确输出。 Blackboard: 在32位深度混音缓冲区中工作
在进行声音混合时如果我们使用16位音频进行处理可能会遇到裁剪clipping问题。假设有两个声音第一种声音的波形第二种声音的波形当将这两种声音相加时结果会超出16位的表示范围导致裁剪发生。即使将两种声音叠加它们的值可能会超出16位的最大值从而发生失真。
然而如果我们再加入第三种声音假设第三种声音当我们将它与前两个声音相加时第三个声音的波形可能会减弱前两个声音的影响使得最终的结果回到16位范围内。这样就避免了裁剪问题。也就是说多个声音的叠加并不一定总是会导致裁剪因为不同声音的波形可能会互相抵消从而将总和控制在可表示的范围内。
因此通常在处理16位音频时为了避免裁剪问题应该在32位精度的空间中进行混合处理。这样能够确保即使多个声音叠加最终结果也不会溢出16位的范围。
在实现过程中使用浮点数float是一个不错的选择因为浮点数能够更方便地进行动态范围调整和其他的调制操作。在混合过程中我们可以将16位音频数据转换为浮点数进行处理完成混合后再将结果转换回16位这样能够确保音频的质量并避免裁剪。
总结来说处理音频时最好使用32位浮点数作为中间缓冲区先将16位数据转换为浮点数进行混合最后再将混合后的结果转换回16位数据输出这样能够避免音频裁剪并保持较好的音质。
game.cpp: 引入 RealChannel0 和 RealChannel1
在这个过程中我们希望能够创建一个真实的声音缓冲区并且这个缓冲区是针对每个声道的。对于立体声来说我们会有两个声道即左声道和右声道。所以我们需要为每个声道分别处理缓冲区的数据。
为了实现这一点我们将为每个声道分配一个浮点数类型的缓冲区因为使用浮点数有助于我们在混音过程中避免裁剪问题。每个声道的缓冲区将用来存储混合后的音频数据。
具体步骤如下 初始化缓冲区我们将为每个声道例如左声道和右声道创建一个缓冲区。每个缓冲区中的数据将以浮点数的形式存储这样我们可以避免16位音频格式可能带来的裁剪问题。 数据转换从加载的声音文件中提取每个样本首先会将其转换为浮点数格式。然后使用浮点数表示的缓冲区进行混合操作。 混合处理我们将逐个采样地处理每个声音的样本并将它们写入到相应的声道缓冲区中。通过这种方式我们在混音过程中能够保持较高的音质并且避免在处理多个声音时出现裁剪现象。 最终输出混合操作完成后我们将得到一个包含所有声音数据的缓冲区它们已经被转换为浮点数格式并进行了合适的混合。接下来我们可以将这些数据转换回16位音频格式准备输出到音频硬件。 game_asset.h: 引入 GetSound
在这个过程中需要先确保有一个获取音频数据的功能。首先代码中并没有直接实现获取声音的函数因此需要添加一个新的函数来获取声音。
步骤如下 创建获取声音的函数目前代码中并没有 GetSound 函数因此需要编写这个函数。该函数的作用是根据声音的 ID 获取对应的音频数据。 调用获取函数在处理播放的声音时需要通过该 GetSound 函数获取到对应的声音数据。通过声音的 ID 提取出声音文件数据为接下来的混音操作提供数据来源。 使用声音 ID每个播放中的声音都有一个唯一的声音 ID通过声音 ID 可以访问到对应的声音资源。这个 ID 存储在每个播放中的声音对象里作为获取该声音数据的关键。
在这个步骤中最重要的是确保获取音频数据的功能能够正常工作以便后续对音频进行混音和处理。
game.cpp: 调用 GetSound 并在循环中对样本进行求和
首先加载声音的过程需要从资源中获取。资源会存储在 ttransient_state瞬态状态中具体来说是它的 groups 和 TransientStorage 中存储了所需的音频资产。获取声音时需要检查LoadedSound是否已加载声音如果未加载便无需继续处理。若未能获取到声音数据则可以跳过相关处理。
为了确保声音在播放时不出现突兀的响声建议在加载音频时调用 LoadSound 函数这样可以确保音频尽快准备好播放。另一方面为了避免声音出现“爆音”现象可以在播放前设置音量渐入效果逐渐将音量调到正常水平。实际上最好的方法可能是延迟播放直到音频文件加载完毕避免音频未准备好就开始播放。
处理过程中对于未LoadedSound使用一种方法延迟音频播放直到所有数据准备完毕。通过调整播放进度和播放样本数可以避免音频处理错过任何样本。这个策略可以确保音频的顺畅播放而无需担心遗漏或不准确的播放。
在声音加载完成后需要遍历音频的样本并进行加总。每个样本的处理方式是首先将该样本的值与当前通道的Volume音量值相乘再进行加总。每个通道的音量例如 Volume0 和 Volume1应该提前提取并清晰标记以便确保音量的控制在正确的通道中应用。
要从加载的声音样本中提取特定样本值时可以通过计算样本的索引位置来获取该样本。每个音频样本的位置由 SampleIndex 和 SamplesPlayed播放样本数决定因此需要将它们相加得到准确的样本索引。
另外在处理音频时必须考虑到边界情况。例如可能需要处理立体声的特殊情况stereo。这种特殊情况会影响到如何处理样本值特别是在多个通道或样本位置不一致时。
处理声音加总的过程时如果多个声音同时播放我们需要保证清除旧的音频数据否则加总结果可能不准确。特别是在每次新的音频播放开始前需要先清空音频通道中的原有数据确保加总过程从零开始。这样音频加总将不会受到先前数据的干扰。
为了简化初步实现避免对第一次播放的声音进行特殊处理可以在每次处理时统一方法即对每个声音的处理方式一致而无需对首个声音进行特别优化。为了确保加总结果正确可以在加总前将所有的通道数据清零。
以上步骤可以确保音频的加载、播放、加总和音量处理能够平稳进行从而避免音频播放时出现卡顿、爆音或其他异常情况。
game.cpp: 循环遍历求和后的声音从中读取并将其写入 SampleOut 缓冲区
接下来我们需要完成一个循环来处理所有的音频数据。具体来说现在已经有了所有声音的加总结果并且这些加总后的音频数据已经存储在内部缓冲区中。接下来的任务是从这些缓冲区中读取出加总后的值并将其写入输出缓冲区。
首先我们需要确保音频数据从内部缓冲区正确地转换并输出为16位的音频数据。具体来说就是从每个通道的源缓冲区中读取数据然后进行四舍五入确保数据格式正确。四舍五入后的值将转化为16位整数这就是最终要写入输出缓冲区的音频数据。
在左声道和右声道中分别从源缓冲区如源0、源1读取数据并进行四舍五入。这一步是为了确保输出的音频数据精度符合16位的要求并避免任何意外的截断或误差。
在数据处理过程中原先的测试索引已经不再需要因为现在不再依赖于这些索引来处理音频而是直接通过内存中的加总数据进行处理。
接下来需要确保处理过程的区域是有效的。我们需要约束处理的音频样本范围避免越界或无效的内存访问。为此可以从瞬态内存transient memory中分配临时内存以确保内存管理的正确性。通过从瞬态内存中分配内存块确保了我们在混音过程中有足够的内存来存储数据并且内存释放也会在混音完成后自动进行。
为了管理这些临时内存可以创建一个临时的音频混音缓冲区并在其中进行样本的处理。这些缓冲区将用于存储混音过程中所需的数据。每当需要使用内存时从瞬态内存中申请相应的内存块处理完后再释放回去。
在此基础上清理缓冲区中的音频数据以便开始新一轮的混音操作。然后对所有声音进行加总最后将加总后的数据转换为16位格式确保音频数据符合输出要求。
总结一下这个过程的核心包括
从源缓冲区读取音频数据并进行四舍五入处理将四舍五入后的数据转换为16位整数在处理过程中管理内存确保临时内存的正确分配和释放对所有声音进行加总并将结果写入输出缓冲区。
这些步骤确保了音频的正确处理避免了数据溢出或内存错误并确保最终输出的音频数据格式正确。 game.cpp: 引入 SamplesToMix 和 SamplesRemainingInSound以处理音频的有限性
目前混音器的主要部分已经基本完成接下来需要处理一些细节问题特别是关于声音时长的管理。
首先需要考虑声音的时长问题。当进入音频处理循环时每个加载的声音LoadedSound都具有特定的样本数量SampleCount。在处理过程中如果样本索引超出了这个样本数量意味着已经到达了声音的末尾。如果继续读取超出范围的数据可能会访问到错误的内存区域。虽然不会导致程序崩溃因为内存通常是连续分配的但会导致音频流中出现错误的数据从而产生杂音或其他不良效果。因此需要一种机制来判断声音是否已经播放到结尾以防止这种情况的发生。
为了解决这个问题可以引入“待混合样本数”SamplesToMix的概念。初始时待混合的样本数量默认等于输出缓冲区的大小。但在计算时需要检查实际可用的样本数即当前声音剩余的样本数。具体计算方式如下
计算当前声音已经播放的样本数 SamplesPlayed。计算该声音的总样本数 SampleCount。通过 SampleCount - SamplesPlayed 得到该声音还剩余多少样本可以被混合SamplesRemainingInSound。
如果待混合样本数大于该声音剩余的样本数则需要调整待混合样本数使其不会超出实际范围。然后在处理循环时就可以使用这个调整后的 SamplesToMix避免访问超出范围的内存。
调整播放样本数后还需要进一步处理播放列表中的声音。当混音完成后需要更新 SamplesPlayed即增加已播放的样本数量。如果发现某个声音已经播放到了末尾就应该从播放列表中移除它。否则它会一直留在列表中导致播放列表不断膨胀占用不必要的资源甚至可能影响程序性能。因此需要在适当的时机清理已播放完毕的声音。
这部分处理的核心步骤包括
计算剩余样本数确定当前声音还有多少样本可以播放。调整待混合样本数确保不会超出声音的实际数据范围避免访问无效内存。更新已播放样本数记录已经混合的样本数量确保下一帧的计算正确。移除播放完毕的声音如果某个声音已经完全播放完毕就将其从列表中删除以保持播放列表的整洁和高效。
这一机制保证了混音过程的稳定性同时优化了资源管理使得播放列表不会无限增长提高整体性能。
game.h: 在 game_state 中添加 playing_sound *FirstFreePlayingSound
为了管理播放列表需要在播放结束时从列表中移除已播放完毕的声音并将其加入一个空闲列表free list以便后续复用。当需要播放新的声音时可以直接从空闲列表中取出一个已释放的声音对象而不是重新分配新的内存从而提高资源利用率和性能。
具体来说在移除声音时首先要确保它不会再被使用然后将其指向空闲列表的头部例如 FirstFreePlayingSound。这样下一次需要播放新声音时可以直接从空闲列表中取出一个已有的结构而不必重新创建新的实例。这种做法减少了内存分配和释放的开销提高了播放管理的效率。
game.cpp: 在 PlayingSound 循环中使用 FirstFreePlayingSound
为了优化声音播放管理在移除已播放完毕的声音时需要将其加入空闲列表以便后续复用。具体实现方式是利用链表结构将移除的声音节点添加到空闲列表的头部这样可以快速回收并复用内存而无需频繁申请和释放新内存。
在执行这一操作时需要使用 PlayingSound 结构中的 Next 指针。具体步骤如下
取出当前游戏状态中的 FirstFreePlayingSound即当前空闲列表的头部。将即将被释放的 PlayingSound 节点的 Next 指向 FirstFreePlayingSound将其链接到空闲列表的最前端。更新 FirstFreePlayingSound 指针使其指向新的空闲节点即刚刚释放的 PlayingSound。
通过这种方式可以确保被移除的声音不会被遗忘而是被回收到空闲列表中等待下一次需要播放新声音时直接复用。这不仅减少了内存分配和释放的开销还提高了声音管理的效率使得播放列表不会无限增长同时减少了系统资源的浪费。 game.cpp: 提前固定 Next 指针以防止其被释放时影响循环迭代
在遍历播放列表时如果在循环过程中移除当前正在处理的声音对象会导致迭代出现问题。因为 playing sound 被移除后其 next 指针指向的内容可能已经改变导致后续遍历发生错误。因此需要在移除之前先保存 next 指针以确保迭代能够正确进行。
具体实现方式如下 提前获取下一个播放声音节点 在处理当前 PlayingSound 之前先将 Next 指针保存到一个独立的变量 NextPlayingSound这样即使当前 PlayingSound 被移除其 Next 仍然可以被访问。 在循环结束时使用已保存的 Next 指针 在完成当前节点的处理后使用预先存储的 NextPlayingSound 作为新的迭代对象而不是直接访问 PlayingSound-Next这样就不会受到节点被释放的影响。
这种方法在需要从链表中移除元素的迭代过程中非常常见主要目的是防止因节点删除导致的访问错误提高代码的稳定性和可维护性。
此外还添加了一个断言确保 PlayingSound-SamplesPlayed 始终大于等于 0。这样如果未来需要支持声音延迟播放代码会提醒需要对混音逻辑进行额外调整以正确处理延迟情况。目前暂时不支持延迟播放但为后续扩展留出了可能性。
game.h: 引入 MetaArena 概念
当前代码中存在一些需要解决的问题尤其是在内存管理方面。
- 现存的问题游戏状态的内存管理 目前在 game_state 中存在两种不同的内存分配区域 世界World相关的内存区域 这个区域用于存储与当前世界游戏场景相关的数据当世界被销毁时该区域的所有数据都会被释放。长期存活Meta区域 这个区域存储一些需要在多个世界之间持久存在的数据例如音乐或音效。因为当玩家从一个世界退出回到主菜单时背景音乐不应该停止因此音效不应与世界的内存区域绑定而应该属于一个更持久的区域。 目前并没有正式区分这两种内存区域但未来可能需要引入 “世界内存”会随世界销毁而释放和 “元内存”会跨世界持续存在来更好地管理游戏状态中的不同数据。虽然现在可以暂时不处理这个问题但之后应该在架构层面进行改进以适应更复杂的游戏需求。 2. 声音加载逻辑尚未完成 当前的 load_sound 相关代码似乎尚未完全实现存在以下问题 load_sound 函数被调用后尚未正确地在 asset_system 中完成音效的加载。具体来说SoundInfos 变量的 FileName 是否正确赋值仍存疑可能导致加载逻辑无法正确执行。SoundInfos音效信息数组似乎没有被正确填充导致声音数据实际上并未正确加载到内存中。sound first 变量目前也没有正确指向任何数据这意味着音效数据结构的初始化可能未完成。 3. 可能的解决方案 内存管理优化 需要在 game state 中正式引入 长期存活的内存区域Meta Arena将跨世界存活的音效存储其中。目前的 World Arena 仍然保留用于存储随世界创建和销毁的临时数据。未来可能还需要引入 动态管理机制例如在 Meta Arena 中进行手动释放以避免长期运行导致的内存泄露。 改进声音加载逻辑 需要确保 load sound 代码能够正确解析 sound info 并填充相应的数据结构。应该检查 SoundInfos 是否在 asset system 中正确初始化并存储音效数据。如果 Info 变量的 FileName 未被正确赋值需要找到 LoadSound 的具体调用流程并确保 FileName 的来源正确。 4. 结论 目前的代码在内存管理和音效加载方面仍然有一些未完成的部分 内存管理方面 需要区分 World Arena 和 Meta Arena以确保音效数据不会因世界的销毁而被错误释放。声音加载方面 需要检查 LoadSound 相关代码确保 SoundInfos 被正确初始化并存储音效数据。未来需要进一步完善 资源管理系统包括加载、释放、回收等机制以提高整体稳定性和扩展性。 game_asset.h: 将音频资源添加到 asset_type_id 目前的代码正在处理游戏中的音效资源并尝试将它们纳入系统中进行管理。
- 组织音效资源
在现有的资源管理中已经有了一个存放 图片bitmaps 的区域现在需要以类似方式管理 音效sounds。因此代码中新建了一个 sounds 目录并开始往其中填充一些初步的音效资源。这些音效文件包括
bloop //轻微的弹跳声drop //物体掉落glide //滑行music //背景音乐(BGM)pup //获取道具、能力提升power-up
这些音效文件可能只是测试用的资源而非最终游戏中的正式音效但目前会先将它们纳入系统进行管理和测试。 2. 加载音效
代码正在尝试从 sounds 目录中读取音效文件。其中一个音效文件体积较大大约 30MB但目前先默认接受它不对大小进行特殊处理。具体的加载过程暂时未详细展开但整体目标是确保所有列出的音效都能被正确读取并在游戏中使用。 3. 未来的优化方向 音效管理系统完善 可能需要为音效创建一个 缓存系统避免重复加载相同的音效文件提高加载效率。需要考虑 音效格式的转换或压缩特别是大体积的音效可能会影响加载速度和内存占用。 音效的分类和使用 目前的音效名称bloop、drop、glide 等可能只是测试用最终需要根据游戏需求重新设计音效库。未来可能会引入 不同类型的音效背景音乐、环境音效、UI 交互音效等并对其进行分类管理。 动态音效加载 如果游戏存在大量音效可能需要动态加载和卸载而不是一次性全部加载到内存中以优化资源管理。 4. 结论
当前代码已经初步引入了 音效资源管理并将部分测试音效纳入系统。虽然目前只是简单地将目录中的文件加载进来未来仍需要对 音效格式、加载方式、内存管理 等方面进行优化以提升游戏的音效系统稳定性和性能。
game_asset.cpp: 扩展资源加载处理音频 当前的任务是将音频资源的处理方式扩展使其与之前的资产系统保持一致。现阶段我们尚未定义完整的资源包文件格式和目录结构因此暂时通过代码直接构造音频资产后续会改为从磁盘加载。 目前我们已经有了一些临时音效文件 bloopdropglidemusicpup 这些音频文件暂时通过手动方式加入到资源系统并按照索引进行管理例如 bloop_00.wav、bloop_01.wav 等。同样地Puhp 也有多个变体如 Puhp_00.wav 和 Puhp_01.wav。此外music 作为背景音乐资源也被加入到资产系统中。 由于当前还没有正式的资源文件格式因此这些数据的加载逻辑只是临时的。目的是为了先验证音频系统的可行性确保整个流程符合需求而不是先花大量时间去设计一个完整的资源打包格式。如果后续发现当前方式符合需求就会基于现有逻辑创建正式的资源文件格式并删除这些手写的临时代码。 在这个过程中我们的目标是 模拟资源加载 —— 先手动构造数据模拟最终的加载方式。验证功能 —— 通过手动数据确保音频系统正确运行。优化设计 —— 在功能稳定后再进行资源文件格式和加载流程的优化。 最终所有的音频资源都会存储在一个标准化的资源包文件中而不是硬编码在代码中。 game_asset.cpp: 引入 AddSoundAsset 为了扩展当前的音频资产管理系统需要创建类似于“添加位图资产” (AddBitmapAsset) 的功能但用于音频资源。具体来说需要实现一个 AddSoundAsset 的功能来将音频资产加入到资源管理中。 这个过程大致如下 添加音频资产我们将创建一个类似于 AddSoundAsset 的函数来处理音频资源的加入。这里不涉及对齐alignment因此不需要额外处理对齐的细节。调试信息与之前位图资产的调试方式类似我们需要添加调试信息来追踪加载的音频资产。比如创建一个 DEBUGAddSoundInfo 函数来记录音频资源的加载状态帮助我们跟踪音频的加载和使用情况。音频计数管理需要引入一个调试函数 DEBUGUsedSoundCount用于管理和显示当前使用的音频资源数量。通过这个函数我们能够查看音频资源的使用情况确保没有遗漏或错误。索引管理在加载音频时类似于位图资源的索引我们需要维护一个 SoundCount 来确保音频资源的正确加载和访问。这些索引将确保音频资源不会被错误地覆盖或者未正确加载。 为了确保所有的音频资源在加载时不会出问题音频数据的加载系统会在这里进行调试和优化确认加载过程没有问题。需要注意可能有些类型的索引如样本索引需要处理为正确的类型比如在代码中处理为32位整数避免发生不兼容的类型错误。 总之现在添加音频资源的功能已经准备好理论上应该能顺利地加载音频资源而不会出现问题。 game.cpp: 使新系统模拟流中的初始内容 现在的目标是确保新系统能有效模拟最初开始时的效果确保在继续开发其他功能之前已经验证了当前实现是否正常运行。因此我们首先会在游戏开始时模仿之前的音效加载方式做一些初始化工作。 具体的步骤如下 分配音效资源在游戏开始时我们需要分配一个音效资源这个资源会被设置为正在播放的音效。在初始阶段将其分配到 WorldArena 中这样可以保证它在正确的位置并且是临时使用的不会长期保留。 将音效资源连接到播放列表将这个音效资源赋值给 FirstPlayingSound这表示当前正在播放的音效。这样做可以验证音效的加载和播放是否正常。 测试加载和播放音效现在我们测试的是新实现的功能是否正常。我们希望通过简单地加载一个音效并播放来确认我们之前实现的音效系统是否能够正常工作。 改进和简化代码代码中出现了一些重复和不太清晰的部分比如在加载时需要获取第一个音效的 ID。为了避免代码冗余我们可以优化这些函数确保它们能够处理不同类型的资源如位图和音效。本质上我们希望这些函数能够通用不仅仅局限于处理位图还可以处理音效等资源。 类型安全性虽然可以让这些函数接受任何类型的资源但为了增强类型安全性可以在必要时使用包装函数。这些包装函数将为不同类型的资源提供更多的类型检查使得在整个游戏开发过程中能更有效地处理和确保类型的正确性。
总之当前的目标是确保音效系统能够正确加载和播放并且让代码更加简洁和具有通用性以便后续扩展和维护。
game_asset.cpp: 引入 GetFirstSlotID 和各种获取函数用于位图和声音 接下来我们希望对音效和位图的处理代码进行一定的改进和重构。具体来说就是通过对现有的函数进行封装使它们能够处理不同类型的资源而不仅限于位图。以下是我们要进行的一些改动和改进的步骤 封装函数我们将对现有的 GetFirstBitmapID 这类函数进行封装创建一个 GetFirstSlotID 的函数这个函数不再与位图直接相关而是通用地处理所有类型的资源。我们希望这些函数能够在不考虑资源类型的情况下返回对应的插槽 ID保持通用性。 修改相关函数类似地我们还需要对 GetRandomBitmap 和 GetRandomSlot 等函数进行相应的修改使它们能够支持不同类型的资源处理而不局限于位图。例如我们可以创建 GetRandomSound 函数这样可以通过相同的逻辑来处理音效和其他资源。 代码重构通过这种方式原本与位图相关的函数会变得更加通用能够支持音效和其他资源的操作。虽然这些修改看起来很简单但它们实际上提高了代码的可维护性和扩展性避免了代码冗余。 改进命名为了提高代码的可读性和明确性我们还需要改进一些函数的命名。例如将 GetFirstBitmapID 改为 GetFirstSoundFrom 或者 GetRandomFrom这些命名更加符合实际功能。 测试和验证在修改了大量的代码后我们需要确保这些修改不会引入新的问题。我们将在调试模式下检查代码确保所有的资源加载和播放操作都按预期工作特别是在资源管理方面。 临时处理在实际操作中我们先将音效资源添加到 WorldArena 中并临时分配播放的音效资源。然后通过 FirstPlayingSound 来跟踪当前正在播放的音效。虽然这些修改是临时的但它们有助于验证代码的有效性。 调试经过上述改动后我们在调试时遇到了一些问题比如访问冲突等错误。为了排查问题我们需要深入分析和逐步调试确保所有的资源都得到了正确的分配和使用。
总结来说当前的目标是通过对现有函数的封装和修改使得代码更加通用并且能够灵活地处理音效和其他类型的资源同时确保新代码能够顺利运行不引入新的错误。 调试器: 步进查看资源加载代码 我们开始分配资源并设置标签范围。在这过程中遇到了一些问题比如声音计数有时小于预期的声音数量。这时我们调整了声音资源的计数器以确保它不会低于实际需要的数量。 具体步骤如下 分配资源首先我们分配了音效资源并开始设置相关的标签范围。这一步是为了确保音效能够被正确加载和使用。 设置标签范围标签范围是用来标识不同类型资源的位置和顺序。通过设置这些范围系统能够快速访问和管理资源。 调整计数器遇到的问题是声音计数有时低于预期的声音数量。这可能是因为在设置或分配资源时计数器的值没有正确更新。因此我们调整了声音计数器的值以确保它与实际资源数量一致避免了资源使用上的错误。
总结来说通过分配和调整音效资源以及修正计数器的设置我们确保了资源管理系统能够正确处理音效资源避免了计数器值过低导致的问题。 运行发现段错误
game_asset.cpp: 将 SoundCount 设置为 256 * Asset_Count 显然这样的做法是行不通的。但这其实并不需要太过担心因为这些问题在当前阶段并不重要。我们只需要确保它在现阶段的功能是足够的毕竟最终这些设置不会硬编码在程序中而是会通过包文件来设置。我们在打包时会准确知道包文件中有多少个声音资源因此在那个时候一切都会正常运作。
调试器: 检查 GameState-FirstPlayingSound 现在我们可以查看一下游戏中的第一个播放声音看看它被设置成了什么。实际上它已经有了一个有效的ID这说明我们成功地找到了一个真实的声音资源。希望这意味着我们确实成功地定位到了一个实际的声音资源并且它现在已经在系统中可用。
game_asset.cpp: 将音量调到最大 现在考虑到音量的问题显然它不应该是零因为如果我们想听到声音我们必须给它一个非零的音量。因此暂时将音量设置为最大。接下来在其他代码部分我们可以查看混音器确保现在有声音数据实际传递给它。这样做是为了确保系统能够正常处理并播放音效。
调试器: 步进进入 LoadedSound 需要加载音频因为它还没有被加载。接下来要确保获取音量和目标通道这些通道应该已经被清空。我们需要确认这一点并查看混音器实际处理了多少样本。检查音频的数据流确保样本值正确地积累并处理。如果音频数据有值它应该会被正确地累积。接下来我们会继续播放声音并更新状态确保播放过程顺利进行。所有这些步骤都看起来合理接下来将继续处理输出缓冲区。
build.bat: 切换到 -O2 并运行游戏 现在的目标是创建一个简单的功能允许我们轻松地触发声音。在之前的代码中我们已经为播放声音做了相关的内存分配。现在的计划是将这些分配代码移回到合适的位置。虽然目前有一些关于内存分配的问题比如是否应该进行内存分区或是从操作系统动态分配内存但现在并不是讨论这些问题的时机。 我们现在的重点是能够在“World Arena”中随意分配声音暂时不考虑其他复杂的内存管理问题直接假设能够从World Arena 中分配所需的声音资源。 game.cpp: 引入 PlaySound 接下来要实现一个新的功能允许通过 PlaySound 函数播放声音。这个函数将接受一个声音 ID 和一个游戏状态用来决定在哪个状态下播放声音。函数的工作流程是首先创建一个新的播放声音对象并初始化它就像之前的初始化过程一样。然后把这个播放声音对象放到播放列表的顶部。 在此过程中如果存在一个空闲的播放声音对象则使用它如果没有空闲的则会创建一个新的播放声音对象并把它添加到空闲列表中。同时每个新的播放声音对象的 next 指针会设置为零确保它不会被其他地方使用。 这段代码的目的是确保每次播放声音时都能正确地管理播放声音对象。如果没有空闲对象系统会自动创建一个新的并插入到空闲列表中。最后这个操作应该可以启动音乐并使其在游戏中播放。
运行游戏并听到我们的声音 game.cpp: 在触发剑时调用 PlaySound 为了测试音频混合的功能计划在特定事件发生时播放声音比如当角色发动攻击或其他动作时。在代码中考虑通过调用 PlaySound 函数来实现这一点。具体来说将从一个已定义的资产列表中随机选择一个声音并播放它。目标是确保能够通过随机选择的声音来测试音频播放的过程。 在实现时发现 PlaySound 函数并不接受两个参数这与预期的调用方式不符。因此需要检查这个函数具体需要多少个参数并弄清楚应该传入什么类型的参数。此外考虑到可能还没有定义一个用于选择随机声音的系列也需要检查是否已经创建了一个随机选择声音的序列。如果没有需要相应地进行处理。
game.h: 向 game_state 中添加 random_series GeneralEntropy 在这个阶段发现系统没有实现随机序列功能因此需要添加一个随机序列来实现音效的随机播放。具体来说要在系统中引入一个随机种子random_series机制以便生成不同的随机数序列。通过对代码的检查发现游戏中已经有了随机种子的实现方式。因此可以利用现有的机制来生成随机数。 为此首先需要将随机种子设置到游戏状态中并且确保该种子能影响到音效播放中的随机行为。完成这些步骤后应该就能顺利地进行声音的随机播放测试。 触发剑时候段错误 必须得在播放声音吧已经播放的声音的样本数设置为0才行 当播放新声音时需要重置所有相关参数但 SamplesPlayed已播放的采样数没有被正确重置为零。这导致在后续的播放过程中声音的状态可能不正确进而影响音频的播放逻辑。要修复这个问题需要在 PlaySound 函数中确保 SamplesPlayed 被正确初始化为 0以保证声音从头开始播放而不会出现异常行为。 game.cpp: 调查 bug 在检查代码时发现存在一个问题。具体来说在播放声音时未正确更新“下一个播放声音”的指针。应当在播放完成后将“PlayingSound-Next”指向游戏状态中的“FirstFreePlayingSound”。这个指针没有正确更新导致播放声音的处理没有按照预期进行。 Blackboard: 链表 在代码中使用了一个链表来管理播放的声音。链表中的每个节点代表一个正在播放的声音包含指向下一个声音的指针。问题出在链表中节点的连接处理上。当播放声音结束后链表的“next”指针需要正确地跳过已完成的声音指向下一个正在播放的声音。然而在实现过程中只有链表中的一部分得到了处理忽略了更新指向新节点的指针。这导致了链表的指针没有正确连接影响了声音播放的管理。 game.cpp: 正确构建这个链表 在管理播放声音的链表时需要跟踪每个节点的指针。每个“playing sound”都指向一个声音并且通过链表的“Next”指针连接下一个播放的声音。为了解决指针更新的问题应该在遍历过程中保持对当前节点的指针并在需要时通过更新“PlayingSoundPtr”来正确指向下一个节点。 具体来说处理播放声音时需要确保在移除当前节点时前一个节点的指针能够指向下一个节点。这个操作的关键在于在移除一个节点时将当前节点的“next”指针赋值给上一个节点指针从而保持链表的完整性。这样一来不需要额外的操作来更新链表只需要确保每次移除节点时正确更新指针链表自然会自动推进。 通过这种方式链表的管理变得更加高效并且避免了不必要的重复操作。
调试器: 步进查看 playing_sound 链表 在这个过程中目标是简化播放声音的循环。最开始时操作的是指向“playing_sound”的指针而现在是操作指向“PlayingSoundPtr”的指针的指针即查看指针所在的位置。在这种方法中首先需要获取第一个播放声音的指针并从这个位置获取当前的播放声音。然后其他的操作就按正常流程进行。 如果播放的声音已经完成应该进行一些额外的操作。首先要确保记住当前节点的“前驱”指针即指向当前节点的指针。然后往前推进时只需取出当前节点的“下一个”指针来替代当前节点的指针继续处理下一个节点。完成后将当前节点放回空闲列表。 通过这种方式链表的结构得到更新并且指针的操作变得更加清晰、简化。完成后循环会自动继续到下一个声音无需额外复杂的操作。 之前的一个错误 梳理一下
运行游戏并听到我们的声音 我们确认了一些内容调整了一些设置现在一切看起来都已经恢复正常。 在回顾的过程中我们重新设置了一些参数并进行了测试。在播放音效时出现了一个声音并不完全是我们希望在游戏中射击时听到的声音。这意味着当前的音效可能不太符合预期或者需要进一步调整以确保射击音效听起来更符合游戏的氛围。 此外还听到了一些额外的声音包括一些无关的声音元素。这表明当前的音频处理可能还存在一些问题需要进一步优化或筛选以避免多余或不恰当的音效混入最终的输出。 虽然已经完成了主要的内容但仍然有一些额外的工作需要进行例如进一步调整音频参数、优化音效播放机制或者确保所有的声音都符合游戏设计的要求。不过总体来说核心部分已经完成只是一些细节仍需优化。 由于时间关系接下来需要转向其他任务后续可以再继续优化这些细节以确保音效的最终效果符合预期。 声音缓冲区是对数还是线性分贝值还是线性值我们是否需要在构建缓冲区的和时考虑到这一点 我们讨论了声缓冲sound buffer的特性主要关注其是线性的linear还是对数的logarithmic以及在计算混音总和时是否需要考虑这一点。 在分析过程中我们确认了声缓冲是线性的而不是对数的。这意味着在进行音频混合时样本的数值是以线性方式进行叠加的而不需要进行对数运算或额外的非线性处理。这一点与某些音频系统如人耳感知的响度不同因为人耳对声音的感知通常是对数的但声缓冲本身依然是线性存储的。 由于声缓冲是线性的在进行混音时可以直接相加各个声音的样本值而不需要考虑对数缩放或其他复杂的变换。这使得音频混合的实现更加直观和直接同时也符合一般的数字音频处理方式。 最终我们得出结论声缓冲是线性的不是对数的因此在构建混音总和时无需额外考虑对数缩放的问题。 你们已经有人在为游戏创作原声带了吗 我们讨论了游戏的音乐和艺术资源的版权问题以及未来可能的规划。目前游戏的配乐是通过授权获得的因此在版权方面受到了限制无法随意发布或修改。这意味着我们无法将当前的音乐作为游戏源代码的一部分发布也无法将其归入公共领域public domain。 未来是否会重新制作配乐取决于游戏的销售情况。如果游戏的收入足够可能会考虑聘请作曲家专门创作音乐以便能够完全掌控其版权并将其作为游戏的一部分发布。这样的话音乐可以像游戏的源代码一样被自由分发甚至有可能进入公共领域。但目前尚不确定是否会有足够的资金支持这个计划。 相比之下游戏的美术资源是完全自主创作并拥有全部版权的因此可以随时以任何方式发布。例如可以选择将部分美术资源公开让任何人都可以在自己的游戏中自由使用就像游戏的代码计划进入公共领域一样。不过目前还没有明确的计划只是希望保留这样的可能性以便未来可以自由决定如何处理这些资源。 提醒一下 InterlockedIncrement 中的参数顺序及其掩盖的 bug 我们一直想修复这个问题但不确定这是否就是要修复的那个问题。之前已经注意到 InterlockedIncrement 相关的问题但不确定这是否就是讨论中的问题。 关于 InterlockedIncrement 中的参数顺序可能掩盖了某个 bug。我们一直有意修复这个问题但现在才真正开始处理。当前遇到的疑问是是否在错误的地方复用了 InterlockedIncrement特别是在 Queue-CompletionCount 上的使用方式可能存在问题。 在检查时发现 InterlockedIncrement 可能被重复使用但不确定这是否就是导致问题的原因。为了进一步确认需要回溯到之前讨论的具体内容以确保正在处理的是正确的问题。 game_asset.cpp: 处理 LoadBitmap 中 BeginTaskWithMemory 失败的情况 我们不确定具体指的是哪个 bug因此需要更具体的描述。我们其实是在考虑另一个问题主要是在进行原子比较交换atomic compare exchange时会遇到的情况。问题出现在任务开始时如果任务开始失败了我们希望能够确保将资源的状态恢复为“未加载”状态。这样做是因为如果没有恢复到“未加载”状态那么资源将进入队列但永远不会被分配任务因为队列中没有任务可以处理结果资源就永远无法加载。 这个问题虽然今天没有讨论到但它确实是我们在论坛上曾经提到过的一个 bug需要修复。我们需要确认是否有在相关代码中加上这个修复。 将单声道声音混合到立体声时每个通道的音量应该是中间声道的 50% 当缺少单声道声音时如果每个声部的音量设置为50%那么它们会被放在中间声道center panned。这个做法的原因似乎是如果我们想让声音移动到另一边可以更容易地调整音量。例如通过在两个声道之间平滑过渡可以实现声音逐渐向另一侧推送。 然而这样的设置是否合理则取决于我们想要的效果。一个可能的默认设置是这样做因为在中间声道时两个声部的音量合并成一个而50%能避免两个声音叠加时过于吵杂。虽然我可以理解这种方式的合理性但我不确定是否完全认同这一点。因为如果想要声音在中间时达到全音量可能需要不同的调整方式。可能我更倾向于让声音在向其他声道移动时逐渐变得较安静而不是保持一定的音量。 因此虽然这可以作为一个默认设置我仍然不确定是否完全适合可能还需要进一步探讨。 这个音频代码是否允许你同时播放相同的声音即如果在第一次播放结束之前再次启动相同的声音 这段音频代码的目的是让同一个声音可以同时播放也就是在第一次播放还没有结束之前启动同一个声音的第二次播放。从描述来看代码可能是通过允许在音频还未完全播放完时再次启动同一音频来实现这个功能。 不过这是否能够实现取决于代码具体的实现方式。一般来说如果音频播放系统允许同一个声音实例同时多次播放就应该能够成功。但如果系统有一个限制比如不允许同一个音频文件在播放完成前被再次触发那么代码就不会实现预期的效果。 因此是否能成功播放同一个音频文件取决于音频播放机制的设计。假如允许多次播放同一声音而不会覆盖或冲突那么就能实现并行播放但如果音频播放器在处理时会拒绝重复播放或者音频资源没有正确管理就可能会导致问题。 比较和交换中的第二和第三个参数 关于提醒的代码讨论的是比较和交换compare and swap中的第二个和第三个参数。我们最初可能混淆了函数误以为是在讨论另一个函数。实际上我们谈论的是原子比较交换atomic compare exchange。 现在问题是在比较和交换的过程中这两个参数的顺序是否正确。需要检查一下代码看这两个参数是否被放置在了错误的顺序里。具体来说原子比较交换操作的顺序应该是首先是预期值expected然后是新的值new。这个顺序非常重要因为它决定了交换操作的正确性。 game_intrinsics.h: 交换 AtomicCompareExchangeUInt32 中的参数然后再交换回来并改变其调用的函数 我们发现之前的比较和交换compare and swap操作中参数顺序确实错了这导致了某些行为不符合预期。虽然代码似乎能够正常工作但实际操作可能存在其他隐藏的 bug。我们决定进一步调查并确保这部分的实现是正确的。 为了避免混淆包括自己在内的开发者更容易理解我们打算将代码调整为与 Windows 中的实现保持一致因为 Windows 是采用这种方式的。这种做法可以避免产生混淆特别是参数的顺序。我们查阅了相关代码确认原子比较交换操作中的顺序应该是预期值expected放在最后一个位置新的值new放在前面。 在我们期望的场景中任务的状态应该是“未加载”unloaded但我们发现系统意外地将状态设为了“排队中”queued。这显然是由于参数顺序错误导致的然而系统还是能够返回“未加载”状态这其实是因为交换操作从未真正执行过。由于条件设置问题操作总是会返回“未加载”状态这解释了为何代码在错误的实现下仍然能够“正常工作”但这种行为并不符合预期。 因此调整代码的参数顺序后应该能够避免混淆并且确保原子比较交换操作的正确性。 目前音频似乎依赖于帧率。是否可以将其放在单独的线程中或使用中断如果可以的话使其不再依赖于帧率 音频的播放似乎是与帧率相关的。如果将音频放在一个单独的线程中或者使用中断可能有助于让音频不再依赖于帧率。 然而在 Windows 上使用中断并不太现实。不过确实可以将音频处理放到一个独立的线程中这样可以减少音频对帧率的依赖。然而这样做有一个问题就是必须确保该线程不会被“饿死”因为操作系统无法保证一定会及时唤醒这个线程。 通常的解决方案是虽然帧率可能不稳定但可以通过在音频缓冲区中加入更多的音频数据确保下一个帧率周期的音频数据足够。如果帧率保持正常那么可以将新音频数据覆盖到缓冲区中如果没有达到预期的帧率则音频可能会出现“跳跃”的现象但这并不会对整体效果造成太大影响。 目前这个问题不太需要担心因为理想情况下游戏应该总是能够保持稳定的帧率。在发布的游戏中错过帧率是非常罕见的这是不应该发生的。然而考虑到一些外部因素例如后台程序占用资源偶尔可能会错过某一帧。在这种情况下可能需要采取一些措施来确保音频处理不受影响虽然这些措施暂时并不紧急。 你能为 gcc / clang 添加一个 __sync_val_compare_and_swap(Value, Expected, New) 吗 我们讨论了是否需要添加一个 __sync_val_compare_and_swap并且提到需要考虑 GCC 和 Clang 编译器的支持。虽然可以完全实现这个功能但目前不太记得具体的实现细节。 同时我们还考虑是否已经在代码中定义了其他编译器相关的宏或者标识符。如果没有我们可能需要再检查一下是否需要为其他编译器添加相应的支持。 game_intrinsics.h: 添加 tfnw 的建议 讨论的重点是关于添加 __sync_val_compare_and_swap以及如何在 GCC 中处理相关的问题。首先回忆起上次的实现记得曾经用过某些特定的指令来处理这个问题。对于 GCC我们需要确定使用哪种“屏障”指令来实现同步。 具体来说提到如果想在 GCC 中实现这个功能我们需要知道应使用什么指令来保证内存的同步。如果使用 volatile 关键字它可以起到一定的屏障作用这符合我们记得的做法。尽管这个方法有些不寻常但它似乎是有效的。 接下来我们计划将 __sync_val_compare_and_swap 的功能添加到代码中并测试其是否按预期工作。我们会进行一些微调确保它能正确地执行然后将代码提供给其他人下载和测试看看实际效果如何。 如果还有其他问题可以进一步调整和完善。 game.cpp: 播放音乐而不是 bloop以演示同时播放相同的声音 我们决定回答关于是否能够在同一时间播放多个声音的问题。为了演示这个功能我们打算不播放平常的声音而是选择一个非常响亮且持续时间很长的声音进行测试。通过这个方法可以清楚地展示系统是否能够处理多个声音同时播放的情况确保音频不会被覆盖或中断。 那为什么它能工作 之所以能实现多个声音同时播放是因为播放声音和实际的声音数据是两个完全独立的概念。我们采用了将播放中的声音与实际加载的声音分开的方法创建了一个单独的播放声音列表。这样我们可以在列表中添加任意多个播放条目而这些条目可以都指向同一个底层的声音缓冲区。 换句话说播放的声音是基于播放列表的数量而不是加载的声音数量。播放的循环是针对当前正在播放的声音进行的而不是针对所有已加载的声音。这种方法使得可以同时播放多个声音即使它们指向相同的声音数据。 Blackboard: 将资产数据与实例数据分开 我们实现了一个完全分离的系统具体来说加载的声音和正在播放的声音是两个独立的部分。加载的声音存放在一个地方而正在播放的声音则在另一个地方。当我们加载一个声音并将其添加到播放列表时可以根据需要堆叠任意数量的播放声音这些播放声音都指向同一段音乐。 每个播放声音都保存了已经播放的样本数量从而能够记录每个播放实例在不同时间点的位置。这一点非常重要因为它说明了每个播放实例的数据是独立的即使它们指向相同的声音数据。 这里的核心概念是应该始终将资源数据例如声音的定义与实例数据例如播放声音的状态分开。这就像定义一个结构体类型然后可以创建多个该类型的对象。我们也为每个播放声音定义了一个结构体并可以创建多个实例。同样对于声音资源系统也是如此我们加载了声音文件并可以随时播放它。通过这种方式可以在不干扰其他播放实例的情况下反复播放同一声音。 这样系统就能支持同时播放多个相同的声音实例并且每个实例的播放位置和状态都能独立管理确保系统的灵活性和高效性。 为什么使用链表而不是其他数据结构比如vector 使用链表而不是其他数据结构如vector的原因是链表在随机添加和移除元素时表现得更高效。vector在这方面存在一些局限性。具体来说当你往vector中添加元素时如果vector满了你可能需要重新分配整个vector这样会带来较大的性能开销。而当你删除元素时如果没有做额外的空闲列表跟踪通常需要将整个vector压缩重新整理元素。 而链表则避免了这些问题因为它在随机添加和移除元素时非常高效尤其是在不需要进行随机访问的情况下。在这种情况下链表能够很好地工作因为它专门为频繁的插入和删除操作设计不需要像向量那样进行整个结构的重新分配或压缩。 总的来说如果不需要对数据进行随机访问只需要频繁地添加和删除元素链表是一个非常合适的数据结构能够提供更好的性能。 但是你能拿到声音求和机制的输出并播放它吗 如果需要完全可以将声音合成机制的输出播放出来。没有任何理由不能这么做。
- 上一篇: 惠州网站建设是什么网站开发 企业 定制系统
- 下一篇: 惠州网站设计定制闸北区网站建设网页设
相关文章
-
惠州网站建设是什么网站开发 企业 定制系统
惠州网站建设是什么网站开发 企业 定制系统
- 技术栈
- 2026年03月21日
-
惠州网站建设多少钱如何在网站上做背景图片怎么做
惠州网站建设多少钱如何在网站上做背景图片怎么做
- 技术栈
- 2026年03月21日
-
惠州网站建设l优选蓝速科技免备案空间免费
惠州网站建设l优选蓝速科技免备案空间免费
- 技术栈
- 2026年03月21日
-
惠州网站设计定制闸北区网站建设网页设
惠州网站设计定制闸北区网站建设网页设
- 技术栈
- 2026年03月21日
-
惠州网站网站建设pw域名网站
惠州网站网站建设pw域名网站
- 技术栈
- 2026年03月21日
-
惠州响应式网站建设wordpress 聚美主题
惠州响应式网站建设wordpress 聚美主题
- 技术栈
- 2026年03月21日
