游戏引擎学习第35天
开场介绍
今天的任务是继续改进一个虚拟的瓦片地图系统,使其适合处理更大的世界。我们希望这个系统能管理大范围的游戏世界,其中包含按需存储的小区域。昨天,我们介绍了“内存区域”的概念,用于管理持久性存储。我们计划今天继续这个主题,深入探讨它的工作原理及其可能的优化方向。
目前,我们的世界生成还相对简单,只是随机生成了一些内容。今天的目标是进一步完善这个过程,并实现一个可以存储和加载更大规模游戏地图的功能。
创建一些房间
我们开始设计一个简单的房间系统。为了让游戏中的小角色能够在这些房间里活动,我们需要设置一些墙壁来限制空间。最初,系统是随机生成的,屏幕大小为32x32,每个瓦片的值也都是随机设置的。为了改进这一点,我们决定在游戏中加入一些实际的墙壁。
在实现时,墙壁的放置方式是:如果某个瓦片的位置位于屏幕的边缘,我们就将这个瓦片的值设为1,这样它就会成为一个“拦截器”或者墙壁。这样做可以在屏幕的边缘生成水平墙壁,同时也可以生成垂直墙壁,从而有效地创建封闭的房间。
通过这种方式,我们能够创建出一个被墙壁围住的空间,使得角色无法轻易离开这个房间,直到墙壁的设置完成。最终,这种设置形成了一个简单的房间,角色被限制在其中,无法脱离该区域。通过这样的方式,我们为小角色提供了一个明确的活动空间。
创建一些门
目前已经有了基本的房间布局,但是还没有添加任何门。接下来需要做的是为这些房间添加门。门的生成有一些条件,首先需要检查是否满足条件才能在指定位置生成门。如果一个房间的高度大于 2,则不需要门;否则,若宽度超过 2,那么门会出现在中间位置。
这样就能在房间中生成门,使得玩家能够通过这些门穿越不同的房间。这样创建的世界会非常庞大,但目前它还只是一个简单的房间布局,里面没有其他特殊内容。通过这个过程,可以快速生成多个房间,但这些房间目前并没有充实的内容。
进一步的想法是测试地图的稀疏性,尝试进行优化或变化,例如引入一些可能影响帧率的测试。当前的实现方式可能会很慢,因为我们正在循环遍历所有的瓦片并对其进行操作,因此需要进一步优化来提升性能。
小房间和瓷砖 ID 值的处理
-
瓦片尺寸调整:
- 假设我们将瓦片的尺寸做得非常小,这样在同样的屏幕空间内就能放置更多的瓦片。通过调整瓦片的大小,可以使得在屏幕上显示更多的瓦片,例如,可能将像素单位调整为更小的值,或者以更高的分辨率绘制瓦片地图。
-
调整世界的大小:
- 通过改变瓦片的大小,可以改变游戏世界的规模。当前测试场景的像素数量会影响地图的大小。例如,将瓦片尺寸缩小,使得每个瓦片占据的空间减少,可以让地图的显示变得更大,绘制的瓦片数量也会增加。
-
绘制瓦片地图:
- 初步目标是绘制一个较大的瓦片地图版本,而当前的绘制方法可能会导致帧率变慢,特别是如果我们没有进行优化时。例如,当瓦片数量增加,绘制的计算量增大时,性能可能会下降。
-
初始化瓦片地图:
- 瓦片地图中的某些部分可能还没有初始化。如果某个瓦片区域没有内容(即未初始化),则需要使用不同的颜色或标记(例如灰色)来表示这些区域。这有助于在调试时看到哪些部分是空的,哪些部分是有效的。
-
处理无效瓦片:
- 当访问瓦片地图时,如果某个区域没有被初始化或不包含任何内容,可以将其标记为无效瓦片。当前如果返回值为零,就意味着该位置没有瓦片。在绘制时,可以选择不绘制这些无效区域,避免浪费性能。
-
瓦片的不同值:
- 当前的地图使用数值来表示不同类型的瓦片。例如,
0
代表没有瓦片(空白区域),1
代表可行走区域(或地面),而2
可能代表墙壁或不可通过的区域。这些值可以用来定义不同的瓦片类型和其行为。
- 当前的地图使用数值来表示不同类型的瓦片。例如,
-
存储和数据结构:
- 讨论中提到,当前的瓦片存储方法并没有涉及到实际的存储结构和优化。瓦片存储需要更多的考虑,因为随着地图的复杂性增加,瓦片地图的存储需求和数据量也会变得更加庞大。可能需要考虑如何优化存储结构,以更高效地管理这些数据。
-
下一步工作:
- 下一步可能是进一步考虑如何设计和优化这些瓦片地图,特别是在存储和访问瓦片数据时,如何平衡性能和可扩展性,避免无效区域的绘制,并使地图可以更高效地加载和渲染。
仅在某些位置生成房间 - 随机数的使用
-
世界布局与坐标定义:
- 当前世界的坐标系统定义了世界的范围,然而并没有对所有位置的瓦片进行定义。例如,某些区域可能没有瓦片或内容,返回值为零。
- 提到世界是“环形的”(torodial),即如果从一边走出会从另一边重新出现,这种设计意味着世界的左右边界是连接的。
-
生成房间的策略:
- 当前的想法是,只在某些地方生成房间,而不是遍历整个世界生成房间。这样可以减少计算量,并控制房间的分布,使世界变得更加有序和合理。
-
随机数的使用:
- 在生成房间时,讨论了随机数的使用。计划使用某些方法生成随机数字来决定哪些位置生成房间或进行其他操作。
- 提到可以通过使用 C 运行时库来生成随机数,但也在考虑其他方式来生成随机数字,不一定依赖 C 库。
-
屏幕绘制与布局:
- 提到当前的屏幕布局是一个 32x32 的瓦片网格,而在优化时,考虑到画面显示数量增加的情况下,如何使得世界更加高效。
- 计划将屏幕的生成从简单的循环播放改为更加动态的、基于随机数的方式生成屏幕。生成的屏幕数量将从 32 增加到 100,且不再按固定顺序排列。
-
选择和移动屏幕:
- 采用随机方式选择屏幕的位置,而不是简单地按照索引顺序排列屏幕。例如,可以选择一个随机位置创建一个屏幕,并根据随机数字选择下一个屏幕的位置。
-
屏幕数量与逻辑:
- 最初的设计使用了 32 个屏幕,但现在计划增加屏幕数量至 100。新的生成方法将基于随机数来决定屏幕的位置和生成的顺序,而不再是按固定的顺序进行。这种方法能够更灵活地布置屏幕,提高游戏世界的多样性和复杂性。
总的来说,这段内容主要讨论了如何通过改变屏幕的生成方式以及使用随机数来调整世界的布局和房间分布,以创建一个更加动态和复杂的游戏世界。
回到随机地图生成
-
场景描述:
- 首先,讲述了一个“数字屏幕”或者“房间”的设计过程,房间有门,可以随机生成或调整。
- 随机性在布局过程中起着重要作用,可以通过增加或减少屏幕的X或Y坐标来生成不同的布局。
-
随机选择:
- 使用随机数生成器来决定选择房间的方向(例如,增加X或Y坐标)。
- 采用
mod
操作符来通过取余数来决定随机选择的结果,这通常用于生成二选一的情况,例如取余2,得到0或1。
-
随机数生成:
- 提到了如何生成一个随机数表,并使用一个随机数索引来选择数字。
- 生成器需要在后续的世界生成中发挥作用。
-
实现与调试:
- 随着系统开发,出现了一些编程问题和调试细节,例如代码缩进问题和性能问题。
- 讨论中提到了生成随机数时遇到的一些困难,以及尝试使用不同的技术来提高代码效率。
-
代码和算法:
- 对话中涉及到使用宏(例如键盘宏)和文本模式来加速开发,并谈论了C++中的代码结构和调整。
- 还讨论了如何有效地进行编辑、代码格式化和宏使用。
整体来看,这段对话涵盖了房间布局生成的基本原理,特别是在程序中如何通过随机数控制房间的排列,并且提到了生成器、调试过程以及优化方面的挑战。如果需要更详细的代码示例或进一步的解析,随时可以告诉我!
随机生成的房间
还没有达到稀疏的效果
如何将一个本来存储整个地图的做法,转变为一个更加稀疏的存储方式。起初,整个地图被预先分配了存储空间,但并没有实际填充任何数据,只是创建了一个巨大的区域。随着开发的推进,目标是让地图数据的存储更加高效,只在需要的地方分配存储空间。
在实现过程中,采用了16x16的块来划分地图区域,每个块只有在需要填充瓷砖时才会分配实际的存储空间。这种做法称为“懒加载”或“按需分配”,即只有在需要填充数据时才进行存储分配,而不是预先为整个地图分配大量内存。
为了实现这一点,首先会检查是否有瓷砖需要存储,如果没有,则不会为该区域分配存储空间。只有在访问某个区域并发现该区域没有初始化时,才会为其分配存储空间。这样,内存使用更加高效,避免了存储不必要的空白区域。
这种方法使得游戏世界可以更加庞大,因为不再需要为每个可能的区域预留大量的内存。只有实际可见或需要的数据才会被存储,其他地方则不占用内存。这种“稀疏存储”的方法减少了内存的浪费,并且可以轻松扩展世界的规模。
最终,通过这种方法,存储变得更加高效,玩家也不会察觉到世界的其他部分的存在,除非他们实际进入并加载这些区域。
制作门
在这个逻辑中,主要任务是实现一个系统,管理屏幕之间的门生成和墙绘制逻辑,并且确保这些门和墙的布置能够动态地适应屏幕的布局和随机选择。
-
门的位置随机性
- 在进入屏幕时,系统会随机选择门的位置。
- 每个屏幕的门布置需要根据相邻屏幕的门状态来动态调整。例如,如果左侧有一扇门,那么这个屏幕的右侧也需要有一扇门以保持连通性。
-
门和墙的初始化
- 使用逻辑判断,确定当前屏幕的边界(顶部、底部、左侧、右侧)是否需要绘制墙或生成门。
- 如果边界在中心区域且需要生成门,那么会绘制一扇门;否则会绘制一堵墙。
-
逻辑设计要点
- 在生成门时,必须确保上一屏幕的门状态被正确传递到下一屏幕。例如,如果当前屏幕右侧有门,那么相邻屏幕的左侧也需要有对应的门。
- 对于顶部和底部边界,同样的逻辑适用。
-
代码的改进点
- 初始逻辑是基于条件判断和剪切复制的代码,虽然可以工作,但不够优雅。
- 可以通过优化条件判断,减少重复代码,使逻辑更加简洁和清晰。
-
测试与调试
- 在完成基础功能后,对生成的门和墙的布局进行测试。
- 确保门的生成逻辑与相邻屏幕的需求一致。
- 修正了一个门生成错误,具体问题在顶部门的条件判断中,因符号错误导致逻辑失效。
-
实际运行结果
- 完成了基本的门和墙绘制功能,能够根据屏幕布局生成合理的门。
- 系统可以动态调整屏幕间的连接,确保每个屏幕的布局与随机性生成逻辑匹配。
这个逻辑主要实现了屏幕布局的动态生成,并通过门的随机性增强了地图的可玩性,同时保证了屏幕之间的连通性。
回到显示单个房间的状态
从一个近距离的视角观察,可以想象如果摄像机被锁定在某个位置,玩家只能看到摄像机前的区域。在这种情况下,玩家不会意识到世界的外部是如何被存储的,因为他们永远看不到世界之外的部分。实际上,虽然世界的外部并未被存储在玩家视野中,但玩家会被引导去认为这个世界是无缝存在的,实际上世界的外部并没有被完全渲染出来。
为了避免让玩家察觉这一点,如果需要让摄像机平滑滚动至其他区域,我们可以通过设计一个“围绕房间”的空间填充来确保无论摄像机如何移动,都有东西供玩家查看。通过填充这些空白区域,可以使玩家始终看到一些内容,即使他们看不到实际的世界。
如果不想这样做,也可以采取另一种策略。当到达某些没有内容的区域时,可以随机生成一些图像或背景来填充这些区域,而不是让玩家看到空白区域。这样做可以在视觉上维持一致性,并给玩家一种完整世界的错觉。
在设计这些瓦片地图时,目标是通过平铺数据来填充世界,即使这个世界并不完全被储存,也能让玩家认为它是无缝的。通过精心设计这些瓦片,可以确保当玩家走到某个地方时,他们不会注意到世界的边界。
整体而言,通过这种设计,可以确保玩家在探索虚拟世界时,体验到一个流畅且持续的游戏环境,而不必担心地图的大小或设计上的限制。通过对空间和视觉的精心布局,可以保持游戏的沉浸感。
此时的地图已经相当大,虽然在测试时,穿越一百个屏幕需要相当长的时间,但这也展现了游戏世界的广阔。而这些测试图形本身,更像是展示游戏核心功能的工具。它们提醒人们,曾经的经典冒险游戏也用了类似的技术。那些早期的游戏在视觉上看起来简单,但依然能够给玩家带来深刻的体验。尤其是“冒险”这款经典游戏,它通过非常简单的图形和有限的资源,依然能够吸引玩家投入大量时间,探索这个简单但充满乐趣的世界。
瓦片地图的回顾
现在,瓦片地图已经基本完成,接下来是讨论如何优化和改进这一实现。首先,瓦片地图的尺寸可以灵活调整。一个关键点是,我们不再需要将瓦片大小与像素大小绑定在一起,因为瓦片地图本身不需要关心像素的具体细节,它只是一个抽象的表示。瓦片的大小和像素之间的关系不再重要,因此应该将这种绑定关系移除。通过这样做,我们能让瓦片地图独立于实际的像素渲染,从而让地图的存储和渲染变得更简洁。
接下来,考虑到存储效率,提出了一个虚拟化的瓦片存储方式。当前的系统在处理瓦片时仍存储一个包含指针的数组,而这些指针指向实际的瓦片数据。这样,尽管整个世界的大小可以非常庞大,但实际上我们只存储了小块区域。存储时,世界的每个区域并不是全尺寸的,而是分成了较小的“块”进行存储,类似于一个二维指针数组来表示世界的瓦片。
通过这种方法,可以在有限的存储空间内管理一个非常大的世界。例如,如果当前世界的大小是四十亿瓦片,那么只存储16x16的区域块。这种分块存储的方式大大减少了内存占用,因为我们只存储必要的区域数据而非整个世界的每个瓦片。
然而,尽管这种方式节省了大量内存,但仍然有优化空间。如果能进一步减少存储的数据量,系统将变得更加高效。为了达到更高的稀疏性,可以考虑进一步优化存储方法,例如通过“真正的稀疏存储”方式,仅在需要时动态加载和存储数据,而不是将每个可能的区域都事先分配好。这种方式可以极大减少内存消耗,尤其是在处理大规模世界时。
最终,虽然当前的存储方式已经通过指针数组减少了内存占用,但为了达到最优的稀疏性,可能还需要进一步探索不同的存储策略,比如基于空间位置的稀疏查找方法。这些方法可以确保只在必要时加载和存储瓦片数据,从而最大化内存利用率和处理效率。
添加上下楼梯的功能
在开发城市地图时,添加了对Z轴的支持,以便能处理上下层次。这个修改的目标是使地图能够处理多个高度层次,在这种情况下,Z轴代表着垂直维度。增加Z轴的功能不难,主要是通过给地图增加一个“上下”移动的能力来实现。最初,Z轴只是一个附加的概念,用来支持更复杂的地图层次结构。
这个过程中,Z轴并不会像X轴或Y轴那样直接影响地图的存储方式,因为通常我们不需要在任何时刻查看多个Z层。对于大多数情况来说,Z轴层是分离的,与X和Y轴的操作有所不同,通常代表着“上下”之类的层次移动,而不是二维空间中那样的平面划分。
接着,修改了地图存储的方式,将Z轴作为一个附加的维度来处理。具体来说,存储时,Z轴会对每个瓦片进行标记,将其与X和Y坐标结合,使得每个瓦片都能具备一个高度属性。这样,Z轴的操作和存储方式需要特别处理,尤其是在获取或设置瓦片值时,每次都会附加Z轴信息。
这个过程中,为了便于操作,采用了三维坐标的存储方式,而不是简单的二维平面。将X、Y和Z结合后,形成了一个三维空间,使得可以更清晰地表示不同层次的瓦片。在存储时,X轴表示横向,Y轴表示纵向,而Z轴则用于表示上下的层级关系。每次移动Z轴时,都会跨越一整块X和Y的区域。也就是说,Z轴的移动代表着在三维空间中的“跳跃”,每次上升一个Z层就会改变当前可见的瓦片。
总体来说,添加Z轴的支持能够为地图带来更多的层次结构,使得城市地图更加立体和复杂。而这个修改并不会引入太多新的复杂性,因为主要的操作还是围绕着传统的二维瓦片地图展开。
继续疯狂地增加一些楼梯
这段文本描述了一段代码调试的过程,涉及到随机选择和门状态控制等问题。下面是详细的总结和复述:
-
开始时,主要关注的是一个与随机选择相关的逻辑,用于处理某种网格或地图系统。随机选择操作从三个选项中选择一个,每个选项代表一种可能的动作或结果。根据随机选择的结果,触发不同的条件。
-
尝试根据随机选择的结果设置不同的动作和条件,具体条件与门的状态相关。例如,当条件等于“二”时,执行某些特定操作;否则,根据不同的条件切换样式或其他变量。条件似乎关注门是否存在,以及门的“向上”或“向下”状态,这可能是与游戏机制或关卡元素相关的设置。
-
在某一时刻,逻辑涉及设置门的状态,确保某个房间内没有出口,或者根据需要调整瓦片或其他对象的状态。
-
接下来,代码检查某些属性(如库存)是否相等,并根据条件触发相应的操作。代码中出现了一个明显的错误,导致门的状态出现异常行为,可能是由于设置不当或逻辑流程错误。
-
还有对网格中的瓦片位置(X、Y、Z坐标)进行处理,处理“向上”和“向下”门的状态。问题在于确保状态正确转换,避免门被错误地放置或操作。
-
调试过程中发现与“门”状态相关的错误,门的状态切换似乎存在故障,导致一些预期条件没有得到满足。
-
在回顾这些行为后,认为问题可能出在门的切换逻辑上,或者是某些条件值(如库存或瓦片条件)的设置错误。尽管面临这些挑战,仍在尝试通过追踪逻辑找到问题的根源并解决它。
-
调试过程涉及大量的试错,目标是修正因随机选择逻辑和瓦片处理导致的错误,尤其是与门状态和瓦片条件相关的问题。
核心问题是确保门的放置和状态转换符合预期,同时排查在随机选择和瓦片管理中的错误。整个过程强调了在处理网格或系统中的状态变化和交互时的复杂性,特别是当涉及随机结果和特定条件时。
你会使用德摩根定律(DeMorgan’s Laws)吗?
这段对话讨论了布尔表达式的简化,特别是涉及德摩根定律和真值表的使用。以下是详细总结:
-
德摩根定律和简化布尔表达式:
- 德摩根定律指出一些常见的布尔表达式等价关系,例如:
not (A and B)
等同于not A or not B
,这可以帮助简化复杂的布尔表达式。尽管这是一种有效的简化方法,但实际中不常用,尤其是在编写代码时。
- 德摩根定律指出一些常见的布尔表达式等价关系,例如:
-
实践中的做法:
- 在实际编程中,简化布尔表达式并不是常见的做法。通常情况下,开发者倾向于将代码保持简洁和直观,尽量通过清晰的表达式让代码易于理解,而不是为了优化布尔运算的数量去做过多简化。
- 在很多情况下,编译器会自动优化这些布尔表达式,因此手动简化的必要性较小。
-
编译器的优化作用:
- 开发者倾向于依赖编译器来处理这类优化工作。编译器通常能够自动执行这些优化,从而减少开发者的工作量。因此,简化布尔表达式的主要工作交给编译器完成,而开发者则更多关注代码的可读性和表达清晰性。
-
语言学简化的优先级:
- 开发者更注重语言表达的简洁性,而不是优化布尔运算的数量。清晰的表达式让代码更易于理解和维护,因此在写代码时,开发者倾向于使用更直观的表达式。
-
真值表的使用:
- 真值表是一种帮助分析和简化布尔表达式的工具,通过列出所有可能的输入条件并计算每种情况下的输出,帮助了解布尔逻辑的行为。尽管这是一种理论上有效的方法,但实际上并不常用。
- 真值表类似于电子表格,其中列出所有条件,并分析每种条件组合下的布尔表达式结果。
-
总结:
- 总体来说,虽然德摩根定律和真值表是有用的工具,但在实际编程中,简化布尔表达式更多依赖编译器的优化,而开发者则倾向于关注代码的清晰表达,而不是手动进行布尔运算的简化。
将瓦片地图放入 3D 维度,这是否与体素八叉树算法有关?
-
三维空间和算法:
- 提到将时间纳入三维空间,虽然这种想法可能不直接相关于某个特定算法(如 “Vauxhall Autrey Algorithm”),但讨论的重点是如何理解空间的分割与优化。
- 在三维空间中,提到可以将一个体积分割为多个部分,例如将一个立方体划分成八个较小的立方体。这种操作有点像魔方的分割,可以进一步提升每个小立方体的分辨率。
-
在三维空间中填充:
- 讨论了在三维空间中如何以无偏的方式进行空间填充,即确保在所有维度中都有均匀分布。这种方法在某些情况下非常有效,尤其是在处理三维空间时,避免某一维度不均匀填充。
-
游戏世界的空间划分:
- 对于游戏世界,尤其是在二维游戏的上下文中,这种三维分割的概念并不总是适用。二维游戏通常会集中于几个“切片”或“视图”,而不是三维空间的完整填充。
- 游戏的世界划分通常依赖于视角和地牢布局。例如,可能会查看地牢的底层或顶部,或者观察一定数量的“切片”而不是全景。
-
二维和三维视角的差异:
- 在二维游戏中,三维的空间划分并不总是有意义,因为大多数游戏世界的视角和层次结构较为简单,通常只处理较少的高度或深度。这里的讨论强调了二维与三维游戏中空间处理的差异。
-
树形结构的局限性:
- 提到在游戏世界划分中,使用树形结构来分割空间并不总是最优选择,特别是当游戏涉及到复杂的世界结构时。树形结构可能在某些三维游戏中有效,但在处理大规模、复杂的二维或多维地图时可能并不高效。
总的来说,这段讨论将三维空间的划分和优化技术与游戏世界的设计进行了对比,探讨了在不同类型的游戏中如何有效地应用这些概念。
为什么区块大小设置为 2 的幂次?
这段对话深入探讨了算法优化和存储结构,特别是在处理数据块大小和查找操作时的选择。以下是详细总结:
-
块大小与2的幂:
- 讨论了使用2的幂作为块大小的原因。这样做是因为在查找操作中,使用移位和掩码(shift and mask)可以高效地分割数据。这种方法仅在块大小为2的幂时有效,因为移位和掩码操作能够快速地提取数据的高低位部分。
- 如果块大小不是2的幂,就无法使用这种优化方法,可能需要使用常规的除法和余数操作(divide and remainder)来代替。这样的方法虽然能完成相同的任务,但效率较低,因为需要进行除法运算。
-
移位与掩码:
- 通过移位操作,可以将数据分割成两个部分:一个是块的高位部分,另一个是低位部分。这个过程依赖于块大小是2的幂的前提,因为这使得移位操作变得简单且高效。
- 这使得块的划分非常清晰,且能高效地进行索引。每次取余数来确定某一部分的数据(比如瓷砖的索引),而通过移位操作来确定数据的其他部分。
-
替代方案:除法与余数:
- 如果不使用2的幂,理论上可以通过常规的除法和余数操作来进行划分。虽然这种方法可以工作,但其性能通常不如移位和掩码高效,尤其是在需要频繁进行查找操作时。
-
性能调优与选择:
- 讨论了在性能优化过程中做出的决策,强调在开发游戏引擎时要尽量避免做出那些会限制未来选择的设计决策。例如,某些操作可能并不需要特别快,但在某些情况下为了更好的存储和灵活性,采用简单的除法和余数操作也是可行的。
- 重要的是,在开发过程中要确保能够保持灵活性,以便未来能够根据需要调整优化方案,避免过早地做出决策导致无法后续调整。
-
存储与操作的平衡:
- 在决定使用哪种优化方法时,需要权衡存储的效率和操作的速度。如果存储是主要考虑因素,那么可以接受稍慢的操作,以减少对存储空间的需求。
- 反之,如果操作速度更为重要,可能需要继续使用2的幂作为块大小,利用移位和掩码来优化性能。
总之,这段讨论的核心在于如何平衡存储优化与操作效率。在设计和开发时,必须考虑性能需求,同时保持灵活性,以便在实际实现中根据具体情况做出最佳选择。
什么时候会使用 #define
而不是局部变量?
这段对话探讨了使用全局变量的情况及其优缺点,尤其是如何在不同的情境下决定是否定义全局变量。以下是详细总结:
-
定义全局变量的时机:
- 使用全局变量是否合适,取决于具体的需求和情况。在某些情境下,使用全局变量可能是一个很好的选择,但没有唯一的“正确”或“错误”的答案。关键在于理解使用它们的差异,并据此做出选择。
-
全局变量的类型问题:
- 无类型变量(如常量):在某些情况下,使用没有类型的全局变量(例如常量值)可能是有利的。这样做的一个好处是这些变量可以在任何地方使用,而不需要明确的类型定义。对于一些不需要类型约束的操作,这种方式非常灵活。
- 有类型的变量:然而,如果你需要对数据进行类型控制,尤其是需要确保某些操作只在特定类型(如无符号整数、浮点数等)下进行时,定义全局变量的类型是非常重要的。一个例子是,如果你希望某个值只能在特定情况下(如只有在
UN32
的情况下)使用,明确的类型定义可以避免错误发生。
-
移除类型的优缺点:
- 优点:没有类型的全局变量可以直接插入表达式中,使得代码更加灵活。这种方法适用于那些不需要对数据进行类型严格控制的情况。
- 缺点:没有类型也意味着在某些情况下,可能会导致数据使用错误。例如,如果一个无符号 32 位数(
UN32
)被错误地用于不适合的场景,可能会导致程序错误。为了避免这种情况,可能需要对变量进行类型转换或额外的类型检查。
-
全局变量的具体应用场景:
- 在某些情况下(例如使用浮点数后缀或需要特定的类型),全局变量可以帮助确保变量有一个明确的类型。例如,如果想要一个八位类型的全局变量,可能就需要通过显式定义类型来确保其大小和特性。
- 例子:如果需要使用某些操作(如浮点数运算)而该操作只适用于某种类型,定义类型明确的全局变量就能够确保数据的正确性。
-
灵活性与限制:
- 全局变量有助于在不需要特别精细控制类型的情况下实现灵活性,但有时需要明确类型以避免潜在的错误。在某些复杂场景中,无法简单地依靠类型推断,必须使用强类型定义。
-
总结:
- 总的来说,使用全局变量是否合适取决于应用场景。如果不需要严格的类型控制,使用无类型的全局变量可以提高灵活性;如果需要控制数据类型,则需要明确地定义全局变量的类型。每种方法都有其适用的场景,且没有绝对的“正确”与“错误”。
为什么会偏好使用瓷砖区块数组?
-
内联变量的使用:
- 对于内联变量的问题,提到并未使用内联变量。没有明确说明为何不使用内联变量,主要是因为相关的概念并不在当前讨论范围内。
-
为什么不用位标志(bit flags)表示门的状态:
- 标志通常用于表示二进制状态,但当前的游戏设计并不适用这种方法。原因是,目前的地图还未完成设计,也未明确如何存储这些地图信息。门只是用一个数字表示,以便进行测试,并未深入实现。
-
为什么不使用简单的传送门:
- 在某些情况下,传送门可以简化游戏的设计,使玩家在地图的不同部分之间移动。但如果只是使用传送门,无法满足更复杂的游戏需求。例如,传送门无法处理玩家看到的世界几何形状与实际地图之间的关系。想要展示多个房间或不同楼层之间的联系,简单的传送门机制就不够用了。
- 游戏需要一个更加持续和一致的世界,其中的每个部分都能通过几何方式进行查询,从而保证更复杂的互动和场景切换。例如,玩家可以从一层看到另一层的内容,这种设计无法通过简单的传送门来实现。
-
游戏世界的几何结构:
- 游戏世界的设计应允许玩家在一个连贯的几何世界中自由移动,这样不仅能够显示多个区域,还能处理复杂的互动。例如,假设游戏中有一个角色能破坏墙壁,产生洞口,玩家能看到并进入这些新的空间。为了实现这一点,游戏需要知道这些空间是如何通过地图相连的,而不是依赖于传送门或其他不反映真实地理关系的设计。
-
关于声音传播的例子:
- 声音传播是另一个体现游戏世界几何结构的重要元素。假设玩家站在墙壁旁,能听到另一房间内的声音,尤其是像老板怪物这样的强大存在。如果系统仅仅依赖于传送门,那么就无法处理这种墙壁穿透声音传播的问题。因此,游戏设计必须具备能够处理空间连接的几何方式,以便在复杂的环境中进行正确的声音传播。
-
为什么使用“块”(Chunks)而不是简单的传送门:
- 游戏设计希望能以一种几何化的方式查询和展示世界。使用“块”可以将虚拟世界分成较小的单元,在需要时可以通过几何方法查询这些单元。而不必担心将整个世界存储为一个庞大的数组或数据结构。这样设计的优势是既能保持世界的一致性,也能让世界保持更大的扩展性。
是否计划按需加载/卸载瓦片区块?
这段讨论主要涉及了内存管理、分页策略和游戏世界的加载与卸载。以下是详细总结:
-
加载和卸载世界数据:
- 关于游戏世界的内存管理,提到世界的数据(如瓦片块)可能会保留在内存中,但也有可能根据需求进行加载和卸载。加载和卸载的操作对当前设计而言非常简单,因为系统已经为这一点做好了准备。
- 如果某个瓦片块没有在内存中,可以通过分页将其加载到内存中。这种加载方式非常灵活,且几乎不影响性能。
-
分页方案:
- 分页(paging)指的是按需加载数据块的方式。在当前方案中,数据块的加载与卸载几乎是无成本的,但仍有一些复杂的操作需要避免过于简单地进行分页。
- 如果需要更复杂的分页方案,比如根据需求动态加载或卸载数据(需求分页),这个过程理论上非常简单,只需大约30分钟的时间进行调整。然而,目前尚不确定是否真的需要实施这种复杂的分页策略。
是否计划让瓦片地图分层?
这段讨论涉及到代码的复杂性和未来的计划,特别是在生成游戏世界时的临时解决方案。以下是详细总结:
-
代码的复杂性:
- 游戏的最终程序代码看起来非常复杂,尤其是语言层面的改动非常难以实施。这可能指的是代码的现有结构或设计,使得任何语言上的修改都需要较大的工作量。
-
临时的农村生成代码:
- 在开始进行世界生成(尤其是乡村区域)之前,现有的代码只是作为一个临时解决方案存在。当前的目的是为测试城市地图(town map)提供一个简单的基础,确保能够在更广泛的世界范围内进行测试。
- 目前的代码并没有做任何实际的删除或优化,只是用来进行初步的测试。这段代码并不代表最终的设计方案,而只是暂时的过渡阶段。
-
未来的计划:
- 这段代码被称为“垃圾”,意味着它在实际项目中不会被使用。它只是一个过渡性工具,用于测试和验证某些功能,比如城市地图的表现。
基本稀疏瓦片地图存储是否需要压缩?
-
图形、物理和逻辑的分层:
- 对于游戏的图形、物理和逻辑系统的分层,虽然可能存在不同的系统设计,但这些系统可能并不会完全分开,而是有一定程度的合并。例如,图形和物理系统虽然是不同的部分,但它们之间不一定会有完全分离,可能会在实现时合并在一起。这样做是为了灵活性和便于修改。
-
资源切换和类型定义:
- 在类型定义方面,倾向于使用较为简化的方法。例如,不使用特定的布尔类型,而是采用整数类型来表示数据池,这样可以避免频繁的转换,减少性能开销。这种方式源自过去的习惯,即避免过多定义和视觉化的类型,从而简化了代码的书写和理解。
-
声音和平台的隔离:
- 在声音管理方面,游戏的声音部分需要独立管理,以便在开始播放更多的声音时能有效地处理这些数据。同时,也有一定的隔离意识,避免过多依赖平台特定的实现,保持一定的灵活性,以便更好地控制声音资源。
-
资源压缩和内存管理:
- 对于资源大小的担忧,尤其是关于地图和艺术资产的大小,虽然考虑过压缩策略,但由于游戏资产(如大规模的地图和图像资源)可能非常庞大,压缩可能不会带来显著的效益。尤其是如果瓷砖地图本身造成了内存压力,压缩才可能成为一个值得考虑的方案。总体来说,艺术资产的大小可能会限制压缩的有效性,因此压缩方案不一定是优先选项。
-
内存压力和性能:
- 对于游戏世界的大小,预计不会对内存产生巨大的压力,但如果瓷砖地图的内存需求超出了预期,那么可以考虑使用一些技术来优化内存使用,例如压缩。这些决策将依据实际的资源需求和内存使用情况来调整。
方法。
- 对于游戏世界的大小,预计不会对内存产生巨大的压力,但如果瓷砖地图的内存需求超出了预期,那么可以考虑使用一些技术来优化内存使用,例如压缩。这些决策将依据实际的资源需求和内存使用情况来调整。
房间是否应该仅在玩家接近时才创建?
是否担心内存泄漏?
三目运算符不好吗?
为什么无法保存完整地图?
主要观点:
-
稀疏存储的挑战:
- 稀疏存储(Sparse Storage)是指只在需要时才分配内存,避免预先分配过多空间。尽管这种方法可以节省内存,但其代价是需要额外的计算和内存管理开销。随着资源减少,稀疏存储的密集度会增加,这意味着管理变得更复杂,效率下降。
-
计算复杂度与存储的权衡:
- 在大世界设计中,如果希望通过稀疏存储减少内存占用,那么在存储管理方面需要付出更高的代价。直接存储和访问稀疏世界中的数据可能会导致性能问题,尤其是在动态改变世界(如可破坏环境)时,存储需求会激增。
-
优化的重点是最坏情况:
- 游戏开发中,通常优化的焦点应该是“最坏情况”,即当玩家处于复杂的环境中,屏幕上显示的内容是最为密集和复杂的,而不仅仅是优化那些玩家几乎不接触的区域。例如,尽管优化那些不需要渲染的部分(如被墙壁遮挡的区域)看起来有意义,但如果忽视了最坏情况的优化(如大规模渲染的复杂环境),反而可能导致游戏性能问题。
-
内存泄漏与资源管理:
- 在内存管理上,有些时候不需要过于担心内存泄漏的问题,尤其是当所有的数据都存储在永久存储中,而不常驻内存时。此时内存泄漏的风险较低,但如果在游戏中涉及到动态修改(如破坏环境),那么数据存储和内存管理的策略就需要更加细致。
-
巨型纹理(Mega Texture):
- 在设计大规模世界时,可能会考虑使用“巨型纹理”来存储完整的世界地图。但这种方法在内存中无法完全保存整个纹理,而是通过分页的方式,将需要的部分动态加载到内存中。巨型纹理的处理方式和存储方法,要求在运行时将纹理分割并根据需要加载部分数据,避免了将整个地图都加载到内存中的问题。
-
存储空间与游戏设计的平衡:
- 在游戏设计中,必须在存储空间、内存管理和计算成本之间找到平衡。例如,使用稀疏存储可以减少内存占用,但也可能导致存取速度的下降,特别是在大规模动态世界中。当房间或环境发生变化时,存储和管理这些变化的数据会变得更加复杂,甚至可能导致性能问题。因此,尽管理论上可以保存整个地图,但由于内存限制和计算成本,实际开发中通常选择动态加载和部分存储的方法。
总结:
这段内容深入探讨了大规模游戏世界中如何处理存储和内存问题。关键在于如何在保持游戏世界动态和可变的同时,优化内存使用和计算复杂度,特别是在需要存储稀疏数据和应对动态变化时。
仓库 : https://gitee.com/mrxiao_com/2d_game
相关文章:
游戏引擎学习第35天
开场介绍 今天的任务是继续改进一个虚拟的瓦片地图系统,使其适合处理更大的世界。我们希望这个系统能管理大范围的游戏世界,其中包含按需存储的小区域。昨天,我们介绍了“内存区域”的概念,用于管理持久性存储。我们计划今天继续…...
learn-(Uni-app)输入框u-search父子组件与input输入框(防抖与搜索触发)
1.父子组件u-search (1)父组件 <!-- 父组件 --> <template> <div><searchBar change"change" search"search"></searchBar> </div> </template> <script> // 子组件搜索 import…...
设置IMX6ULL开发板的网卡IP的两种方法(临时生效和永久有效两种方法)
设置开发板网卡的IP,有两种方法。 方法一:临时生效 第一种方式是临时设置,只有本次有效,重启后又要重新设,命令为: ifconfig eth0 192.168.5.9设置成功后可以使用ifconfig命令来查看已设置的 IP 地址。 …...
流量转发利器之Burpsuite概述(1)
目录 一、Burpsuite Burp Suite Spider 的主要特点: 在 Burp Suite 中使用 Spider: Spider 的用例: 限制: 声明:学习视频来自b站up主 泷羽sec,如涉及侵权马上删除文章 声明:本文主要用作技…...
Transformer入门(6)Transformer编码器的前馈网络、加法和归一化模块
文章目录 7.前馈网络8.加法和归一化组件9.组合所有编码器组件构成完整编码器 7.前馈网络 编码器块中的前馈网络子层如下图所示: 图1.32 – 编码器块 前馈网络由两个带有ReLU激活函数的全连接层组成。全连接层(Fully Connected Layer)有时也…...
element-plus中的resetFields()方法
resetFields()确实是Element Plus中的方法,该方法主要用于重置表单,将其值重置为初始值,并移除校验结果。以下是对该方法的详细解释: 一、resetFields方法的作用 在Vue3结合Element Plus开发时࿰…...
【过滤器】.NET开源 ORM 框架 SqlSugar 系列
目录 0、 过滤器介绍 1、表过滤器 (推荐) 1.1 手动添加过滤器 1.2 禁用、清空、备份和还原 1.3 联表查询设置 1.4 动态添加 2、修改和删除用过滤器 2.1 局部设置 2.2 全局设置 (5.1.4.62) 3、子查询用过滤器 4、联表过滤…...
Jmeter Address already in use: connect 解决
做压测接口时,并发一段时间后,会报java.net.BindException: Address already in use: connect 原因: windows提供给TCP/IP链接的端口为 1024-5000,并且要四分钟来循环回收它们,就导致在短时间内跑大量的请求时将端口占…...
C#常见错误—空对象错误
System.NullReferenceException:未将对象引用设置到对象的实例 在C#编程中,System.NullReferenceException是一个常见的运行时异常,其错误信息“未将对象引用设置到对象的实例”意味着代码试图访问一个未被初始化或已被设置为null的对象的成…...
Leetcode数学部分笔记
Leetcode数学部分笔记 1. 回文数2. 加一3. 阶乘后的零4. x 的平方根5. Pow(x, n) 1. 回文数 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。 回文数 是指正序(从左向右)和倒序&…...
微信小程序web-view 嵌套h5界面 实现文件预览效果
实现方法:(这里我是在小程序里面单独加了一个页面用来下载预览文件) 安装 使用方法请参考文档 npm 安装 npm install weixin-js-sdk import wx from weixin-js-sdk预览 h5界面代码 <u-button click"onclick" type"primary" :loading"…...
【汽车】-- 燃油发动机3缸和4缸
3缸和4缸燃油发动机是小轿车常见的发动机配置。以下从结构特点、性能、经济性等方面对两者进行对比,并分析优缺点及使用注意事项: 1. 结构与运行原理 3缸发动机 特点:少一个气缸,内部零部件更少,整体结构更紧凑。优点…...
轻量级的 HTML 模板引擎
Mustache 简介:Mustache 是一个非常简单的逻辑少的模板引擎,支持 HTML 文件中的占位符替换。它不会执行复杂的逻辑,只支持简单的变量替换。 安装: npm install mustache示例: const Mustache require(mustache);c…...
Mysql | 尚硅谷 | 第02章_MySQL环境搭建
Mysql笔记:第02章_MySQL环境搭建 说明:本内容整理自尚硅谷B站MySQL视频>>尚硅谷B站MySQL视频 文章目录 Mysql笔记:第02章_MySQL环境搭建第02章_MySQL环境搭建 1. MySQL的卸载步骤1:停止MySQL服务步骤2:[软件](h…...
Maven学习(传统Jar包管理、Maven依赖管理(导入坐标)、快速下载指定jar包)
目录 一、传统Jar包管理。 (1)基本介绍。 (2)传统的Jar包导入方法。 1、手动寻找Jar包。并放置到指定目录下。 2、使用IDEA的库管理功能。 3、配置环境变量。 (3)传统的Jar包管理缺点。 二、Maven。 &#…...
CTF: 在本地虚拟机内部署CTF题目docker
step 1 安装基本依赖 sudo apt-get update sudo apt-get install -y \ca-certificates \curl \gnupg \lsb-releasestep 2 安装docker sudo apt-get remove docker docker.io containerd runc sudo apt-get update sudo apt-get install \apt-transport-https \ca-certificate…...
视频推拉流EasyDSS无人机直播技术巡查焚烧、烟火情况
焚烧作为一种常见的废弃物处理方式,往往会对环境造成严重污染。因此,减少焚烧、推广绿色能源和循环经济成为重要措施。通过加强森林防灭火队伍能力建设与长效机制建立,各地努力减少因焚烧引发的森林火灾,保护生态环境。 巡察烟火…...
SpringBoot【十一】mybatis-plus实现多数据源配置,开箱即用!
一、前言🔥 环境说明:Windows10 Idea2021.3.2 Jdk1.8 SpringBoot 2.3.1.RELEASE 正常情况下我们在开发系统的时候都是使用一个数据源,但是由于有些项目同步数据的时候不想造成数据库io消耗压力过大,便会一个项目对应多个数据源…...
【嵌入式linux基础】关于linux文件多次的open
在 Linux 中,设备文件可以被多次打开(open()),但这取决于具体的设备类型和其驱动程序的实现。以下是关于设备文件多次打开的一些关键点: 普通字符设备: 对于大多数字符设备,如串口、TTY 设备等&…...
TPAMI 2023:When Object Detection Meets Knowledge Distillation: A Survey
摘要 目标检测(Object Detection,OD)是计算机视觉中的一项关键任务,多年来涌现出了众多算法和模型。尽管当前 OD 模型的性能有所提升,但它们也变得更加复杂,由于参数规模庞大,在工业应用中并不…...
2024前端面试题(持续更新)
目录 一、js的数据类型有哪些? 二、什么是symbol? 三、什么是浅拷贝什么是深拷贝? 四、vue2的生命周期? 五、vue2中父子组件的生命周期调用顺序 六、vue3的生命周期 七、vue3对比vue2的变化 八、组合式API中的ref和reactiv…...
apache转nginx访问变成下载解决方法
在配置文件 nginx.conf中存在 第一行: include mine.types 对应了文件的mime类型。 第二行: 默认的是octet-stream, 意思是如果一个文件的mime类型不存在就会使用默认的类型。 通常是这个导致了文件的下载。 第一种方案:(推荐&a…...
【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(三)
目录 ARC规则 概要 所有权修饰符 __strong修饰符 __weak修饰符 __unsafe_unretained修饰符 __autoreleasing修饰符 ARC规则 概要 “引用计数式内存管理”的本质部分在ARC中并没有改变,ARC只是自动地帮助我们处理“引用计数”的相关部分。 在编译单位上可以…...
Oracle数据库使用dblink是时出现 ORA-12170:TNS:连接超时
原因: 我遇到这种情况是因为dblink那端的数据库被我重新导了一下dmp,然后本地这边查询就报错了。 解决办法: 把已有的dblink删掉或者说是换个名字,然后按照原来的再新建一个同名的dblink就解决了。...
OpenHarmony系统中实现Android虚拟化、模拟器相关的功能,包括桌面显示,详细解决方案
在 OpenHarmony 系统中实现 Android 虚拟化 和 模拟器功能(面显包括桌示)是一个复杂的任务,涉及多个关键技术栈的集成和深度定制。我们可以通过多种方式来实现 Android 系统的虚拟化和模拟器功能,类似于在普通操作系统中运行虚拟机…...
决策曲线分析(DCA)中平均净阈值用于评价模型算法(R自定义函数)
决策曲线分析(DCA)中平均净阈值用于评价模型算法 DCA分析虽然不强调用来评价模型算法或者变量组合的优劣,但是实际应用过程中感觉DCA曲线的走势和模型的效能具有良好的一致性,其实这种一致性也可以找到内在的联系,比如…...
《经验分享 · 软考系统分析师》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...
记录一下 js encodeURI和encodeURIComponent URL转码问题
escape:由于它已经被废弃,不建议在任何新的代码中使用。encodeURI:当你需要对整个URI进行编码时使用,例如在将整个URL作为参数传递时。encodeURIComponent:当你需要编码URI中的某一部分,尤其是查询字符串参…...
【C语言】二维前缀和/求子矩阵之和
相信你是最棒哒!!! 目录 一、题目描述 正确代码 二、题目描述 题目代码 总结 一、题目描述 输入一个 𝑛 行 𝑚 列的整数矩阵,再输入 𝑞个询问,每个询问包含四个整数 𝑥1…...
SRS 服务器入门:实时流媒体传输的理想选择
在当今视频流媒体需求爆炸式增长的时代,如何选择一款高效、稳定且功能强大的流媒体服务器成为了许多开发者和企业关注的焦点。而 SRS(Simple Realtime Server)作为一款开源的流媒体服务器,以其卓越的性能和灵活的功能,…...
wordpress 登录没反应/宁波seo专员
单链表(single-linked list)链表结构应用实例分析数据结构算法类方法对象代码实现插入向尾部直接插入节点思路分析算法实现按照顺序插入指定位置思路分析算法实现修改思路分析代码实现删除思路分析代码实现查找思路分析代码实现面试题有效元素的个数代码…...
小说网站静态页面模板/长沙专业做网站公司
最近研究设置数据,稍微总结一下,以后继续补充: 在一个18摸AIX上跑的一台11g的库,检查的时候发明视图V$UNDOSTAT的数据很诡异,第1条的记载时光段竟然一直没有被切割过,开始时光是2011-12-26 09:52:08结束时光…...
html怎么做音乐网站/百度文库网页版
Python中几种常用包比较2、用xlrd包读取Excel文件 引用包 import xlrd 打开文件 xlrd.open_workbook(r/root/excel/chat.xls) 获取你要打开的sheet文件 # 获取所有sheet sheet_name workbook.sheet_names()[0] # 根据sheet索引或者名称获取sheet内容 sheet workbook.sheet_by…...
网站维护专业/网店推广费用多少钱
import Vue from vue import App from ./App import router from ./router import axios from axios Vue.config.productionTip false Vue.prototype.$http axios;...
各大浏览器的网址/佳木斯seo
击上方“C语言与CPP编程”,选择“关注/置顶/星标公众号”干货福利,第一时间送达!在防御性编程的大框架之下,有许多常识性的规则。人们在想到防御性编程的时候,通常都会想到“断言”,这没有错。我们将在后面…...
苏州网站建设营销/郑州网络seo
前言 netty框架马上就进入尾声了,小编没有特别深入的讲解,第一是网络编程确实挺难的,第二用好netty其实是挺不容易的一件事情,尤其都是异步的情况下,今天小编继续为大家带来开发实战,上次分享了redis客户端…...