游戏引擎架构
1.游戏引擎
就如汉字最开始是由图案构成的一样,游戏引擎一开始也只是一个单独的游戏。当人们不断的制作新的游戏的时候,发现很多功能是通用的,没有必要做重复的内容。所以,人们开始对游戏里面的功能进行抽象总结,这样在开发新的游戏的时候可以直接使用之前的成果,即节省时间,又可以避免很多前人遇到的坑。
这些由之前开发游戏的经验与成果所抽象总结的一个系统就是游戏引擎。一个成熟的游戏引擎应该尽可能的通用于各种游戏制作,提供一套完善的制作流程。各个游戏大厂一般都有自己的游戏引擎,其他小型公司一般使用的是商业游戏引擎,如Unity,UE4。
2.架构
架构是一个系统的草图,描述了构成系统的抽象组件以及他们之间的关系。任何一个稍微复杂一些的软件系统都需要一个合适的架构,对于如此复杂的游戏引擎,当然更为需要。
3.第三方软件开发包
也就是常说的第三方库,包括静态库与动态库。别人写好的封装好的代码库,引入之后直接调用。
4.平台独立层
这里面的内容并不是说与平台完全无关,而是说其基本的逻辑在各个平台大致是相同的,而且几乎都是必须要用到的。其中的差异通过适配器来适配到各种平台。
5.原子类型与原子操作
这个偏向于操作系统层面,所谓原子就是不可拆分的。在操作系统底层,一般只有bool,int等类型是可以被各个系统识别的,我们在高级语言所写的类在系统底层都会被转换为原子数据来操作。
6.定时器
游戏中定时器是一个非常重要的内容,很多游戏逻辑都需要,比如每隔2秒恢复10点生命值。为了提高游戏各项精度,很有必要提供一个高精度的定时器。
7.Graphics Wrappers 图形包裹类
图形抽象层,动态切换DX/OpenGL
8.Moudule模块
游戏引擎相当复杂,我们需要将其拆分成各个模块来便于我们理解与使用。如渲染模块,物理模块,音效模块,动画模块等,不同的引擎在模块的设计上不同。
9.字符串
我们看到在核心系统架构内,字符串被单独拿出来,证明其独特性。字符串的复杂性主要在于编码,同样的内容不同的编码其二进制数据是不同的,我们在进行本地化(国际化)的时候不得不面对这个问题。另外,hash string表示将一个字符串转换成一个数值,在服务器验证的时候经常用到。
10.调试
一般分为断点调试与日志输出调试,好的调试方法可以快速的定位问题。下一节还会更详细的描述。
11.语法分析器
其实可以理解为XML等文件解析器,方便我们使用XML等描述游戏对象,做相关配置
12.性能分析
一般的引擎都会提供相关的性能分析工具,比如unreal内置的Profiler。当然,我们还可以选择一些其他的工具,比如Intel的vtune。
13.引擎配置
游戏引擎里面有太多的参数需要我们配置了,如何方便的修改配置并应用?常见的手段就是ini文件,通过简单的修改配置文件里面的内容,可以在引擎启动时就调整某些内置选项,比如光照精度,游戏控制等等。很多游戏项目里面也经常会用到配置文件。
14.曲线
学过图形学的朋友应该比较熟悉,常见的曲线有B样条与贝塞尔曲线。我们游戏中摄像机移动,各种渲染插值都会用到曲线。
15.RTTI、反射、序列化
RTTI是运行时类型识别,C 基本概念。简单来说就是在运行的时候可以判断一个对象的类型。
反射是在RTTI的基础上提供有关类的更详细的信息。
序列化就是将类对象保存到磁盘并能在之后正确的加载还原出来。
16.唯一标识符
这个在网络里面经常用到,比如给一个网络玩家添加一个唯一标识,给一个同步对象添加唯一标识,一般叫做GUID。
17.游戏资产
简单理解就是各种文件了,动画资源文件,图片文件,字体文件,地图文件等等。这么多种类型的文件当然需要一个统一的管理与解析,资源管理也是非常复杂的一个模块,有点像一个小的操作系统了。
18.摄像机
摄像机的位置决定了渲染的内容
29.物理
核心内容:动力学与碰撞
其他内容:射线检测,一般在带有物理的引擎中,所有的射线检测都是基于物理的。射线检测非常好用,一般我们的无实弹枪的命中,障碍检测都需要射线检测。
20.BSP树
BSP树与八叉树各有什么优劣,分别有什么应用场合。
21.关于剔除
渲染我们值只渲染玩家可见的部分,其他的剔除。有些数量庞大的对象我们只加载玩家附近的,其他的剔除。
22.光照贴图
动态光照对cpu以及gpu的消耗是很大的,为了减小开销。我们可以将静态的光照贴图贴在对象上从而代替动态关照。
23.贴花Decal
就是将一个面片贴到指定的模型上来表现新的图案效果,比如子弹打在地上的弹孔。贴花是在触发时临时创建的带图案的模型,并不是直接在原来的模型上绘制。
24.环境映射EnvironmentMapping
这里翻译成环境映射比较好,环境映射是一种用来模拟光滑表面对周围环境的反射的技术。而环境贴图一般指CUBEMAP,属于环境映射的基本实现方式。
25.PRT
PRT是指Precomputed Radiance Transfer,光辐射传输预计算。把辐射率的传输(包括阴影和相互反射的关系)预生成球谐函数(spherical harmonics, SH)的系数存储在几张纹理中(SH lightmap).然后,存储静态 light probe 的 SH,或动态地把光源生成 SH,点积后就可以得到含有环境光照、阴影和相互反射的全局光照效果。简单来说就是实现全局光照。
26.层级式物体依附
这个翻译到汉语有点绕口。其实本意就是骨骼关联,我们可以将任意一个对象Attach到任何一个骨骼上面。骨骼是层级关系,被attach的对象就会跟着这个骨骼保持一致,不会受这个骨骼的子骨骼影响。
30.MatchMaking
这一块涉及到网络游戏的游戏模式,涉及到游戏大厅内直接玩,还是单独开房间,如何匹配等问题。一般引擎都会提供相关的接口来对接,不过很多细节还需要游戏项目自己去写。
27.音频
一般分为音效和音乐,音效是简短的几秒的声音,音乐一般是长时间播放的背景音乐。引擎应该提供渐变,混合等效果,3D游戏引擎还需要提供三维音频模型。
28.前端FrontEnd
前端这一块主要指UI,包括HUD与内置GUI。
IGC表示利用游戏内容实时录制动画,很多过场动画都是这么做的,比CG要节省太多成本。
29.静态与动态元素
一般在游戏世界,那些在整个游戏过程中位置不会移动的就是静态元素,如建筑,地形,草木。可以与玩家产生位移交互的一般就是动态元素,如NPC,动物等。当然,这不是绝对的,任何东西理论上你都可以把他做成是动态的,不过很明显二者在实现上有很大的差异。
30.世界载入
所有在游戏场景的物体都需要从硬盘载入到内存中运行,这个过程就是World Loading。涉及到场景加载还有很多技术,比如流式加载,可以用于大世界场景的动态加载。
31.脚本
脚本方便程序修改,可以像资源一样热更新到程序里面。
32.游戏专用子系统
这里就是指我们常见的各种游戏逻辑模块,如道具模块,武器系统,成就系统等。当然,游戏引擎没有必要去实现这些内容,因为他们属于游戏项目逻辑。不过,鉴于大部分引擎都是基于游戏项目演变的,所以不同的引擎会构建不同的游戏GamPlay框架,如虚幻4的游戏框架就是基于FPS的,非常适合FPS游戏项目的开发。
33.游戏摄像机
前面提到的摄像机是从渲染底层来看的,他决定了渲染的内容。不过在游戏逻辑这一块,我们主要在乎的是他的位置。是一直跟随着玩家,还是可以通过上帝视角查看,这都是游戏逻辑需要控制的。
34.人工智能
理论上如当前火爆的AI技术一样,你可以用深度学习,机器学习去操作影响AI角色的表现。但实际上,大部分游戏里面的AI角色都是比较“低能”的,只要会简单的寻路,简单的感知即可。原因很简单,我花费最简单的逻辑就能让玩家体验到逼真的AI了,何必搞得那么复杂。
当然,这并不是不需要去深入探索游戏AI,目前还是有很多游戏领域的人在这个方面探索的。
35.常用开发工具
引擎开发IDE:Visual Studio
版本管理:SVN,Git
36.Debug,Release
主要差别有一下几点:
Debug模式下程序没有优化,可以断点
Debug模式很多代码会做更多的检测,如数组越界。导致有时候在Debug有断言中断的位置,换成Release就没有任何问题。
Debug模式下编译出来的程序比Release大的多,而且跑起来也可能慢的多。
37.游戏开发常用的数学知识
向量运算,三角函数,矩阵运算,四元数,随机数,坐标系运算(笛卡尔坐标系、极坐标系、球坐标系),空间变换,以及其他的几何学(点到面的距离,点是否在几何体内等)。
38.内存管理
涉及到内存碎片,内存分配,内存泄露,虚拟内容等内容。
39.容器
就是我们常说的数据结构,包括堆、栈、数组、链表、树、图、队列等。注意他们的特点与使用场景,比如是不是连续内存,是不是动态改变大小等。
40.I/O
I/O即输入/输出(Input/Output),一般指磁盘IO与缓存IO。我们在读写时可能需要将磁盘内容写到内存,内存写到缓存,缓存写到寄存器等等操作,一般这些操作是很耗时间的。所以CPU不能在IO时干等着,他会将任务交给DMA去处理,这样整个IO对CPU的消耗就大大减轻。不过即使如此,在上下切换时也要消耗CPU的时间,所以减少IO的数量还是很有必要的。
41.游戏循环与渲染循环
其实整个游戏本质上就是一个无限循环的程序,所有的逻辑都在循环里面处理。
同理,渲染模块本身也是一个循环,里面会有更新相机,更新场景元素,开始渲染,输出到屏幕等操作。
42.对象组件模型
很多游戏引擎都采用这种模式来构建游戏对象,组件模型通过将多个功能独立包装成单独的组件,可以自由的装配或或拆卸。达到了很好的解耦效果,增加了组件的通用性与灵活性。
游戏中的设计模式
1.有必要深刻的认识到设计模式的意义
游戏开发是一个软件工程,他的特点是需求极易变化。对于任何一个软件工程,我们要从代码的组织方式上来提高其可读性(别人也很容易理解),安全性(不能经常出bug),易用性(逻辑复杂,思路太偏,使用上各种麻烦),健壮性(要能良好的处理意外情况),可维护性(是否容易修复与修改),可拓展性(是否容易添加功能),封装和复用性(一段代码不要到处拷贝粘贴)
2.游戏项目的特点
对于游戏项目来说,除了上面的问题,我们更要注意其可拓展性,因为游戏的需求每天都可能发生变化,所以在写游戏逻辑的时候一定要尽可能的考虑多种情况,考虑到以后的修改是否方便等。
因此,我们需要尽可能降低代码的耦合性,做到不同的功能尽可能不要互相影响。不过,这里的降低耦合不仅仅是指自己写的模块间的耦合,还有考虑与别人写的模块的耦合,尽可能做到不要过分依赖别人的模块,这样别人代码的一点修改不至于造成我们代码的大规模修改。
3.不要过度设计
因为需求变化太过频繁,很可能你针对当前需求设计的是否完美的一个方案在第二天需求变更时就完全失效了。另外,对一个需求的设计是需要时间成本的,也许花费一个星期才思考一个近乎完美的解决方案,但第二个星期需求就改了,这一个星期的时间几乎完全浪费。这一点几乎无法完全掌握,但是可以通过经验积累尽量避免问题。
4.游戏性能是一项非常重要的指标
游戏与其他软件不同的是,很多游戏逻辑不是消息驱动,需要每帧都去调用。所以,我们在向Tick()函数里面添加方法时,都要去思考能不能换一种方式驱动来减小函数执行的开销。
5.Builder模式:
细分一个对象的构造细节并拆分,达到可以自由调整构造顺序以及构造内容的效果。
6.原型模式:
复制一个对象的实例,简单来说就是克隆。一般用在根据对象自己本身创建一个新的对象,隐藏对象创建细节。
7.Bridge桥接模式:
假如一个对象有两个维度的变化因素,那么可以通过继承抽象实现一个维度变化,通过在抽象类里面聚合另一个维度变化的因素(这里面也通过继承来抽象,而且还可以继续聚合抽象),这就是桥接模式。
8.Decorator装饰器:
就是对已经存在的某些类进行装饰,以此来扩展一些功能,装饰器的价值在于装饰,他并不影响被装饰类本身的核心功能。通常在一个继承的体系中,子类通常是互斥的,而有一些功能却是通用的,为了给不同的子类动态添加不同功能,就可以使用装饰器来达到这样的效果。
转载声明:本文来源于网络,不作任何商业用途。
IOS下载
安卓下载
小程序