如何从零开始的线上游戏写一个简单的游戏引擎

如何从零开始构建游戏引擎丨消息机制_gad吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:10,188贴子:
如何从零开始构建游戏引擎丨消息机制
作为开发者的,我们生活在一个伟大时代。每个人都能够使用大量的3A级游戏引擎,通过简单拖拽的方式创建游戏。这样看起来没有任何理由需要再重新写一个游戏引擎了,但是有一个世俗的问题是:你为何要开发游戏,而不是游戏引擎?这篇文章主要针对独立开发者和小型团队,并且假设读者对面向对象编程方式(Object-Oriented Programming)已经有所了解。我想要给正在从事引擎开发的工人人员提供一些建议并且使用一个简单虚拟的引擎来进行阐述。为何要开发游戏引擎?简短的回答是:如果能够避免,则不要开发游戏引擎?生命何其短暂,以至于不能为每一个游戏开发一个游戏引擎(摘自:Sergei Savchenko所著的三维图形开发)。目前主流的优秀游戏引擎,例如:Unity,Unreal和CryEngine都非常的灵活,用户可以使用它们创建几乎任何类型的游戏。对于一些特殊的开发工作,当然会有一些特殊的解决方案,像是Adventure Game Studio或者是RPG Maker,仅仅举一些例子。尽管消费级游戏引擎的成本不再是争论问题了。开发一个你自己的游戏引擎仅仅只是为了考虑一些小众团体的原因:1、你想要学习一个游戏引擎是如何工作的2、你想要的一些商业游戏引擎无法提供的功能,或者这些游戏引擎提供的解决方案不稳定3、你相信你可以让游戏引擎变得更好、变得更快4、你想要控制游戏引擎的开发 上述这些都是很好的理由,如果你正在阅读这篇文章,那么你可能会是因为上述原因。我的目标不是在这里进行冗长的辩论:关于我应该使用哪个游戏引擎或者我是否应该开发一个游戏引擎?所以,直切主题。为何开发游戏引擎会失败?诸位且慢,首先我告诉你不要去开发引擎,接下来再解释开发游戏引擎是如何失败的?非常好的开篇吧……不管怎样,在你开始写第一行代码的时候,有大量的事情需要你来思考。任何正在开发一个游戏引擎的人所面临的第一个也是最重要的问题总结如下:我想要尽快的看到一些能玩的游戏。然而,当你越快的意识到在你真正的能够看到一些有趣的事情之前将会花费大量的时间时,那么你开发的游戏引擎会变得越来越好。尽可能快的让你的代码展示一些图形或者是成品游戏,仅仅通过一些视觉上的内容来确保开发的“进程”,这是你开发过程中所面临的最大敌人。因此,不要急,慢慢来!在开发过程中,甚至不要考虑从图形来开始入手。你可能已经阅读了大量的关于OpenGL/DirectX的教程和书籍,并且知道如何绘制一个简单的三角形或二维位图(早期计算机图形学中的概念)。你可能会认为一个简短的用于渲染少数三角面片的程序段会是一个好的开始,然而并非如此。不得不承认,你刚开始开发的程序的确让人感到惊讶。你可以通过大量教程或者StackOverflow简单的复制粘贴,在仅仅一天的时间内就完成了一个第一人称游戏。但是,我敢保证,两天之后,你会删除你的每一行代码。更糟糕的是,你可能因为开发一个游戏引擎而变得气馁,因为之前的事情没有对你有所激励。开发者面临的第二个问题是特征变化。每一个人都想要开发一个可以完成任何事情的完美的游戏引擎。第一视角射击,RPG策略游戏以及任何应有尽有的游戏。但是事实是:我们并不能做到。看看这些大公司,即使是Unity也无法满足每一种类型的游戏的开发。在开始阶段,不要考虑开发一个可以处理超过一种类型的游戏引擎。慎之又慎!开发游戏引擎,从何入手?开发游戏引擎就像是为汽车开发一个真正的引擎一样。开发步骤显而易见,假设你已经知道你要开发哪种类型的游戏(或者汽车)。步骤如下:1.准确的指出你的引擎需要具备哪些能力以及不需要具备哪些能力。2.将你的引擎需要具备的部分组成成为不同的系统。3.设计一个完美的结构来将所有的系统进行融合。4.尽可能的重复1-3。5.开始写代码。当且仅当在1-4步骤上花费了足够的时间和精力时,你的游戏设计不再会突然的发生变化时——从恐怖游戏变为街机游戏(译者:游戏架构不再发生大的变化),此时,编程开发会比较容易的实现。尽管编程仍然不是很容易,但是代码已经变得易于管理,这些要求甚至对于独立开发者也是如此。步骤1:需求分析所有的这些步骤乍一看都十分简单。但事实并非如此。你可能会认为,开发一个第一人称视角的射击游戏引擎的步骤1可能是这样的:我需要加载一个关卡,玩家的枪支以及一些AI作为敌人。该步骤结束,转到步骤2.是否这么简单呢?步骤1最好的方法是,从点击桌面上的图标到关闭程序,一步一个动作的完成这个游戏,直到获得了你想要的内容。然后列出一个你想要的需求的清单和一个你不想要的需求的清单。经过上述操作,步骤1看起来应该是这样的:开启游戏,直接进入游戏主菜单。菜单是否使用静态图片?分镜怎样?如何控制主菜单,使用鼠标还是键盘?主菜单上需要哪种类型的GUI按钮?是表单还是滚动条?音乐如何设计?这些都需要进行大量的考虑。尽可能详细的列出需求。不仅仅考虑哪个按钮可以实现功能,还需要考虑所使用的按钮的好坏。我需要一个具有四种状态的按钮,向上、悬停、向下和禁用。是否需要为按钮指定音效?按钮需要什么样的特殊效果?它们在空闲环境下是什么状态?如果在设计主菜单的结束之后,你的需求列表所包含的内容没有超过10条,那么你肯定有所疏忽。在该阶段,你所要做的就是在你的大脑中模拟游戏引擎,并且写下你的需求。步骤1会在每一次迭代之后变得更加清晰,所以在第一次需求分析的时候不要担心会有所忘记。步骤2:将需求组织成为对应的系统至此,你拥有了一个你需要做以及不需要做的事情清单,是时候将它们组织起来了。显而易见,GUI相关的事情,像是按钮会被归类为GUI系统。渲染相关的会被归类为图形系统。正如步骤1一样,各个部分应该位于哪些正确的位置在第二次迭代的时候变得更加清晰。在完成步骤3之后,作为第一阶段,它们被组织为上述示例。关于“哪些位于何处”和“哪些应当做什么”的最好的参考书是由Jason Gregory撰写的游戏引擎架构。开始对函数进行组织,考虑如何组合它们。例如,可以将如下情况Camera-&rotateYaw(floatyaw), Camera-&rotatePitch(float pitch)重新组织为:Camera-&rotate(float yaw, floatpitch);让它们尽量保持简单。过多的函数会让你变得疲于奔命。考虑哪些函数是需要暴露给用户的(译者:即public函数),哪些函数在系统内部实现即可(译者:私有函数)。例如,你的渲染器需要在绘制之前将对象进行排序,其中排序的操作是不需要暴露的。你需要知道在绘制之前要进行排序,但是你并不需要借助于外部系统。步骤3:程序构建从这里开始的内容将会是有趣的并且重要的。最简单的构建游戏引擎的方法是通过类来实现每一个系统,并且通过一个游戏主循环来调用子程序。看下来会是如下的样子:while(isRunning){
Input-&readInput();
isRunning = GameLogic-&doLogic();
Camera-&update();
World-&update();
GUI-&update();
AI-&update();
Audio-&play();
Render-&draw();}首先看起来是非常合理的。涵盖了所有基本内容,从输入到处理,从输入到输出。的确,这对于一个简单的游戏引擎来说足够了。但是,它会给程序的维护带来困难。原因很显然:依赖关系复杂。每一个系统都需要和其它系统进行通信。但是,在上述的游戏循环中我们并没有任何实现。因此,从这个例子很显然的看出,为了处理一些必要的事情,每一个系统都必须具有其它系统的一个引用。GUI和游戏逻辑必须知道输入操作。渲染器为了更好地工作必须知道游戏逻辑。这些都会使构建过程变得复杂:看起来像意大利通心粉一样。很显然这不是你想要的。我们可以很简单快速的完成编码,我们能够获得可以接受的结果。但是,这些代码不具有良好的维护性。在某些局部进行修改,在我们不知情的前提下会对其它系统会造成一连串的反应。总会有一些代码需要多个系统访问。例如,GUI和渲染器都需要进行绘制操作,或者至少需要访问一些交互操作。我们可以简单让每个系统都具备直接操作OpenGL/DirectX的能力,但是这回造成大量的冗余操作。解决该问题的方法是将绘制函数全部放在渲染系统中,并且通过GUI系统来调用。那么,渲染系统需要为GUI系统提供一些特殊函数。这些内容并没有在渲染系统中有合适的位置,因此也违背了步骤1和步骤2的原则。综上所述,我们需要考虑的第一件事是将我们的系统进行分层。引擎之“千层面”至少在编程中,“千层面”要比“通心粉”好。对于我们的渲染系统的例子,我们想要做的是在每个系统中不去直接调用OpenGL/DirectX函数。这看起来像是一种包装。我们将所有的绘制函数放在另外一个类中。这些类相对于其它系统更加基础。我们称这些类为框架。这个想法的内涵是将大量的底层API函数抽象出来,将它们作为游戏引擎的定制功能。这些功能的启用和禁用仅仅是为了在渲染系统中进行一个简单的调用,因此我们并不想设置顶点缓存、索引缓存、纹理等。让我们将所有这些底层函数放在框架中。那么我们仅需要调用框架中的“绘制”操作即可。我们所要做的是设置,然后绘制。不需要关心到底是怎么绘制的以及在哪里绘制。将这些功能全部交给渲染系统来完成。这看起来比较奇怪,因为我们一方面想要我们的游戏引擎有较快的处理速度,但是过多的抽象层(框架)会降低游戏引擎的速度。如果在上世纪90年代,这种考虑是十分正确的。但是现在我们更需要关注程序的维护性,况且引入过多的抽象层对引擎速度几乎没有太大的影响。如何设计我们自己的绘制框架呢?简而言之,要做我们自己的轻量化API函数。SFML是一个很好的例子。有一些重要的事情需要牢记:a、保持良好的文档记录。我们开发了哪些函数?这些函数怎样调用,以及何时进行调用?b、函数尽量简单。简单的函数会让你在长期的开发过程中保持愉悦,例如:drawMesh(Mesh* oMesh)或者loadShader(StringsPath)。c、保持函数的功能性。不要设计一些特殊的函数,例如:使用drawSpritefunction取代drawButtonSprite。具体功能让用户来决定。通过上面的介绍,我们可以获得很多:a、对框架进行一次配置即可以在我们需要的每一个系统中使用(GUI、渲染系统)。b、我们可以在不对系统进行修改的前提下,有选择的很容易的修改底层API函数。OpenGl和DirectX之间的切换也没有问题。仅仅需要重写框架类即可。c、可以让每一个系统中的代码保持简洁和紧凑。d、如果有一个很好的文档说明,一个人可以在开发框架的同时,另外一个人可以进行系统级别的开发。经过上述调整,我们的系统看起来是这样的:将哪些内容放入框架非常简单。如果需要调用一些第三方的库(OpenGL, OpenAL, SFML…)或者有一些每个系统都需要用到的数据结构/算法,那么应该将这些内容放入框架中来完成。至此,引擎之“千层面”中的第一层已经完成了。但是,仍然存在有很多的“通心粉”。接下来我们解决这些问题。消息响应最大的问题仍然存在。我们的各个系统之间仍然是有关联的。然而我们不想让这种情况发生。有大量的方法可以处理上述问题。事件、消息、带有函数指针的抽象类(真是难懂)…这里选择消息响应的方法。比较简单,并且在GUI编程中极其受欢迎。非常适合作为我们引擎的一个简单的例子。它的工作原理比较类似与邮寄业务。公司A向公司B发送一些消息,并且发出请求。这两个公司之间不需要物理连接。公司A仅需要确保公司B在某个时间点完成任务即可。公司A并不关心公司B什么时候以及如何完成。它只需要公司B完成即可。不好了,公司B可能会将消息发送给公司C或者公司D,让它们进行处理。因此,更进一步,公司A不需要对发送对象进行特殊指定,公司A只需要将消息发送给那些可以处理的公司即可。在这种方式下,公司C和公司D可以直接处理这些请求。显然,我们可以把公司等价于我们的系统。看一个简单的例子:框架通知输入系统,“A”已经按下输入系统将按键响应“A”翻译为“打开目录”,并且发送一个包含“打开目录”的消息。GUI将会处理该消息,并且打开目录窗口。游戏逻辑将会处理该消息,并且暂停游戏。输入系统并不关心对“消息”做了什么。GUI系统也不关心游戏逻辑系统会处理同一条消息。这些系统还是耦合的吗?输入系统需要在GUI系统和游戏逻辑系统中调用函数。但是现在不再需要了。通过消息机制我们已经能够成功的将它们进行解耦。消息机制看起来应该是什么样的?至少应该具有一些类型。例如,打开目录应该是一个枚举类型:OPEN_INVENTORY。这对于简单的消息已经足够了。更多高级的消息需要包含数据,使用某种方式来存储这些数据。有很多方式可以实现,最简单的实现方式是使用一个简单的映射结构。但是,我们如何发送消息呢。当然是通过消息总线机制了。是不是很漂亮?不再有“通心粉”的存在,只有一些扁平的“千层面”(译者:取而代之的是分层系统)。我很小心的将游戏逻辑系统放在消息总线的另一头。正如你所见,它和“框架”层之间是没有关系的。这是非常重要的,对于避免“仅调用一个函数”的情况发生。相信我,你迟早会需要这样处理。但是这会打乱我们的设计。我们已经有足够的系统使用框架进行处理,游戏逻辑系统不需要。消息总线是一个很简单的类,每一个系统都有一个消息总线的引用。如果在队列中有一个消息,那么消息总线会将这个消息通过一个简单的消息句柄函数发送给每一个系统。与之对应,每一个系统都有消息总线的一个引用,用于发送消息。这种方式可以通过内部变量或者函数传参的方式实现。所有的系统需要从如下类派生而来:class System{public:
void handleMessage(Msg *msg);
switch(msg-&type)
//// Example
//case Msg::OPEN_INVENTORY:
} private:
MessageBus *msgB
//// Usage: msgBus-&postMessage(msg);}突然,游戏逻辑系统想要让消息总线派发消息。我们需要定期的通过一些更新回调函数来完成不同系统的更新。但是信息会进行不同的处理。然而,使用框架,并在上层构建消息机制。毫不违心的说,这些都会对引擎的速度有所降低。但是我们并不关心,我们只想要一个干净简单的设计,一个干净简单的架构。当然,最酷的事情是什么?我们可以很轻松的获得一些神奇的事情!诸位情况:控制台每一个消息基本上都是一个函数的调用。并且消息的发送无处不在。假如我们拥有一个可以简单打印每一个发送到输出窗口的消息的系统?假如这个系统同样可以将我们键入的消息进行发送?是的,我们赋予了控制台活力。它所需要的只是几行代码而已。当我第一次看到这些的时候有所震撼。它并没有绑定任何东西,它本身就存在在那里。游戏内建动画,回放以及调试假如我们需要“伪造”一些信息?假如我们仅仅需要一个在某一时刻发送消息的新的系统?想象跟随一个旋转对象,然后发送一个移动相机的消息。瞧,我们有内建的游戏动画。假如我们仅仅需要将游戏发送过程中的输入消息记录下来并且保存为一个文件。瞧,我们有录制功能。假如我们仅仅需要记录玩家的一切动作,并且在游戏崩溃时,将这些文件提供给玩家使用。
瞧,我们有玩家所有动作的准确备份来处理崩溃问题。多线程多线程?是的,多线程。我们将所有的系统进行解耦。这意味着,它们可以在任何时候、用任何方式、更重要的是在任何地点来处理这些消息。我们可以让我们的消息总线来决定每一个系统应该在哪个线程中处理消息。这也就是所谓的多线程。帧率固定如果我们在当前帧中有太多的消息要处理,怎么办?没有问题。我们将这些消息放入消息队列,然后交给下一帧处理。这将会让我们的游戏始终保持在60帧左右。玩家无法注意到AI系统花费了过长的时间去“思考”,他们只会注意到游戏帧率降低了。消息机制相当棒!为每一个消息和它的参数精细的撰写文档相当重要。把它们当作API接口对待。如果能够正确的做到,每一个开发者可以在不破坏任何事情的情况下在不同的系统上进行开发。即使假如一个系统需要离线工作或者正处于构建过程中,这个游戏仍然可以运行并进行测试工作。没有音频系统?我们还好有可视化系统。没有渲染系统,我们还好有控制台…不幸的是,消息机制也不是完美的有时候我们想要知道一个消息的处理结果,有时候我们想要消息马上被处理。我们需要找到一个可行的方案。一个解决办法是开发一条“高速公路”(译者:消息即使处理机制)。它是独立的简单的消息发送函数,我们可以通过postImmediateMessage函数来实现。该函数可以马上处理消息。处理返回的消息相对简单。这些迟早会发送给ourhandleMessage函数。我们只需要在发送时记住该消息即可。如果过多的处理即时消息显然违背了多线程和帧率固定原则。因此要在使用过程中有所限制。但是最大的问题是该系统会有延迟。本文介绍的并不是一个以运行速度为主的架构。如果你要开发一个快速响应的第一人称射击游戏,那么这是一个关键的问题。回头设计我们的架构我们决定使用系统和消息总线来开发游戏。我们已经完全知道该怎么构建我们的游戏引擎。是时候进行设计阶段的第四步了。迭代开发。一些函数可能并不适合存在在系统内部,我们必须找到对应的解决方案。一些函数可能会被频繁的调用,这可能会堵塞消息总线,我们必须找到对应的解决方案。这可能会花费一些时间,但是这在漫长的开发过程中是值得的。终于要进行编程开发了!步骤4:从何处开始编程在你开始编程之前,请阅读RobertNystrom所著的书籍游戏编程模式。除此之外,我草拟了一个技术路线路供你参考。它可能不是迄今为止最好的办法,但却非常有效。a、如果你要开发消息总线类型的游戏引擎,建议首先开发控制台和消息总线部分。一旦这些功能实现,你可以假设任何系统的存在,即使还没有完成代码编写工作。你将会在开发的每一个阶段对整个引擎进行掌控。b、下一步考虑开发GUI系统,同时考虑框架开发中所需的绘制函数。一个与控制台匹配的稳定的GUI系统可以让你更容易的开发其它系统。测试也变得轻而易举。c、下一步应该进行框架开发,至少先开发它的接口。具体实现函数可以稍后开发。d、最后,进行其它系统的开发。包括游戏玩法。你会注意到,事实上渲染任何和游戏相关的内容可能总是放在最后去做。这会是一件好事!你会感到值得的,始终保持动力直到完成你的游戏引擎的最终开发。你的游戏策划可能会在开发过程中打扰你。通过控制指令进行游戏可行性的测试和在IRC上玩反恐精英一样有趣。结论花费一些时间来寻找一个稳定的架构并坚持下去!这是我希望你能够从这篇文章中学到的内容。如果你能够这样做,你将会有能力在一天结束的时候构建起一个完美的、可维护的游戏引擎。否则,要花费一个世纪。翻译:刘超
贴吧热议榜
使用签名档&&
保存至快速回贴经检测你所在的网络可能存在爬虫,因资源限制,我们只能拒绝你的请求。
如果你是推酷的用户,可以以继续访问使用。
如有疑问,可将IP信息发送到
请求解封。&&&&从零开始写一个简单的操作系统
从零开始写一个简单的操作系统
本文档将介绍从零开始写一个操作系统,由英国伯明翰大学贡献。
Writing a Simple Operating System from Scratch. School of Computer Science, University of Birmingham,
嵌到我的页面
<input type="text" readonly="true" value="">
若举报审核通过,可奖励20下载分
被举报人:
举报的资源分:
请选择类型
资源无法下载
资源无法使用
标题与实际内容不符
含有危害国家安全内容
含有反动色情等内容
含广告内容
版权问题,侵犯个人或公司的版权
*详细原因:
VIP下载&&免积分60元/年(1200次)
您可能还需要
操作系统下载排行

我要回帖

更多关于 从零开始异世界 游戏 的文章

 

随机推荐