问题

游戏《戴森球计划》这么火,它的一些细节在技术上是怎么实现的?

回答
《戴森球计划》之所以如此火爆,并不仅仅是因为它“堆料”式的建造和宏大的目标,更在于其背后许多精心设计的技术细节,这些细节共同营造了令人沉浸的游戏体验。下面我们就来详细剖析一下它在技术上是如何实现的,并且尽量深入细节:

1. 动态生成的庞大宇宙与多线程处理

核心问题: 如何在一个可观测宇宙尺度上,动态生成并流畅渲染成千上万个恒星系、行星、卫星、小行星带以及星际物质?并且在玩家的探索过程中,这些物体及其属性能够保持一致性和逻辑性?

技术实现:

程序化生成 (Procedural Generation):
恒星系生成: 游戏使用一套复杂的算法来生成恒星系。这包括:
恒星类型与属性: 根据预设的概率分布和一些基本规则(如质量与温度的关系),生成不同类型(G型主序星、K型、M型等)和大小的恒星。恒星的光照颜色、强度、大小等都与其实际物理属性挂钩。
行星轨道计算: 遵循开普勒行星运动定律来计算行星的轨道参数(半长轴、偏心率、倾角、升交点经度、近日点幅角等)。这些参数决定了行星绕恒星运转的轨迹和速度。
行星类型与属性: 根据距离恒星的远近、恒星的类型等因素,生成不同类型的行星:岩石行星、气体巨星、冰巨星。它们的表面特征(地貌、植被、液态水)、大气成分、重力、自转速度、磁场等也通过算法随机生成或根据物理规则进行关联。
卫星生成: 气体巨星会生成卫星,它们的轨道和大小也经过算法生成。
小行星带与星际尘埃: 在合适的轨道区域生成小行星带,以及散布的星际尘埃云,增加宇宙的视觉丰富度。
“种子”机制 (Seed): 整个宇宙的生成是基于一个随机种子。这意味着,如果你使用相同的种子,你将看到完全相同的宇宙布局,这为玩家之间的分享和复制提供了可能,也方便了开发者进行测试和复现bug。
多线程与异步加载 (Multithreading & Asynchronous Loading):
核心处理线程: 地球、火星等玩家活动的初始星系会被高精度地加载和模拟,这部分是主线程或专门的物理模拟线程。
后台生成线程: 游戏会启动多个后台线程,在玩家探索的区域之外,异步地生成和加载新的恒星系和行星数据。当玩家的飞船接近一个未探索的区域时,其数据会从后台加载并渲染。
LOD (Level of Detail) 系统: 对于远处的恒星系和行星,只会加载低细节的模型和基本属性。当玩家接近时,才会加载更精细的模型、纹理和模拟数据。这极大程度上减轻了CPU和GPU的压力。
数据压缩与流式加载: 未被玩家访问的星系和行星数据可能采用压缩格式存储,只在需要时解压并加载到内存。

2. 宏大尺度下的精确物理模拟

核心问题: 如何在恒星系和星际旅行的宏大尺度下,准确模拟行星的运动、采矿飞船的轨迹、物流网络的运行,并且不出现穿模或物理矛盾?

技术实现:

坐标系与精度:
浮点数精度: 在如此巨大的尺度下,标准的单精度浮点数(float)在处理远距离坐标时会出现精度丢失问题(例如,一个在非常远行星上的物体,即使移动很小的距离,其相对位置的计算也可能因为浮点数的误差而变得不稳定)。
双精度浮点数 (double) 或浮点数偏移技术: 《戴森球计划》很有可能采用了双精度浮点数来处理远距离坐标的存储和计算,以保持高精度。或者,它可能使用了一种“浮点数偏移”的技术,即将玩家(或以玩家为中心的局部区域)的坐标作为原点,其他物体的坐标相对于这个局部原点进行计算。当玩家移动到新的区域时,系统会重新计算所有物体的相对位置,确保在局部范围内具有高精度。
开普勒轨道模拟:
解析解与数值积分: 行星围绕恒星的轨道,其基础是开普勒定律。游戏在生成时会计算这些轨道参数。在运行时,可以通过解析解(如果恒星系是简化的双体或多体系统)或高精度的数值积分方法(如RungeKutta方法)来实时模拟行星的运动。
多体引力(有限影响): 虽然主要模拟的是行星围绕恒星的运动,但其他大质量天体(如气体巨星的引力)对行星轨道的轻微扰动可能也会被考虑在内,尤其是在行星轨道生成时。然而,为了性能,游戏可能不会实时模拟所有天体之间的精确多体引力作用,而是主要依赖于预设的轨道参数,并在近距离互动时进行更精细的碰撞检测。
飞船与物流模拟:
航线规划与路径查找: 运输船会根据玩家设定的物流网络,在各行星之间规划最优航线。这可能涉及到路径查找算法(如Dijkstra或A算法)来寻找连接两个采矿站或加工厂的最短或最有效率的路线。
碰撞检测与避障: 虽然在宏观尺度上飞船之间的碰撞不常见,但在密集区域(如围绕行星的轨道或行星表面),游戏需要进行碰撞检测。简单的包围盒或球形碰撞检测可以用来检测飞船与行星、小行星、空间站、其他飞船的碰撞。为了避免卡死,可能有简化的避障逻辑。
轨道力学模拟(简化): 飞船在星际旅行中,虽然不是完全模拟真实的牛顿万有引力,但可能包含一些简化的轨道力学效果。例如,飞船在加速和减速时会受到推力,并且在长距离飞行时,其轨迹会受到恒星和其他天体的引力(可能是一个简化的引力模型或预计算的轨道)影响,使得旅行感觉更真实。
粒子系统与视觉效果:
恒星光辉: 恒星的体积光、辉光效果,以及围绕恒星的光晕,都是通过粒子系统和后处理效果实现的。
小行星与尘埃: 小行星带和星际尘埃通过大量的粒子渲染,以及基于摄像机距离的LOD和剔除技术来表现。
能量传输与激光: 戴森球和各种能量传输的视觉效果,如激光束的渲染,使用了体积光、光束着色器和粒子系统。

3. 复杂的工厂自动化与物流系统

核心问题: 如何在游戏内处理数以万计的机器人、运输带、自动化工厂单元,并确保它们的运行效率、数据流和物理交互是流畅的?

技术实现:

自动化生产链管理:
状态机与脚本: 每个生产建筑(如采矿机、熔炼炉、组装机)都有一个内部的状态机,管理其生产、待机、故障等状态。它们通过预设的脚本或内部逻辑来执行生产过程。
需求与供给逻辑: 物流系统通过识别每个节点的生产(产出)和消耗(需求)来自动分配运输工具(物流机器人)。当一个节点有物品产出,并且有下游节点需要该物品时,物流系统就会尝试连接。
物流机器人行为模拟:
路径查找与寻路: 物流机器人需要高效地在复杂的三维环境中找到前往目的地(例如,从矿机到仓库,或从仓库到组装机)的最短或最快路径。这可能涉及到对星球表面、轨道空间以及工厂内部的网格化寻路,或者使用更优化的图搜索算法。
动态路径规划: 当环境发生变化(例如,出现新的障碍物或最优路径被堵塞时),机器人需要能够重新规划路径。
负载管理: 机器人需要有效地管理其载货能力,避免空载或满载但无法卸货的情况。
调度与并发: 大量的机器人同时运行,需要一个高效的调度系统来分配任务,避免所有机器人同时尝试访问同一个资源点或充电站。
AI 与优化:
机器人行为AI: 机器人并非简单的“点到点移动”,它们需要有一定程度的AI来执行任务,例如寻找可用的起飞点、充电站、卸货点。
性能优化: 为了处理如此庞大的实体数量,游戏可能采用了多种优化手段:
实体剔除 (Culling): 只渲染和模拟视线内或附近区域的实体。
批处理 (Batching): 将相似的渲染请求合并,减少Draw Call。
实例化 (Instancing): 对于大量相同的模型(如运输带、建筑模块),使用实例化技术一次性绘制多个实例,大大提高渲染效率。
ECS (Entity Component System) 模式(推测): 许多现代高性能游戏会采用ECS架构,将数据(Component)与逻辑(System)分离。这种架构非常适合处理大量具有相似行为但数据不同的实体,可以极大地提高数据处理的效率和并行性。例如,所有运输带可以是一个System,它们共享渲染和物理更新逻辑,但每个运输带的连接点和传输内容是其Component数据。
数据同步与一致性:
在多人模式下,所有玩家工厂和物流系统的状态需要保持同步。这涉及到高效的网络同步协议和状态压缩,确保玩家之间的数据一致性。

4. 戴森球的宏伟工程可视化

核心问题: 如何实时可视化一个由无数太阳帆组成的、环绕恒星的巨大结构?如何模拟其能量收集和传输过程?

技术实现:

模型复杂度与LOD: 戴森球的真实模型会非常庞大且复杂。游戏通常会采用LOD技术来处理。
远距离: 只显示一个简化的球体或光环状的视觉效果,甚至只是一个恒星周围的能量收集指示器。
近距离/建造中: 显示单个或一定数量的太阳帆模块,并允许玩家对其进行精细操作。
动态 LOD: 根据玩家的视角和距离,动态地加载和卸载不同细节级别的戴森球模型。
粒子与体积渲染: 太阳帆的能量收集和传输过程可能通过大量的粒子效果和体积光来可视化,模拟能量的汇聚和流动。
物理模拟(简化): 虽然戴森球在真实物理学中可能面临稳定性问题,但在游戏里,它被视为一个稳定的结构。其能量收集效率可能与环绕的恒星能量输出、帆的覆盖率、行星轨道等因素关联,但具体的物理交互模拟会被简化。
数据管理: 戴森球的各个组件(太阳帆、能量收集器)需要被有效地管理和组织,以便于查询其状态、计算总能量输出等。这可能使用某种数据结构来存储球体的整体结构和每个单元的状态。

5. 用户界面 (UI) 与信息可视化

核心问题: 如何在如此庞大和复杂的信息系统中,为玩家提供清晰、直观且易于操作的UI,让他们能够理解和管理整个帝国?

技术实现:

高效的UI渲染框架: 游戏可能使用了优化的UI渲染框架,能够高效地绘制和更新大量UI元素,包括各种列表、图表、按钮和信息面板。
数据驱动的UI: UI元素的数据通常是与游戏内部的数据模型直接绑定的。当游戏数据改变时,UI会实时更新。
信息层级与筛选: 为了应对庞大的信息量,UI设计非常关键。游戏采用了多层级的菜单、过滤和搜索功能,让玩家可以快速找到所需的信息,例如筛选特定物品的运输、查找特定建筑的生产状态。
动态图表与数据可视化:
生产/消耗图: 展示各种物品的生产和消耗速率,帮助玩家平衡生产线。
能源图: 显示能源的产生、消耗和盈余情况。
物流图: 可视化物流网络的连接和流量,方便玩家识别瓶颈。
交互设计: 鼠标拖拽、快捷键、右键菜单等多种交互方式结合,提升操作效率。
可配置性: 允许玩家自定义快捷键、调整UI大小和布局,以适应不同玩家的习惯。

总结

《戴森球计划》的成功是多方面因素共同作用的结果,而其背后强大的技术支撑是不可或缺的。它巧妙地结合了:

程序化生成来创造广阔的宇宙。
多线程和LOD来保证宏大尺度下的性能。
精确(但有时简化的)物理模拟来支撑行星运动和物流运输。
高效的AI和数据管理来驱动成千上万的自动化单位。
先进的渲染技术来呈现壮丽的宇宙景象和复杂的工程奇迹。
精心设计的UI来驾驭海量的信息。

正是这些细节上的打磨,才使得《戴森球计划》能够提供一个既有深度又有广度的游戏体验,让玩家在建造和探索中获得持续的满足感。

网友意见

user avatar

谢邀,就引用一篇之前我们家制作人大大的内容吧~

目前这个存档的游戏时间是118小时。

游戏玩到后期卡吗?这也许是大家最关心的问题之一。

数千艘运输机在忙碌,数千座设施在运作,数万个太阳帆在环绕,数十万货物在运送,斗转星移,地面上所有太阳能板都面朝着太阳… 这计算量可不是闹着玩的!而以上还只是这一个星球。

本篇我将主要介绍为了保证游戏的流畅性,我们是如何实现游戏性能优化的。由于该话题涉及的内容与技术细节实在太多,所以我打算将优化系列分为三篇开发日志,粗略的谈一下我们所用的方法与技术。

游戏的帧率机制

游戏中的帧率分为渲染帧物理帧

渲染帧主要负责渲染游戏画面;

物理帧主要负责运行游戏逻辑。

我们在游戏中制作了帧率计数器以监控实时性能,其中左边的数字是渲染帧,右边的数字是物理帧。也可以在设置面板中设定渲染帧的帧速率。当开启垂直同步时,渲染帧速率会和显示器刷新率一致。

帧速率越高,GPU的使用率也就越高,当使用率接近100%时,就会自动降低画面帧率。

在游戏的生产系统中,我们需要一套能精确量化,服从“决定论”的逻辑,即同样的操作只能导致同样的结果。要抛开帧率带来的影响,首先是游戏中所有的时间必须按物理帧计数,例如制造时间为3秒的“电弧熔炉”,其实在内部逻辑里就是180个物理帧。再例如360kW的工作功率,在内部逻辑里其实是每个物理帧消耗6kJ的能量,颇有一种“量子化”的感觉。

根据以上的逻辑,负责游戏逻辑的物理帧必须相对稳定,不能被渲染帧率所影响,否则玩起来就会感觉时间一会快,一会慢,所以我们将物理帧率锁定为60帧,只有当渲染帧率太低时,才会折减物理帧率,以免塞帧。

使用DOP来代替OOP

DOP和OOP分别指“面向数据编程”和“面向对象编程”。具体的比较可以写好长一篇文章,而且前人已充分讨论,所以这里就不赘述了。简单来讲,在面对游戏中大量物件的情况下,面向对象会造成许多不必要的开销,造成性能低下,而面向数据编程则将对象中的属性逐一拆分出来,形成紧密排列的数组,使得相关逻辑能够更快速的进行遍历,甚至一些数据还能直接传给GPU来处理。

那么为什么大家不都用DOP来代替OOP呢?这是因为“面向对象”更符合人们的认知和代码书写习惯,也便于项目管理,而“面向数据”的代码写起来有一些反人类,有时甚至相似的结构写好几遍,不方便管理,但实际运行效率却极高。所以为了游戏的优化,我们还是选择了“面向数据”来作为该项目的编程核心思想与框架。

读起来太晦涩了吗?那实在不行的话我们就这样吧!

不开玩笑了,回到正题

物理帧的大部分工作是由CPU来完成的,要维持60的帧率,一个物理帧允许的CPU处理时间不能超过16ms(毫秒),除开提交DrawCall渲染的时间和其他必须的开销,只有大约11ms可用于一帧的游戏核心逻辑。

单核游戏?多核游戏?都不是,这是一个GPU游戏!

目前CPU的性能发展遇到了瓶颈,已经没办法再大规模提升单核性能,只能靠提升核心数量。而在很多实际应用的情况下,对核心数量的提升,远不如增加一点点单核频率提升的性能多。

虽然我们在星系的随机生成及模拟、星球地形动态生成、无缝加载等逻辑中使用了多线程来缓解主线程的压力,但是对于游戏逻辑中如此庞大的计算量,就算有100个核火力全开多线程完美配合也未必驾驭得了!

从《戴森球计划》的想法诞生之初,就决定了这是一个GPU游戏。看那数万颗太阳帆,每一颗的运动都遵循着万有引力定律,每一颗都在计算发电量,每一颗都能被近距离观看。像大规模并行计算这样的工作,使用GPU是不二的选择。

我们将凡是可以并行计算的那部分计算工作,全部交给了GPU,剩下的那部分计算量,只要GPU能在16ms之内按时渲染出来,CPU就绝不会掉链子!

这同时也意味着,好的GPU的确能为《戴森球计划》带来更流畅的体验,而CPU的影响则相对较小。

使用GPU来渲染大批量动画

在现今主流游戏引擎中,动画大多是靠移动部件或骨骼的位置(Position)、旋转(Rotation)、缩放(Scale)来完成的,如下图:

就是这样一个建筑,它的动画部件已超过50个,其中还包括父子Transform层级的嵌套,假设我们有1000个这样的建筑,那CPU需要同时处理的动画子部件就有超过50000个,这显然是CPU驾驭不了的,而这还仅仅只是建筑动画,并不是核心逻辑。

用CPU去逐一计算这50000个部件的Transform的确是相当不划算的,这些动画其实可以并行计算,只要在画面帧结束时,所有建筑的动画均就绪即可,这对于具有强大并行计算能力的GPU来说简直就是小菜一碟。

我们在编辑器中将这个建筑的所有动画帧中所有的顶点位置、法线等信息按照一定的顺序,事先录制在一个VERTA文件中。

当游戏加载时,只需要载入这些文件,通过ComputeBuffer将所有信息传递给GPU,这样GPU就拿到了所有建筑动画的预烘焙信息,接下来再将每个建筑的动画状态组成一个数组,传给GPU。

最后,还需要在vertex shader中分析这些数据,逐一还原每个建筑在当前帧的建模。

按照这个思路,我们成功使用GPU Instancing还原了所有建筑的动画,还顺便使用了建筑状态数据来控制建筑贴图上指示灯的开关。在动画和建筑状态更新方面,CPU被完全解放。而在GPU中,仅仅只是多了150MB左右的显存数据用于存储所有建筑的动画数据(仅相当于几张4096贴图)与若干在vertex shader中的寻址与插值计算。可以说是一顿免费的午餐了!

粒子特效

为了将特效渲染纳入上面的框架中,所有的建筑特效均无法使用引擎自带的粒子特效,必须将原本是作为粒子来渲染的特效包含在模型网格中,再使用shader来逐一实现各个建筑不同的特效需求。

在游戏中,几乎每一个不同的建筑都定制了不同的shader,对于同一个建筑特效的不同片元,我们按照类别涂上不同的顶点色,哪些是辉光,哪些是要拉长的,哪些应该从下至上亮度递增,通过不同的片元顶点色,就能做不同的处理。

需要时刻朝向太阳方向的建筑,逻辑不能让CPU来处理。我们将建筑模型按照“底座”、“横向转动部分”、“俯仰转动部分”涂上不同的顶点色加以区分,再在shader里面计算这些顶点应该如何旋转,从而朝向目标点。

有了这套机制,我们就只需要在主线程中计算好每个建筑当前的状态,形成ComputeBuffer,传递给GPU来进行统一渲染,在这个过程中,动画、IK朝向、特效、自发光变化等等,就统统都有了。

Unity Profiler性能测试

说了这么多,直接上目前的性能测试图吧!

下面是100小时规模的存档在Unity Profiler里面的性能测试图

从图中可以看到,游戏主逻辑物理帧CPU耗时5.11ms,也就是说理论上每秒可以跑到195个物理帧,所以CPU这边肯定是没问题,而最终画面能跑多少帧,就看GPU的能力了,我的卡是660Ti,在畅玩了100小时后,还能跑上40帧,不过我们还将继续不懈的优化,争取将我这张卡的帧率提到60!

除了物理帧的优化以外,我们还非常看重C#的垃圾回收机制的优化,因为过多的内存垃圾会导致游戏时常卡顿,严重影响游戏体验。

一般来讲,每次触发垃圾回收机制都会出现不同程度的卡顿,卡顿出现的频率取决于GC Alloc的大小,而每次卡顿的时间取决于数据结构的复杂度。

为了尽可能消除游戏卡顿现象,从立项开始,程序在数据结构上就严格把控,能用数组的地方用数组,尽量少的使用Dictionary或List,凡是物理帧的逻辑除数组扩容等操作以外,均不能产生GC Alloc,在UI逻辑中严格控制字符串的操作,避免不必要的开销。

据测试,目前游戏的GC.Collect卡顿时间已控制在30ms以内,出现频率为几乎没有。

下图为100小时规模下的GC性能统计:

游戏主逻辑在物理帧上的GC Alloc为0,只有UI上有132字节的开销,加上一些引擎必要的开销,每帧总的GC Alloc为5.0KB,目前这个数值处于非常低的水平。

以后如果有时间的话,我们会介绍物流运输机、戴森云的优化,看看GPU是如何轻松达到“数十万”这个数量级!

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有