游戏引擎学习第34天
仓库:https://gitee.com/mrxiao_com/2d_game
#这天内容比较多
开场介绍
游戏开发行业的基础是使用C和C++编程,这是当今几乎所有游戏的开发标准。市面上广受欢迎的游戏,如《使命召唤》或《侠盗猎车手》,它们的底层代码和引擎几乎无一例外地采用了C和C++。虽然有其他语言被用来开发一些游戏,但这些通常不代表行业主流。行业内的顶级性能优化、低级编程控制等,仍然高度依赖C和C++。
使用C和C++开发的优势在于,它们提供了对每个细节的完全控制。这种控制能力使得开发者可以优化每一个代码路径,深入了解和优化程序的性能。在开发过程中,选择一种不会自动化太多、不干预编程细节的语言有助于保持这种完全控制,确保结果完全掌握在开发者手中。
历史上,许多重要的游戏和技术突破也是通过低级语言实现的。例如,早期经典的《Quake》及其他游戏为行业奠定了基础,证明了低级编程在创造重要技术进步中的价值。从这些技术延伸出的许多现代技术也反映了低级编程的持久意义。
这种对底层技术的关注不仅仅是一种技术选择,更是一种追求效率和掌控感的哲学。这种哲学认为,了解和控制代码的每一个层面是开发高性能游戏体验的重要方式。甚至在理想的情况下,移除现代操作系统的干扰,直接对硬件进行编程,将能更好地掌控系统运行的每一个方面,从而创造更加优化的游戏环境。
虽然学习C和C++并非所有开发者的首选,但它们代表了游戏开发的核心技术能力。这种编程模式不仅是一种技能,也是一种为创造突破性游戏体验而磨练的技术方向。低级编程在游戏开发中的重要性不仅体现在历史中,也将在未来继续发挥关键作用。
知识点分享
在深入学习计算机底层编程时,掌握如何操作硬件和低级系统机制,不仅能帮助提升在高层次语言或工具中的效率,还能为应对更复杂的开发场景打下坚实基础。使用更高层次的开发工具(如Unity或Unreal)时,理解底层运作原理,例如垃圾回收、动画系统等机制,能够大大减少潜在问题,并提升代码结构和性能的设计能力。
尽管在某些情况下,开发者可以对硬件或低级操作一无所知,但随着游戏复杂性的增加,这种无知会成为发展的瓶颈。为了在高层次系统中优化设计和解决问题,了解硬件和系统的基本运作规律是必要的。尤其在面对需要复杂逻辑和性能优化的场景时,这些知识尤为重要。
通过完整地经历游戏开发的整个管道,从底层的硬件操作到高层次的功能实现,可以显著提升技能。这种经验不仅赋予开发者更高的自主性和控制力,还让人深入理解技术细节。这种掌握感也常常带来成就感,使开发者更加享受编程过程。
学习底层编程并不需要大量的时间投入。通过每天一个小时的实践,开发者可以快速积累知识,感受到学习的成效。虽然很多人可能认为手动编写底层代码或构建自定义引擎是一种浪费,但实际情况是,这种实践既能提供技术提升,也能带来一种特别的乐趣和成就感。
低层次编程曾在70年代和80年代推动了计算技术的革命,个人电脑用户通过解决硬件挑战获得了深厚的技术积累。这种编程方式的价值在于,开发者不仅可以直接影响硬件的行为,还能获得前所未有的创造自由。
当前,关于开发语言、编辑器选择或引擎使用的争论其实并不重要。真正决定游戏质量的因素在于开发者的编程能力、硬件使用知识,以及专注于核心目标的能力。强调工艺精神和对系统的深入理解,比追求工具本身更有意义。
通过这种方法,甚至可以在短时间内实现超越专业引擎的功能。例如,实时代码编辑就是一个鲜有引擎提供的功能,但这种能力在专注于底层的开发中却能够迅速实现。这种实践证明,低层次编程不仅实用,还能开辟许多新的可能性。
在整个学习和开发过程中,关键在于享受乐趣和探索的过程,而不是被选择工具的纠结所束缚。通过这种方式,开发者不仅能创造出令人惊叹的成果,还能收获成长和满足感。
开始编程
到目前为止,已经实现了一个简单的机制:当玩家移动到新的地图单元时,摄像机也会随之移动。虽然功能基本实现,但它的效果可能还有待优化。在接下来的开发中,可以基于这个初始实现不断迭代,调整到更加理想的表现。
添加平滑滚动效果
目前计划增加一个新的功能——平滑滚动,以提升视觉效果。这种方式比现有的移动机制更受欢迎。为了确保工作流畅,首先检查了平台层的内存使用情况,确认内存始终保持恒定并无异常,这是优化开发流程的重要步骤之一。
在实现平滑滚动时,目标是通过移动地图而非玩家的位置来实现动态效果。具体方法是调整绘制逻辑,使地图根据玩家的位置进行偏移。通过这种方式,玩家可以始终处于屏幕的中心位置,而地图动态移动,呈现流畅的滚动效果。
目前绘制地图的核心逻辑基于中心位置 (CenterX, CenterY)。为了实现平滑滚动,只需要将玩家的偏移量与当前地图位置的偏移量结合起来。具体操作如下:
- 修改绘制瓷砖时的位置偏移逻辑,将玩家偏移量反向应用到绘制函数中。
- 调整偏移计算方式,确保地图动态滚动而玩家保持在屏幕中心。
- 通过简单的加减操作,快速实现了滚动效果。
这项功能非常直观,但并未计划在当前项目中正式启用,因为当前的设计更倾向于从屏幕到屏幕的切换体验,而非连续滚动。然而,这一演示展示了引擎的灵活性,也表明它能够支持更复杂的机制。
通过调整代码逻辑,快速实现了平滑滚动。这种方法虽然简单,但功能强大,能够显著提升游戏的视觉体验。
滚动效果完成 - 测试昨日代码
在目前的开发阶段,需要进一步概念化现有的虚拟瓦片系统,并测试昨天实现的内容。以下是当前的思考方向和操作计划:
测试与扩展虚拟瓦片系统
-
测试当前功能
昨天实现的虚拟瓦片系统尚未经过全面测试。下一步需要确保其在各种情况下都能正常运行。 -
创建更大的世界
考虑生成一个更大的世界,以便能够测试系统在更大范围内的表现。这可能需要实现缩放功能,方便查看更广泛的地图区域。
分析与清理代码库
-
回顾代码中的待办项
查看当前代码库中的待办标记,主要集中在以下几个方面:- 玩家移动代码的进一步完善,计划稍后进行调整。
- 修正与浮点运算相关的潜在问题,以确保数学计算的精确性。
- 检查和改进瓦片地图系统中的功能,目前没有太多待办事项,这表明系统大部分已经完成。
-
记录与整理数据结构
- 确认固定点与瓦片索引的关系。
- 高位用于标识瓦片的大致区域,低位用于描述瓦片的具体位置。
- 对索引与位的使用进行清晰注释,以便后续维护和扩展。
后续方向
- 设计与实现瓦片偏移逻辑
研究如何通过偏移设置瓦片的初始位置,这可能有助于优化地图的生成与加载效率。
目前,测试与文档化是关键步骤,确保系统的每个部分都能清晰地说明功能及实现原理。在此基础上,还需要深入思考如何使系统能够轻松支持更复杂的游戏世界。
更改位置偏移的基准为网格中心
探索瓦片系统的改进设计与舍入机制
当前设计中,瓦片系统的几何中心使用了从瓦片中心的偏移量表示。这种方式有利于避免出现偏向上角、下角或左侧、右侧的偏移,确保中心位置在各个方向上对称且一致。这样一来,无论坐标轴如何翻转,系统都能保持一致性。
关于瓦片的半径与中心点定义
- 当前瓦片的边长被设定为 1.4 米,但如果以瓦片的中心作为基准位置,半径将变为 0.7 米。
- 为了简化计算和统一度量单位,可以尝试将瓦片的边长调整为 2 米,对应的半径为 1 米,这是一个更直观的度量方式。
舍入操作的必要性与技术细节
瓦片的中心定位需要对浮点数进行舍入处理,确保偏移值落在最接近的整数瓦片坐标上。这涉及到以下关键点:
-
当前的舍入方式:
- 对于正数,当前实现方式是将偏移值加上 0.5,然后通过截断取整。
- 截断会将偏移值的上半部分舍入到更高的整数,而下半部分则回落到较低的整数。
-
负数处理问题:
- 负数的截断方向与正数相反,因此直接使用加 0.5 的方法无法正确处理负数。
- 为解决这一问题,需要在负数情况下改为减去 0.5 再进行截断。
-
优化舍入逻辑:
- 系统可以使用标准库函数(如
roundf
)简化操作。这些函数能够正确处理正负数舍入问题。 - 然而,为了增强对舍入过程的理解和适配性,未来可以手动实现这些功能,而不完全依赖运行时库。
- 系统可以使用标准库函数(如
设计与实现中的注意事项
-
偏移值的计算:
偏移值应基于瓦片的中心位置,而不是瓦片边界。这种做法更直观且计算逻辑清晰。 -
浮点精度问题:
在现有实现中,使用舍入函数确保瓦片的中心偏移能够以一致的方式处理。未来计划在性能允许的情况下,自定义实现这些舍入函数。 -
代码实现验证:
在调整和优化舍入逻辑后,需要对所有使用这些函数的代码模块进行验证,确保其行为符合预期。
未来改进方向
-
增强数学库:
当前依赖标准库的舍入函数。在性能需求更高的场景下,可以实现自定义的舍入算法,进一步优化精度和效率。 -
统一瓦片度量单位:
未来或可将瓦片的边长固定为整数值(如 2 米),简化计算逻辑并减少边界情况的特殊处理。
通过这些改进和优化,瓦片系统的几何设计能够更符合对称性和一致性要求,提升整体系统的健壮性和可扩展性。
(int32)roundf(Real32 + 0.5f)
:首先对浮点数进行四舍五入(四舍五入到最接近的整数),然后再转换为整数。这是一个标准的四舍五入操作。(int32)(Real32 + 0.5f)
:直接将浮点数加上0.5后丢弃小数部分。这实际上是在进行截断(向下取整)。
举例:
假设 Real32 = 2.3
:
(int32)roundf(Real32 + 0.5f)
会得到3
,因为2.3 + 0.5 = 2.8
,roundf(2.8)
会四舍五入到3
。(int32)(Real32 + 0.5f)
会得到2
,因为2.3 + 0.5 = 2.8
,但(int32)
会丢弃小数部分,得到2
。
实现网格中心偏移
内容讨论了如何修改一个系统,涉及到瓦片和中心点的计算。首先,描述了如何通过从中心点出发计算相对位置,而不是依赖于角落的传统做法。这需要考虑将中心点与瓦片的半径相关联,并通过四舍五入来确保正确的计算结果。具体来说,四舍五入将决定移动到哪个中心点,简化了半径的使用,使得可以直接通过总大小进行计算,而不需要额外的半径调整。
随后,讨论了如何绘制这些瓦片,特别是在进行偏移时,考虑的是瓦片相对玩家的位置。为了确保计算的准确性,必须在绘制时减去半个瓦片的大小(即半径),从而确保正确的相对位置。
在进一步的讨论中,还提到了如何改进代码中的命名,确保变量名称能准确反映其作用,例如将“center x”改为“screen center x”来避免混淆。然后提到了使用标准化方法来计算瓦片的中心位置,并确保最终的显示效果符合预期。
此外,还提到可能需要一个快捷键(如空格键)来加速游戏中的移动,帮助开发者进行调试和优化,虽然当前的速度对调试来说较慢,但在游戏实际运行中可能合适。
整体而言,这一修改的核心思想是在中心点基础上进行计算,使得瓦片的移动和显示更加精准,计算过程也变得简化,不再依赖于复杂的半径或角落位置。
为了测试让玩家移动更快
在处理玩家移动时,速度的设置是一个关键要素。一个简单的方法是基于玩家按键的输入来设置移动速度。比如,可以设置一个玩家的速度变量,并在按下不同的方向键时根据需要改变速度。例如,当按下上方向键时,玩家的速度可以加速,从而实现更快的移动。这种方式可以通过对按键的状态进行判断来控制速度的变化。
此外,还需要考虑游戏世界的物理特性,尤其是当玩家移动到不同的区域时,是否能够迅速改变其速度,或者在特定情况下进入加速模式。为了避免玩家的移动看起来过于生硬,重要的是避免出现过多的粘滞感,例如让玩家卡在障碍物上,这种情况会让玩家体验感很差。
为此,代码实现时需要特别注意玩家的移动与障碍物之间的互动,确保玩家能够流畅地穿越关卡,避免不必要的卡顿或粘滞现象。对这些问题的有效处理不仅能提升游戏的玩法体验,还能增强游戏的整体质量,尤其是在快速开发或没有足够经验的开发者参与时。
总结来说,优秀的玩家移动代码不仅仅是关于如何简单地控制玩家的速度和方向,还包括如何在玩家的移动中融入合适的加速、摩擦、反应等机制,从而使得游戏玩法既流畅又充满乐趣。
创建更大的游戏世界
在构建一个更大的世界时,首先需要开始考虑如何将游戏世界转变为一个更加程序化的地图系统。为了实现这一目标,首先需要构建一个基本的平铺地图系统,并把这些基础结构分离到单独的文件中,以便更好地管理和扩展。一个常见的做法是将与地图相关的功能,例如瓷砖处理和子块管理,放到独立的文件中,使代码结构更加清晰和模块化。
通过这种方式,可以更好地组织代码并提高其可维护性,同时为未来的扩展做好准备。例如,可以将地图的逻辑和世界的位置管理分开,以便未来能够更容易地修改或添加新功能。
在设计地图时,需要考虑世界的结构。例如,是否使用一个直接的世界坐标,还是使用基于瓷砖的坐标系统来管理物体的位置。这个问题的答案会影响整个地图系统的设计。瓷砖地图作为世界的一部分,将决定如何在游戏中处理实体和其他对象的定位。
为了实现这一点,可能会将世界的所有坐标存储在一个专门的类中,或者将世界的状态与地图的状态紧密结合。此时,平铺地图和世界地图的关系需要明确,这样才能避免混淆。随着设计的深入,可能还会调整这些概念的实现方式,以确保它们能够有效地协同工作。
此外,可能需要将地图和世界相关的不同功能分别存放在不同的文件中,以便后续开发时能够灵活调整和管理代码。这将使代码变得更加整洁,并能确保项目的可扩展性,尤其是在面对复杂的世界生成和处理任务时。
在实现过程中,还需要注意代码的可维护性和可读性。随着世界和地图的扩展,更多的细节会被引入,因此保持代码的清晰结构和合理的功能划分至关重要。
开始通过程序化生成新的网格地图
在当前实现中,目标是开发一种方法来修改一个地图,这张地图是一个以“瓷砖”为单位的网格。以下是所做的总结和复述:
-
初始需求:希望能够在地图上的任意位置设置特定瓷砖的类型。这种设置不需要高效,当前的目标是简单地实现地图编辑功能。
-
基本地图生成:
- 屏幕大小:屏幕的尺寸是 17x9 的瓷砖网格。这个定义被明确为常量,以便以后引用。
- 世界大小:定义世界的范围为 32x32 个屏幕,每个屏幕都是 17x9 瓷砖的网格。
- 初始化过程:在初始化时,为地图的每个瓷砖都赋值为零(即地图中暂时什么都没有)。
-
瓷砖设置逻辑:
- 计算每个瓷砖的绝对索引,将屏幕索引和瓷砖索引结合。
- 使用一个设置函数为特定位置设置值,目前所有位置被赋值为零。
-
存储结构:
- 世界的存储结构新增了一个指针,用于指向瓷砖地图。这个结构允许程序直接访问和修改地图。
- 通过一个即将编写的“世界创建函数”来生成世界,并初始化地图数据。
-
内存管理和分配:
- 世界结构存储在游戏状态中,每次需要访问地图时都可以通过该状态来获取地图。
- 确保分配和初始化的逻辑在世界创建时执行一次,而不是在每次更新时重复。
-
后续开发计划:
- 编写更多关于“瓷砖值设置”的具体实现,包括对内存的进一步管理,以及如何处理数据的动态扩展。
- 世界的生成逻辑将在接下来的时间里实现,预计将包含随机生成的内容。
-
代码组织改进:
- 对于各部分的初始化代码进行了调整,以更清晰地表达其功能,例如避免重复的地址获取操作。
- 逐步清理了遗留代码,使得当前实现更加紧凑和易维护。
最后,虽然当前实现还没有解决所有问题,但已经为后续地图生成和动态内容提供了基础设施。这些结构将允许创建一个可扩展的地图系统,适用于未来的开发需求。
实现游戏状态的持久化存储
总结和复述内容如下:
开始构建持久化世界
当前的目标是创建一个持久化的游戏世界,而不是在每一帧中重新构建整个世界地图。通过将数据存储在专用内存池中,可以实现持久化,允许游戏状态和世界状态在多个帧之间持续存在。
关键任务概述:
-
内存池的划分:
为了实现持久化,需要分配和管理一个永久内存池。这一池用于存储游戏状态和地图信息。 -
内存管理的初步设计:
- 内存划分为两个区域:一个存储游戏状态,一个存储世界地图数据。
- 引入“memory arena”的概念,通过它来分配和管理内存块。
-
持久化的瓦片地图:
游戏地图仅存储实际被使用的瓦片数据,而不是整个地图,从而优化内存利用率。
技术细节:
初始化和使用内存池:
- 初始化时,分配整个永久存储内存。
- 游戏状态结构占据内存池的前一部分;剩余内存用于存储世界地图。
- 通过memory arena接口,提供按需动态分配内存的功能。
示例实现:
- 创建一个
MemoryArena
结构,负责管理指定范围内的内存。 - 定义初始化函数:
初始化函数接受一个内存区域的起始地址和大小,并设置memory arena的内存池。
游戏状态与地图数据的分配:
- 游戏状态的大小和地址确定后,地图数据存储的位置紧跟其后。
- 地图数据分配时,每次请求动态增加内存。
主要逻辑流程:
-
定义memory arena并初始化:
- 初始化memory arena时,将永久存储的起始地址和大小传入。
- 使用
initializeArena
方法指定memory arena的范围,并从游戏状态数据后开始分配。
-
分配游戏世界和瓦片地图的内存:
- 使用
pushMemory
方法从memory arena中获取内存块。 - 确保分配的内存位置正确且不会覆盖其他数据。
- 使用
-
管理世界地图数据:
- 通过逐步请求内存扩展地图存储区域。
- 删除不再使用的瓦片数据,回收内存。
总结:
通过引入memory arena和动态内存分配的机制,可以实现高效的持久化存储。这种方式避免了每一帧都重新构建数据,提高了游戏性能和存储效率。在实现中,初期采用简化的设计,后续根据实际需求逐步完善内存管理策略。
测试一下
当日总结
当前开发的主要重点是将内存管理从堆栈转移到永久存储。这一过渡涉及使用一个简单的内存分配器。最初,所有内容都在堆栈上创建,但现在内存从永久存储的特定区域分配,基本上创建了一个基于memory arena的分配系统。这种方法使得游戏或模拟中的世界数据能够在不同帧之间持久化,而不必不断地分配和释放内存。
在这个内存系统中,预先分配了一块内存空间,然后根据需要将新数据推送到其中。每当分配新的内存时,它从memory arena的底部开始,并向上移动,这意味着在这个系统中无法释放内存。这样可以确保内存一直被分配,并能够连续使用,而无需复杂的释放逻辑。
这个过程还包括将先前在堆栈上分配的结构转换为使用memory arena内存,使内存管理变得更加简单和高效。目标是将所有相关数据移到这个memory arena中,使其跨越不同的游戏或模拟帧时持久存在。
过渡过程包括实现和测试基本系统,比如瓦片管理,其中瓦片值被设置并存储在永久内存中。这个改变最终有助于更高效地管理更大的数据结构,特别是在需要更多数据(如瓦片图或游戏状态)时。
随着开发的推进,系统预计会演变,特别是在处理更大的数据块时。对于如果使用更小的数据块会发生什么,仍然存在一些好奇心,这可能涉及在更细粒度的层面上处理更多数据。这部分仍在测试中,关于数据块大小的决定将基于性能和系统的限制。
从这一过程的主要收获是,现在内存以更永久、更可预测的方式进行管理,确保数据在不同的帧之间保持。尽管仍有许多内容需要探索,特别是在如何优化和更高效地管理内存空间方面。
展望未来,下一步包括实现更稀疏的存储模型,这将允许更好地处理动态加载的内容。重点是提高效率,同时避免过度复杂化内存系统。系统的开发朝着正确的方向前进,许多部分已经按预期工作。
能解释一下控制页面粒度的动机吗?
控制页面粒度的动机主要涉及存储和管理大型游戏世界中数据的效率。世界的维度可以非常大,例如拥有四十亿个瓦片,这对于任何系统来说都显得过于庞大,无法全部存储。因此,必须通过更细粒度的页面控制来优化存储方式,减少不必要的数据存储开销。
通过将瓦片按较小的单元(如16x16或64x64)进行分割,可以大大减少存储和查询的复杂度。每个页面(页面是瓦片集合)可以独立存储,只有当玩家接近这些区域时,才加载相应的数据。这种按需加载的方式有助于避免存储一个庞大的、无法实际使用的世界,避免浪费内存资源。
此外,较小的页面可以降低索引成本。每个页面的索引会比单独索引每个瓦片更高效,从而减少了处理大量数据时的开销。通过这种方式,游戏或应用可以在合理的硬件资源下,处理一个非常大的、复杂的世界,而不至于让存储系统变得过于庞大和低效。
在这种设计中,页面的粒度要足够小,以便捕捉到需要存储的数据,但又要避免过度碎片化,因为过度细分会导致存储基础设施本身成为瓶颈。理想的页面粒度应平衡存储需求和索引处理能力,避免存储大量无用的数据,同时使得系统能够有效地查询和管理这些数据。
内存分配区域(memory arena)是用于存储比游戏状态直接保存更临时的对象吗?
内存领域(Memory Arena)的目的是为了更高效地管理和存储游戏状态,尤其是在游戏运行时。游戏状态包含了世界地图、所有实体及其属性(如分数、金币等)。这些数据需要在游戏运行过程中进行存储,以便玩家能够随时保存和加载游戏进度。
当游戏需要存储大量的动态数据时,直接将所有数据存储到永久存储中可能非常繁琐且低效。为了避免这种情况,记忆领域提供了一个机制,用于在内存中自动布局和存储数据。通过这种方式,可以在内存中方便地管理游戏数据,而不需要手动计算每个数据的位置,避免了直接与永久存储打交道所带来的复杂性和开销。
记忆领域的工作方式类似于一种自动化的内存管理工具,它帮助将游戏状态中的数据在内存中有序地布局。存储过程对于开发者来说变得更简单,因为无需手动处理内存分配,领域会自动处理这些任务。这样,内存的分配和数据的存储就更加高效,开发者可以专注于其他功能,而不需要为每个数据项计算位置或管理内存。
为什么不实现平滑滚动?
平滑滚动被认为不适合某些类型的游戏,特别是在房间或本地瓷砖地图的布局中。当滚动停止时,可以在滚动的终点处添加边界,以使视角与房间或瓷砖地图的边缘对齐。这种方式意味着,游戏中的房间不会比屏幕显示区域更大,因为这样做没有实际意义。
在这种设计中,世界将通过生成不同的房间来创建,而这些房间会根据玩家的移动和视角进行布局。每个房间的布局将通过相机视角来呈现,当玩家走出当前房间时,相机会自动切换,聚焦到下一个房间。这种方式简化了相机的管理和场景切换,不需要平滑滚动,只需根据玩家的位置和房间的生成来调整视角。这种设计的目标是保持简单、高效的场景转换。
我们已经启用了 -Oi
编译器标志。
在设置编译器标志时,有一个与浮点运算相关的标志可能导致问题。特定的标志可能加速浮点运算(如启用fp/fast
),但这可能与代码期望的不兼容,尤其是在避免调用C运行时库的情况下。
一开始检查时,发现在编译器优化设置为fp/fast
时,仍然调用了浮点运算库中的某些功能。尽管期望的效果没有完全实现,但考虑到不希望与C运行时库产生冲突,继续检查编译器标志的效果。接着对代码进行了拆解,尝试确认编译器是否确实按照预期执行。
在观察代码时,发现某些浮点运算(例如四舍五入)没有按照预期被调用,尽管在某些场合中可能没有必要进行这种处理。继续深入检查代码,发现可能需要重新考虑如何处理坐标系统中的浮动运算,尤其是与其他类的操作和功能调用相互作用时。
最终,尽管做了各种检查,编译器依然保持调用了不希望调用的浮点库,导致实现未达到预期。为了克服这个问题,可能需要手动编写特定的操作代码,以绕过自动调用的限制,并确保最终的效果。
使用内存分配区域是否仍允许代码热加载?
使用memory arena的原因是为了便于管理和持久化数据。通过memory arena,所有的数据都能正确地被加载并持久化,尤其是在使用类似循环实时代码编辑的功能时。memory arena背后实际上使用的是一个永久存储区域,这些存储区域在虚拟分配时被管理。所有存储在内存块中的内容,无论是暂时的还是持久化的,都会通过memory arena来进行管理。
memory arena通过虚拟分配程序来处理数据存储,且不会调用任何额外的分配函数。它确保了所有的内存内容在内存块中持久存储,从而保证了数据的持久性。通过这种方式,memory arena简化了数据管理,使得所有需要持久化的数据能够被有效处理,且不会因重复调用分配函数而增加不必要的开销。这种方法使得数据管理变得更加高效和直观。
是否有足够的内存来存储整个稀疏网格地图?
在存储稀疏的map时,是否有足够的内存取决于世界的规模和所需的资源。目前,尚未确定具体的需求,因为游戏还没有开始开发。一旦开发开始,将能够更好地评估内存需求。为了处理大型世界的存储问题,可能会采用分页的方式,将数据分块处理。这种方法并不是不可行的,输入和输出操作也能有效支持这种分页处理。最终的解决方案将根据实际开发过程中对内存使用的反馈来调整。
为什么不直接分配一个大的 1D 数组?
在内存中,世界的表示是以一维数组的形式存储的。对于如何划分世界的块,有两种选择:一种是使用长而薄的块,另一种是使用矩形块。这两种方式的区别在于,长薄块可能会导致不必要的存储空间浪费,尤其是在处理稀疏数据时。为了更好地表示实际需要的数据,使用矩形或方形的块更为合适,尤其是在有矩形房间时,这种方式能够更有效地匹配需要的数据格式。
然而,如果考虑到标准屏幕尺寸(如16:9),可能会希望使用更适合的块大小,例如17x9的块。尽管这样做的查找成本更高(因为查找非2的幂次的大小会变得更贵),但这仍然是可行的,因为最终选择块的大小应该与实际需求相匹配,而不是单纯地选择一个简单的形状。因此,在决定使用哪种块格式时,考虑到具体数据的稀疏性和存储效率是很重要的。
这里使用了大量指针。
在游戏开发中,内存管理通常不会涉及垃圾收集或复杂的内存管理方式。为了避免内存泄漏,采用了一个简单的策略:所有资源都存储在一个大的内存块中,且一旦使用完毕,整个块会被清空,而不是单独释放每个对象。这意味着没有需要手动释放的内存,也不涉及指针管理或复杂的内存跟踪。
这种做法的核心思想是避免过度复杂的内存管理。通过在创建世界时将其作为一个整体块来处理,内存的管理变得简单,避免了需要释放单个资源的情况,从而减少了内存泄漏的风险。也因此,开发者无需关注如何处理内存释放的问题,整个内存块在不再需要时会一起被丢弃。
此外,避免使用反射和复杂的指针操作,因为这会增加不必要的管理成本。如果涉及到垃圾收集,通常是因为程序设计不当,内存管理机制不够简洁。因此,开发者应当避免写出需要复杂内存管理的代码,而应当关注如何保持程序结构的简洁与高效,这有助于避免不必要的内存管理开销。
内存分配区域是否会对齐分配的内存?
当涉及到内存分配时,对齐是一个需要关注的因素。特别是当内存需要按特定边界对齐时,例如16字节的对齐。为了确保内存正确对齐,可能会通过一个对齐参数传递给内存池或“memory arena”,从而使内存块以适当的方式分配。
对齐的目的是确保数据在内存中的存储方式与硬件要求一致,这通常有助于提升性能。比如,某些硬件架构可能要求数据按照16字节对齐,否则可能会导致性能下降。
你怎么能说不需要释放内存?
在内存管理方面,特别是处理敌人或类似对象时,有限的内存容量意味着不能有无限数量的敌人。尽管敌人可能会不断被生成和销毁,但通过合理的管理,确保内存的使用是可控的。对于生成新敌人时,只需要当旧敌人消失时才能产生新的敌人,这是一种相对简单且高效的方法。
至于内存访问的速度,内存访问通常是计算中最慢的操作之一。无论是指令执行还是数据收集,内存访问的速度都远慢于处理器的其他操作,尤其是从主内存读取数据时。为了优化性能,尽量避免频繁访问内存,并考虑如何将数据尽可能保留在缓存中,这样可以提高速度。
关于平铺(Tile Mapping)问题,平铺技术的优势在于它提供了一种易于理解和操作的地图表示方式。与使用多边形的方式相比,平铺地图更直观,便于玩家理解和交互。这种方式不仅能简化地图的渲染,也能让世界构建更具可操作性和趣味性。例如,平铺地图允许玩家在其中进行简单的谜题解答,增加了互动性。而多边形地图则可能显得较为复杂且难以处理,因为它缺乏明确的结构感。
总的来说,平铺地图为游戏的世界构建提供了一种基础框架,使得设计和互动更为直观,也让玩家的体验更加丰富。在设计时,选择平铺地图而不是多边形地图是因为它具有更好的可操作性和玩家理解度。
相关文章:
游戏引擎学习第34天
仓库:https://gitee.com/mrxiao_com/2d_game #这天内容比较多 开场介绍 游戏开发行业的基础是使用C和C编程,这是当今几乎所有游戏的开发标准。市面上广受欢迎的游戏,如《使命召唤》或《侠盗猎车手》,它们的底层代码和引擎几乎无一例外地采…...
深度学习笔记——模型压缩和优化技术(蒸馏、剪枝、量化)
本文详细介绍模型训练完成后的压缩和优化技术:蒸馏、剪枝、量化。 文章目录 1. 知识蒸馏 (Knowledge Distillation)基本概念工作流程关键技术类型应用场景优势与挑战优势挑战 总结 2. 权重剪枝 (Model Pruning)基本原理二分类1. 非结构化剪枝(Unstructur…...
[在线实验]-RabbitMQ镜像的下载与部署
镜像下载 docker的rabbitmq镜像资源-CSDN文库 加载镜像 docker load --input rabbitmq.tar 给镜像打标签 这里发现镜像名为none,需要给镜像重命名下 docker tag [镜像id] [新镜像名称]:[新镜像标签] docker tag ebaf409ffbe2 rabbitmq:management 运行镜像…...
Netty 入门应用:结合 Redis 实现服务器通信
在上篇博客中,我们了解了 Netty 的基本概念和架构。本篇文章将带你深入实践,构建一个简单的 Netty 服务端,并结合 Redis 实现一个数据存取的示例。在这个场景中,Redis 作为缓存存储,Netty 作为服务端处理客户端请求。通…...
推荐 编译器c++
网页型 https://www.acgo.cn/playground C 在线工具 | 菜鸟工具 AcWing - 在线题库 ZJYYC在线测评系统 少儿编程竞赛在线学习 登录 - JOYSKID 余博士教编程_酷哥OJ_酷哥爱编程_酷哥创客AI编程 登录 - Luogu Spilopelia 软件型 DEV-c Dev C软件下载...
【新品发布】ESP32-P4开发板 —— 启明智显匠心之作,为物联网及HMI产品注入强劲动力
核心亮点: ESP32-P4开发板,是启明智显精心打造的一款高性能物联网开发板。它专为物联网项目及HMI(人机界面)产品而设计,旨在为您提供卓越的性能和稳定可靠的运行体验。 强大硬件配置: 双核400MHz RISC-V处…...
MeterSphere 使用脚本处理数据
1、前置/后置脚本 支持BeanShell(JSR223)、python、groovy、JavaScript脚本语言,推荐BeanShell(JSR223)。 在前置脚本中可以直接引用JMeter 预定义对象,例如: -- log:用于在脚本执行过程中打印日志 //打印“Hello World!”到info…...
如何获取谷歌新闻API密钥?
在信息获取和新闻传播领域,快速获取最新的新闻动态至关重要。谷歌新闻API为开发者提供了强大的工具,能够方便地集成全球各类新闻内容。通过使用该API,开发者可以实现对新闻的实时访问和管理,为用户提供丰富的信息服务。本文将指导…...
【全网最新】若依管理系统基于SpringBoot的前后端分离版本开发环境配置
目录 提前准备: 下载源代码 设置依赖 设置后台连接信息 运行后台 运行前端 安装npm依赖 启动前端 登录网页客户端 提前准备: 1、安装mysql 5以上就可以。 2、安装redis. 3、安装npm npm下载地址:https://nodejs.org/dist/v22.12…...
备赛蓝桥杯--算法题目(3)
1. 2的幂 231. 2 的幂 - 力扣(LeetCode) class Solution { public:bool isPowerOfTwo(int n) {return n>0&&n(n&(-n));} }; 2. 3的幂 326. 3 的幂 - 力扣(LeetCode) class Solution { public:bool isPowerOfT…...
如何解决 java.nio.charset.CoderMalfunctionError: 编码器故障错误问题?亲测有效的解决方法!
java.nio.charset.CoderMalfunctionError 是一个在 Java 中相对较少遇到的异常,通常与字符编码转换过程中的错误有关。当 Java 程序在进行字符编码转换时,遇到无法处理的字符或编码故障时,就会抛出该异常。 1. 问题描述 java.nio.charset.C…...
电气自动化 基于PLC控制的四路抢答器设计
摘要 本文描述了一款用三菱FX3U-48M可编程控制器设计的四路抢答器的系统构成、设计思路和功能。此抢答系统除了有基本抢答功能之外,还有计时、计算得分、亮灯提提示以及蜂鸣提醒功能。程序中设定答题时间,在主持人未按下开始抢答按钮之前,选…...
GA优化后的RBF神经网络
遗传算法(Genetic Algorithm, GA)优化后的RBF(Radial Basis Function)神经网络是一种结合进化算法与神经网络的混合模型,用于改进RBF神经网络的性能。以下是该模型的基本原理和相关公式: clear all close a…...
Scala:正则表达式
object test03 {//正则表达式def main(args: Array[String]): Unit {//定义一个正则表达式//1.[ab]:表示匹配一个字符,或者是a,或者是b//2.[a-z]:表示从a到z的26个字母中的任意一个//3.[A-Z]:表示从A到Z的26个字母中的任意一个//4.[0-9]:表示从0到9的10…...
vulnhub靶场之【hacksudo】1.0.1
前言 靶机:hacksudo 192.168.1.45 攻击:kali 192.168.1.16 都是虚拟机环境,桥接模式 主机发现 使用netdiscover或者arp-scan -l扫描 netdiscover -r 192.168.1.1/24信息收集 使用nmap扫描 因为看到2222是ssh服务,所以又扫…...
第4章:颜色和背景 --[CSS零基础入门]
在 CSS 中,颜色和背景属性是用于美化网页元素的重要工具。你可以通过多种方式定义颜色,并且可以设置元素的背景颜色、图像、渐变等。以下是关于如何在 CSS 中使用颜色和背景的一些关键点和示例。 1.颜色表示法 当然!以下是使用不同颜色表示…...
Python实现PBKDF2_SHA256加密密码
加密保存格式:pbkdf2_sha256$迭代次数$盐$哈希值 admin可能的结果:pbkdf2_sha256$10000$yzsusUJwrGfonwZzVxlnA$vgf/OgLf5C4wtQLtfNY9d68Hhxgv8eqZ0mwfxCqqeU import os import hashlib import base64 def password_encrypt(password, saltNone, iterations1000…...
React第十三节开发中常见问题之(视图更新、事件处理)
一、视图更新有哪些方案? 1、对于数据变量 正常的增删改查,只会让数据更新,但是不会触发 React 视图的更新; 如: <script lang"jsx">const baseTable [{name:Andy, age: 18, id: 1},{name:Jack, a…...
PyTorch 深度学习框架简介:灵活、高效的 AI 开发工具
PyTorch 深度学习框架简介:灵活、高效的 AI 开发工具 PyTorch 作为一个深度学习框架,以其灵活性、可扩展性和高效性广受欢迎。无论是在研究领域进行创新实验,还是在工业界构建生产级的深度学习模型,PyTorch 都能提供所需的工具和…...
Vue 3 中的计算属性(Computed Properties)详解
目录 Vue 3 中的计算属性(Computed Properties)详解 引言 什么是计算属性? 创建和使用计算属性 示例 1:基本用法 示例 2:带有 getter 和 setter 的计算属性 计算属性 vs 方法 Vue 3 中的计算属性(Co…...
RocketMQ 过滤消息 基于tag过滤和SQL过滤
RocketMQ 过滤消息分为两种,一种tag过滤,另外一种是复杂的sql过滤。 tag过滤 首先创建producer然后启动,在这里创建了字符串的数组tags。字符串数组里面放置了多个字符串,然后去发送15条消息。 15条消息随着i的增长,…...
AI开发: 知识图谱的初识,学会制作知识图谱- Python 机器学习
一、知识图谱的概念 知识图谱是一个通过图结构来表示和组织知识的工具,它将事物、概念和它们之间的关系以图的形式呈现出来,图中的节点代表实体(比如人物、地点、事件等),而边代表这些实体之间的各种关系(…...
windows系统的环境变量(系统变量)不能编辑可能是这个原因
有些电脑从开始菜单那搜索“环境变量”后是没法编辑系统变量的,只能从设置里面进”环境变量“来进行编辑。 可以观察到系统环境变量即便是点击到了,但还是无法进行编辑...
各种服务器使用 yum 安装 nginx
1. 在Red Hat Enterprise Linux (RHEL) 及其衍生版本(如CentOS, Oracle Linux, Rocky Linux, AlmaLinux)上安装Nginx的步骤如下: 安装前提条件 首先,确保系统已安装yum-utils工具,这将有助于管理软件包和仓库&#x…...
基于BesselJ函数,构建AI中的卷积神经网络之新型卷积核
原创:daode3056(daode1212) BesselJ函数,来自微分方程: 它有通解有以下形式: 就取J0--J5的函数图像,如下: 现取J1(x),也就是红色的这支,作新的函数: 这一函数的导数与不定积分如下:…...
容器镜像仓库
文章目录 1、docker hub1_注册2_登录3_创建容器镜像仓库4_在本地登录Docker Hub5_上传容器镜像6_下载容器镜像 2、harbor1_获取 docker compose二进制文件2_获取harbor安装文件3_获取TLS文件4_修改配置文件5_执行预备脚本6_执行安装脚本7_验证运行情况8_访问harborUI界面9_harb…...
qtcanpool 知 08:Docking
文章目录 前言口味改造后语 前言 很久以前,作者用 Qt 仿照前端 UI 设计了一个 ministack(https://gitee.com/icanpool/qtcanpool/blob/release-1.x/src/libs/qcanpool/ministack.h) 控件,这个控件可以折叠。部分用户体验后&#…...
车载VR可视化解决方案
车载VR可视化解决方案是通过融合跟踪用户头部运动的特殊预测算法与惯性测量数据而开发的。该系统将大范围虚拟现实跟踪技术与IMU传感器相结合,为VR和AR应用打造了一套全面的运动跟踪与渲染流程,极大地方便了虚拟现实头显制造商定制可视化流程。 该车载VR…...
hhdb数据库介绍(10-43)
安全 密码安全管理 密码安全管理为用户提供了对计算节点数据库用户与存储节点的连接用户、备份用户的密码有效期监控提醒。到期后自动提示用户修改密码以提升系统的安全性。 数据库用户密码 (一)密码修改 用户可以在“安全->密码安全管理->数据…...
【优选算法 二分查找】二分查找入门详解:二分查找 & 在排序数组中查找元素的第一个和最后一个位置
二分查找 题目描述 题目解析 暴力解法 我们可以从左往右遍历一次数组,如果存在 target 则返回数组的下标,否则返回 -1; 时间复杂度 O(N),因为没有利用数组有序的特点,每次比较只能舍弃一个要比较的数&…...
Python导入moviepy找不到editor 视频没有声音设置audio_codec参数
moviepy合成视频出错: 问题一:导入moviepy.editor找不到editor,No module named moviepy.editor问题二:合成的视频没有声音 问题一:导入moviepy.editor找不到editor,No module named moviepy.editor from …...
FreeSWITCH auto-rtp-bugs 研究
还在探索中... 抓一个现场的包, Fs 收 rtp 包正常,但 发 rtp 包有问题,比如上次 ts 是 1 万 ,而这次是 1000,并且居然没有 marker。百思不得解。 vars.xml 增加一个全局变量的配置: <X-PRE-PROCESS …...
TypeScript 在 React 中的应用
文章目录 前言一、为什么要在 React 中使用 TypeScript?二、如何在React中使用 TypeScript三、高级类型结语 前言 随着前端开发的复杂度不断提升,开发者对于代码质量、可维护性和开发效率的要求也日益增高。TypeScript 作为一种为 JavaScript 添加静态类…...
汇编和C语言访问存储器
一、汇编语言访问存储器 1、读存储器 LDR R1, [R2] 2、写存储器 STR R1, [R2] 二、C语言访问存储器 1、读存储器 data *ADDR; 2、写存储器 *ADDR data;...
14.3、特洛伊木马分析与防护
目录 特洛伊木马概念与分类特洛伊木马运行机制特洛伊木马植入技术特洛伊木马隐藏技术特洛伊木马存活技术特洛伊木马防范技术Rootkit是什么? 特洛伊木马概念与分类 病毒不具备传播能力,但是蠕虫具备 特洛伊木马运行机制 木马攻击过程主要分为五个部分: ① 寻找攻…...
SpringBoot 分层解耦
从没有分层思想到传统 Web 分层,再到 Spring Boot 分层架构 1. 没有分层思想 在最初的项目开发中,很多开发者并没有明确的分层思想,所有逻辑都堆砌在一个类或一个方法中。这样的开发方式通常会导致以下问题: 代码混乱࿱…...
不一样的CSS(4)--icon图标系列之svg
序言 上一节内容我们讲解了如何利用css去画一个五角星,其中包括了使用svg的方法,有些小伙伴们对svg的使用不是很了解,那么本节内容我们主要来讲一下,关于svg标签的的使用。 目录 序言一、svg的介绍二、安装SVG扩展插件三、SVG基…...
Go-知识依赖管理2
Go-知识依赖管理2 1. go.sum1.1 go.sum 文件记录1.2 生成1.3 校验1.4 校验和数据库2. 模块代理2.1 GOPROXY 介绍2.2 代理协议2.2.1 获取模块列表2.2.2 获取模块元素数据2.2.3 获取 go.mod 文件2.2.4 获取代码压缩包2.2.5 获取模块的最新可用版本2.2.6 下载过程2.3 观察下载步骤…...
el-select的搜索功能
el-select的相关信息: 最基本信息 v-model的值为当前被选中的el-option的 value 属性值 :label是选择器可以看到的内容 过滤搜索 普通过滤搜索 <el-selectv-model"selectedCountry"placeholder"请选择国家"filterable:loading"lo…...
批量将不同的工作簿合并到同一个Excel文件
批量将不同的工作簿合并到同一个Excel文件 下面是一个示例,展示如何批量将不同的工作簿合并到同一个Excel文件,并生成模拟数据。我们将使用 Python 的 pandas 库来完成这个任务。具体步骤如下: 步骤 1: 安装必要的库 首先确保你已安装 pan…...
git遇见冲突怎么解决?
问: 回答:...
Spring和SpringBoot的关系和区别?
大家好,我是锋哥。今天分享关于【Spring和SpringBoot的关系和区别?】面试题。希望对大家有帮助; Spring和SpringBoot的关系和区别? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring和Spring Boot是两种相关但有所…...
python学习——字符串的编码和解码
在Python中,字符串的编码和解码是处理文本数据时非常重要的概念。以下是对字符串编码和解码的详细解释: 字符串编码 字符串编码是将字符串转换成字节序列的过程。Python中的字符串是Unicode编码的,所以在将字符串转换成字节序列时ÿ…...
Web游戏开发指南:在 Phaser.js 中读取和管理游戏手柄输入
前言 Phaser.js 是一个广受欢迎的 HTML5 游戏框架,为开发者提供了创建跨平台 2D 游戏的强大工具。在现代游戏开发中,支持游戏手柄已成为提升玩家体验的重要方面。本文将详细介绍如何在 Phaser.js 中监听和处理游戏手柄的输入,帮助开发者为他…...
HTML5系列(13)-- 微数据与结构化数据指南
前端技术探索系列:HTML5 微数据与结构化数据指南 📊 致读者:探索数据语义化的世界 👋 前端开发者们, 今天我们将深入探讨 HTML5 微数据与结构化数据,学习如何让网页内容更易被搜索引擎理解和解析。 微数…...
MAA ADB问题
模拟器官方MUMU12 连接设置 | MaaAssistantArknights 参考文档,找谷歌platform ADB,放入MAA文件夹 选择谷歌ADB,选择MUMU12的ADB代码 MuMu 模拟器 12 127.0.0.1:16384 重新连接 ok...
基于VTX356语音识别合成芯片的智能语音交互闹钟方案
一、方案概述 本方案旨在利用VTX356语音识别合成芯片强大的语音处理能力,结合蓝牙功能、APP或小程序,打造一款功能全面且智能化程度高的闹钟产品。除了基本的时钟显示和闹钟提醒功能外,还拥有正计时、倒计时、日程安排、重要日提醒以及番茄钟…...
大语言模型应用开发框架LangChain
大语言模型应用开发框架LangChain 一、LangChain项目介绍1、简介2、LangChain的价值3、实战演练 二、LangChain提示词大语言模型应用1、简介1.1、提示词模板化的优点1.2、提示词模板LLM 的应用1.3、Prompt 2、应用实战2.1、PromptTemplate LLM2.2、PromptTemplate LLM Outpu…...
php7.4安装pg扩展-contos7
今天接到一个需求,就是需要用thinkphp6链接pg(postgresql)数据库。废话不多说,直接上操作步骤 一、安装依赖 yum install -y sqlite-devel libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel libcurl libcurl-devel libjpeg libjpeg-dev…...
【开源】A064—基于JAVA的民族婚纱预定系统的设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…...