如何从零开始学软件开发用openGL开发一个游戏引擎

如何从零开始用openGL开发一个游戏引擎? - 知乎460被浏览43077分享邀请回答1添加评论分享收藏感谢收起openGl从零开始之基本图元 - 子弹壳 - 博客园
人生就像一杯茶,不会苦一辈子,但总会苦一阵子。
从本篇开始,会给出一些代码实例,所以要配置好编译环境。
环境配置:vs2012下配置链接
     vc++6.0下配置链接
在给出具体的绘图代码之前,先讲解一下创建一个openGl程序窗口的初始化,在上一篇文章里已经介绍了,这里再贴一遍。
void display(void)
//这是我们自己的绘图函数,我们在这里进行具体地绘制。
int main(int argc, char** argv)
glutInit(&argc, argv);
//对GLUT进行初始化,必须一开始就初始化
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
//设置显示方式,参数之后再解释
glutInitWindowPosition(100, 100);
//设置程序窗口在屏幕中的位置
glutInitWindowSize(400, 400);
//设置程序窗口的大小
glutCreateWindow("第一个OpenGL程序");
//给程序窗口添加标题
glutDisplayFunc(&display);
//*调用我们自己的绘图函数来绘制
glutMainLoop();
//启动消息循环后程序运行起来
OpenGL的基本图元有点(Point)、线段(Line)、多边形(Ploygon)、三角形(Triangle)、四边形(Quadrangle)。组成关系我想大部分人都能够理解:
  ①无数个&点&的线性排列组成----&线段&
  ②多个&线段&可以围成一个&多边形&
  ③多个&多边形&在三维空间中可以围成&多面体&
有此,可以看出,点是最基本的图元,其他的图元都是由顶点组合构成的,因此我们从点开始介绍。
在OpenGL中,&点&称为顶点(Vertex),通常用一个形如(x, y, z)的三维坐标值表示。
有时候会采用齐次坐标表示,就是一个形如(x, y, z, w)的四元组,可以这样理解这个四元组,前面的(x,y,z)表示&点&所在的方向,而w表示在该方向上的距离比例,所以(x, y, z, w)表示的实际坐标值就是(x/w, y/w, z/w)坐标点。如果w为0,则表示方向为(x, y, z)的无穷远点;如果w=10,表示取(x,y,z)的十分之一作为坐标点(x/10,y/10,z/10)。
一般情况 下,OpenGL 中的点将被画成单个的像素。实际上点还可以更小,虽然它可能足够小,但并不会是无穷小,一个像素内可以描绘多个点,取决于对点大小的设定,默认一个点的大小为一个像素。
当然我们也可以改变点的大小,函数原型为:
//size 必须大于 0.0f,默认值为 1.0f,单位为&像素&。
//对于具体的 OpenGL 实现,点的大小都有个限度的,如果设置的 size 超过最大值,则设置可能会有问题,一般情况是不在增大。
void glPointSize(GLfloat size);
代码实现画一个点,我们只需要在我们的绘图函数中添加如下代码即可:
void display(void)
glPointSize(5);//在绘制之前要设置要相关参数,这里设置点的大小为5像素
glBegin(GL_POINTS);
glVertex2f(0.0f, 0.0f); //OpenGl内的点是齐次坐标的四元组,缺省的z坐标为0.0f,w为1.0f,所以该点为(1, 2, 0, 1)
glVertex2f(0.0f, 0.5f); //绘制的第二个点
glVertex2f(0.5f, 0.25f); //绘制的第三个点
glFlush();//glFlush,保证前面的OpenGL命令立即执行(而不是让它们在缓冲区中等待)
需要注意的是:坐标的值默认是1.0为最大,即屏幕边缘,所以如果大于1,则会超出屏幕,这时候除非改变观察点,否则是看不到的。(0,0)表示在屏幕的中间,也就是坐标系原点。
OpenGL中绘制几何图元,必须使用 glBegain() 和 glEnd() 这一对函数,glBegin() 和 glEnd() 需要成对使用,两者之间是绘图语句。
glBegin()的参数就是需要绘制的图元类型,它决定了在glBegin()和glEnd()之间绘图的点将如何组织成目标图元,如参数为&GL_POINTS&,表示每一个点都是独立的,互不相连的,因此也就绘制出了一个个&点&。
线段是由两个顶点连接起来形成的图元。线段间的连接方式有三种:
①独立线段:图元类型参数--GL_LINES
②线段间首尾相连但最终不闭合:折线,图元类型参数--GL_LINE_STRIP
③线段间首尾相连最终封口闭合:图形,图元类型参数--GL_LINE_LOOP
对应的图例:
线段代码实例:
void display(void)
/*同样用5个点,画出不同类型的线段组合*/
//独立线段,5个点能画出2条线段
glBegin(GL_LINES);
glVertex2f(-0.8f, -0.5f);
glVertex2f(-0.5f, -0.5f);
glVertex2f(-0.8f, 0.0f);
glVertex2f(-0.5f, 0.0f);
glVertex2f(-0.8f, 0.5f); //最后的这个点没有与之配对的点,无法连成线段,所以不会被画出来,在独立线段模式下被舍弃
//连续不闭合折线,5个点能画出4条线段
glBegin(GL_LINE_STRIP);
glVertex2f(-0.3f, -0.5f); //起始点
//后面的每一个点都会与前一个相连生成一条线段
glVertex2f(-0.0f, -0.5f);
glVertex2f(-0.3f, 0.0f);
glVertex2f(-0.0f, 0.0f);
glVertex2f(-0.3f, 0.5f);
//连续闭合折线,5个点能画出5条线段
glBegin(GL_LINE_LOOP);
glVertex2f(0.2f, -0.5f); //起始点
//后面的每一个点都会与前一个相连生成一条线段
glVertex2f(0.5f, -0.5f);
glVertex2f(0.2f, 0.0f);
glVertex2f(0.5f, 0.0f);
//最后一个点不仅与前一个点相连,还与起始点相连,形成闭合
glVertex2f(0.2f, 0.5f);
glFlush();//保证前面的OpenGL命令立即执行(而不是让它们在缓冲区中等待)
程序运行示例图:
同样的,直线可以指定宽度:
void glLineWidth(GLfloat width); //width表示线宽,单位:像素
特殊的是线除了直线,还有虚线。可以用虚线连接两个点。
在绘制虚线之前必须先启动&虚线模式&:glEnable(GL_LINE_STIPPLE);
虚线有不同的类型,调节函数如下:
参数pattern是16位二进制数(0或1),如OxAAAA表示1010
从低位开始,每一个二进制位代表一个像素, 1表示用当前颜色绘制一个像素,0表示当前不绘制,只移动一个像素位,中间留下一个像素的空白
factor是用来调节二进制位0和1代表的像素个数,如果factor为2,则表示遇到二进制1的时候用当前颜色绘制两个像素,0移动两个像素不绘制
void glLineStipple(GLint factor,GLushort pattern);
void display(void)
//开启虚线模式
glEnable(GL_LINE_STIPPLE);
//调节虚线类型参数
glLineStipple(2,0xAAAA);
//调节线宽
glLineWidth(3);
//绘制一条虚线
glBegin(GL_LINES);
glVertex2f(0,0);
glVertex2f(0.5,0.5);
glFlush();//保证前面的OpenGL命令立即执行(而不是让它们在缓冲区中等待)
程序运行示例:
类似于点组合线段的方式,OpenGL定义的多边形是由多个点连接成线段再围成封闭区域。
多边形有两种:凸多边形(指多边形任意非相邻的两点的连线位于多边形的内部)和凹多边形,但OpenGL中规定的多边形必须是凸多边形。
但有时需要绘制一些凹多边形,通常解决的办法是对它们进行分割,用多个三角形来组合替代。显然,绘制这些三角形时,有些边不应该进行绘制,否则,多边形内部就会出现多余的线框。OpenGL提供的解决办法是通过设置边标志命令glEdgeFlag()来控制某些边产生绘制,而另外一些边不产生绘制,这里只需知道有这个工能,具体细节待遇到在研究,接下来看重点。
(1)连接方式
&1&按点的定义顺序依次连接:图元类型参数--GL_POLYGON
&2&从第1个点开始,每三个点一组画一个三角形,三角形之间是独立的:图元类型参数--GL_TRIANGLES
&3&从第三个点开始,每点与前面的两个点组合画一个三角形,即线性连续三角形串:图元类型参数--GL_TRIANGLE_STRIP
&4&从第三个点开始,每点与前一个点和第一个点组合画一个三角形,即扇形连续三角形:图元类型参数--GL_TRIANGLE_FAN
对应的图例:
四边形的绘制模式和三角形类似。
(2)多边形的正反两面
多边形为什么会有正反面这一说法呢?举个简单的例子。
虽然多边形是二维的,但是我们知道三维物体可以理解为多边形围成的,也就是说在三维空间物体上有二维多边形的存在。
一张&正方形&纸片,如果极限的薄,我们可以认为是一个多边形(正方形),那么纸仍然会存在两面,纸的正反面。更具体一点,如果我们用同大小6张正方形的纸片围成一个正方体,那么正方体的任何一个面都是一个多边形,而且这个多边形是有正反面,朝外的你能看到的部分是正面(假设),那么朝内的看不到的那部分就是反面。同样的道理,OpenGL要区分多边形的正反面。那么到底怎么定义正面或者反面呢?
有一个函数来定义:
glFrontFace(GL_CCW);//设置点序列逆时针方向围成的多边形为正面
glFrontFace(GL_CW); //设置点序列顺时针方向围成的多边形为正面
设置这个的原因是,默认情况下,OpenGL绘制三维物体时两面都会绘制,而实际的时候很多面我们是看不到的,如物体朝内方向的多边形,还有一些物体间遮挡的情况导致有些多边形是不可见的,因此为了提高性能,我们需要剔除这些不必要的绘制。
glEnable(GL_CULL_FACE); //来启动剔除功能
glCullFace(); //参数可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,表示剔除多边形的哪种面,设定后该类型的多边形不绘制
(3)绘制模式
常用的多边形绘制模式有:填充式(默认)、轮廓线式、顶点式和镂空图案填充式。
前三种都是使用glPolygonMode()函数来指定模式,最后一种比较特殊。
void glPolygonMode(GLenum face,GLenum mode);//该函数要求说明是对多边形哪一个面是定face设置模式
画一个多边形,设置不同的绘制模式,看一下效果:
void myDisplay(void)
//设置点的大小,以便容易观察
glPointSize(5);
//设置正面的绘制模式为:填充式,反面不设定则默认为填充式
glPolygonMode(GL_FRONT,GL_FILL);
glBegin(GL_POLYGON);
glVertex2f(0,0);
glVertex2f(0,0.3);
glVertex2f(-0.3,0.3);
glVertex2f(-0.3,0);
//设置正面的绘制模式为:轮廓线式
glPolygonMode(GL_FRONT,GL_LINE);
glBegin(GL_POLYGON);
glVertex2f(0.5,0);
glVertex2f(0.5,0.3);
glVertex2f(0.2,0.3);
glVertex2f(0.2,0);
//设置正面的绘制模式为:顶点式
glPolygonMode(GL_FRONT,GL_POINT);
glBegin(GL_POLYGON);
glVertex2f(0.9,0);
glVertex2f(0.9,0.3);
glVertex2f(0.6,0.3);
glVertex2f(0.6,0);
glFlush();
关于最后一种镂空图案样式,有点类似于线段中的虚线,这里只不过是针对多边形内部区域进行像素点的绘制与否,来做镂空效果。具体的细节还需查阅资料。
以上为基本图元的知识讲解,并给出了针对性的代码实例,在此基础之上会进一步学习更加复杂的图形绘制。温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
2、添加必要的框架
在“Build Phases”栏,添加进三个框架:
3、修改viewController.h
添加“#import &GLKit/glkit.h&”,并将它修改为继承“GLKViewController”。
4、修改“view”的类
双击“MainStoryboard.storyboard”展开,选择“view”。&&
然后,在其“Identity Inspector”中,将它的类改为“GLKView”。&&
阅读(8720)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_',
blogTitle:'从零开始构建IOS的OpenGL应用(一)',
blogAbstract:'
一、搭建开发环境
1、打开XCODE',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:8,
publishTime:2,
permalink:'blog/static/',
commentCount:3,
mainCommentCount:3,
recommendCount:1,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}努力加载中,稍等...
暂无新消息
努力加载中,稍等...
已无更多消息...
这些人最近关注了你
努力加载中,稍等...
已无更多消息
努力加载中,稍等...
已无更多消息
& 从零开始构建游戏引擎(一):消息机制
Writing a Game Engine from Scratch-Part 1:Messaging
征集热心朋友翻译文章,奖励规则:每100汉字奖励10QB,30天内互动奖励0 - 50QB.
翻译请求:
该文章来自用户转载
Part 1 - MessagingWe live in a great time to be developers. With such a huge amount of great AAA-grade Engines available to everyone, making simple games can be as easy as drag-and-drop. There seems to be no reason at all to write an engine anymore these days. And with the common sentiment, "Write Games, not Engines", why should you?This Article is primarily aimed at solo-developers and small teams. I assume some familiarity with Object-Oriented Programming. I want to give you some insight into approaching Engine development and will use a simple fictive Engine to illustrate this.Why write an Engine?The short answer is: Don't, if you can avoid it. Life is to short to write an engine for each game (Taken from the Book 3D Graphics Programming by Sergei Savchenko)The current selection of excellent Engines such as Unity, Unreal or CryEngine are as flexible as one could hope and can be used to make pretty much any game. For more specialised tasks, there are of course more specialised solutions like Adventure Game Studio or RPG Maker, just to name a few. Not even the cost of commercial grade Engines is an argument anymore. There are only a few niche reasons left to write your own engine:You want to learn how an engine worksYou need certain functionalities that aren't available or the available solutions are unstableYou believe you can do it better / fasterYou want to stay in control of developmentAll of these are perfectly valid reasons and if you are reading this, you probably belong to one of those camps. My goal is not to go into a lengthy "Which engine should I use?" or "Should I write an Engine?" debate here and will jump right into it. So, let us begin.How to fail writing an EngineWait. First I tell you not write one, next I explain how to fail? Great Introduction... Anyways, there are alot of things to concider before even writing a single line of code. The first and biggest problem everyone who starts writing a game engine has can be boiled down to this:I want to see some gameplay as fast as possible!However, the faster you realise that it will take alot of time until you actually see something interesting happen, the better off you will be writing your engine. Forcing your code to show some form of graphics or gameplay as fast as you can, just to have some visual confirmation of "progress", is your biggest enemy at this point. Take. Your. Time!Don't even think about starting with the graphics. You've probably read alot of OpenGL / DirectX tutorials and books and know how to render a simple triangle or sprite. You might think a short code snippet of Rendering a little mesh on screen is a good place to start. It is not.Yes, your initial progress will be amazing. Heck, you could be running around a little level in First-Person in just a day copy-pasting code snippets from various tutorials and Stack Overflow. But I guarantee you, you will delete every single line of that code 2 days later. Even worse, you might even get discouraged from writing an Engine, as it isn't motivating to see less and less.The second big problem developers face while writing Engines is feature creep. Everyone would love to write the holy grail of Engines. Everyone wants that perfect Engine that can do everything. First Person Shooters, Tactical RPGs, you name it. But the simple fact remains, we can't. Yet. Just look at the big names. Not even Unity can truly cater to every Game genre perfectly.Don't even think about writing an Engine that can do more than one genre on your first go. Don't! Where to actually begin when writing an EngineWriting an Engine is like engineering a real Engine for a car. The steps are actually pretty obvious, assuming you know what Game (or Car) you are working on. Here they are:Pinpoint exactly what your engine needs to be capable of AND what your engine doesn't need to be capable of.Organize the needs into Systems your engine will require.Design your perfect Architecture that ties all these Systems together.Repeat Steps 1. - 3. as often as possible.Code.Iff (= if and only if) you spend enough time and effort on steps 1. - 4. and the Game Design doesn't suddenly change from a Horror Game to a Slot Machine (read: Silent Hill), coding will be a very pleasent endeavour. Coding will still be far from easy, but perfectly manageable, even by Solo-Developers.This is the reason this Article is mainly about Steps 1. - 4. Think of Step 5. as "Filling in the Blanks. 50.000 LOC of Blanks".The most crucial part of all of this is Step 3. We will focus most our efforts here!Step 1. Pinpoint the Needs and Need NotsAll of these Steps may seem rather trivial at first. But they really aren't. You might think Step 1 of the process of developing a First Person Shooter Engine can be boiled down to this:I need to load a Level, the Players gun, some Enemies with AI. Done, on to Step 2.If it were only this easy. The best way to go about Step 1 is to go through the entire Game Click-by-Click, Action-by-Action from Clicking the Icon on your Desktop, to hitting the Exit Key after rolling the Credits. Make a List, a large List of what you need. Make a List of what you definitely don't need.This will probably go down like this:I start the game and it goes directly to the Main Menu. Will the Menu use a Static Image? A Cut Scene? How do I control the Main Menu, Mouse? Keyboard? What kind of GUI Elements do I need for the Main Menu? Buttons, Forms, Scrollbars? What about Music?And those are just macro-conciderations. Go in as detailed as possible. Deciding that you need Buttons is fine and good, but also concider what a button can do.
I want the buttons to have 4 States, Up, Hover, Down, Disabled. Will I need Sound for the Buttons? What about Special Effects? Ar they animated in the Idle State?If your List of Needs and Need Nots only contains about 10 Items by the end of the Main Menu, you did something wrong.At this stage, what you are doing is simulating the Engine in your brain and writing down what needs to be done. Step 1 will become clearer each iteration, don't worry about missing anything the first time.Step 2. Organize the needs into SystemsSo, you have your lists of things you need and don't need. It is time to organize them. Obviously, GUI related things like Buttons will go into some sort of GUI System. Rendering related items go into the Graphics System / Engine.Again, as with Step 1, deciding on what goes where will be more obvious on your second iteration, after Step 3. For the first pass, Group them logically as in the example above.The best Reference on "what goes where" and "what does what" is without a doubt the Book Game Engine Architecture by Jason Gregory. Start grouping the functionality. Start thinking of ways of combining them. You don't need Camera-&rotateYaw(float yaw) and Camera-&rotatePitch(float pitch) if you can combine them into Camera-&rotate(float yaw, float pitch). Keep it simple. Too much functionality (remember, feature creep) will hurt you later on.Think about what functionality needs to be publicly exposed and what functionality only needs to reside inside the System itself. For example, your Renderer needs to sort all transparent Sprites before drawing. The function to sort these sprites however, does not need to be exposed. You know you need to sort transparent Sprites before drawing, you don't need some external System to tell you this.Step 3. The Architecture (Or, the actual Article)We might as well have started the Article here. This is the interesting and important part.One of the simplest possible Architectures your engine can have is putting each System into a Class and having the Main Game Loop call their subroutines. It might look something like this:while(isRunning){
Input-&readInput();
isRunning = GameLogic-&doLogic();
Camera-&update();
World-&update();
GUI-&update();
AI-&update();
Audio-&play();
Render-&draw();} Seems perfectly reasonable at first. You have all your basics covered, Input -& processing Input -& Output.And indeed, this will suffice for a simple Game. But it will be a pain to maintain. The reason for this should be obvious: Dependencies.Each System must communicate with other Systems in some way. We don't have any means to do that in our Game Loop above. Therefore the example clearly indicates, that each System must have some Reference of the other Systems in order to do anything meaningful. Our GUI and Game Logic must know something about our Input. Our Renderer must know something about our Game Logic in order to display anything meaningful.This will lead to this architectural marvel:If it smells like Spaghetti, it is Spaghetti. Definitely not what we want. Yes, it is easy and quick to code. Yes, we will have acceptable results. But maintainable, it is not. Change a little piece of code somewhere and it might have devestating effects on all other systems without us knowing.Further, there will always be code that many Systems need Access to. Both the GUI and the Renderer need to do Draw calls or at least have access to some sort of Interface to handle this for us. Yes, we could just give each System the power to call OpenGL / DirectX functions directly, but we will end up with alot of redundancies.We could solve this by collecting all draw functions inside the Renderer System and call those from the GUI system. But then the Rendering System will have specific functions for the GUI. These have no place in the Renderer and is therefore contrary to Step 1 and 2. Decisions, Decisions.Thus the first thing we should concider is dividing our Engine into Layers.Engine LasagneLasagna is better than Spaghetti. At least programming wise. Sticking to our Renderer Example, what we want is to call OpenGL / DirectX functions without calling them directly in the System. This smells like a Wrapper. And for the most part it is. We collect all the draw functionality inside another Class. These classes are even more basic than our Systems. Let's call these new classes the Framework.The idea behind this is to abstract away alot of the low level API calls and form them into something tailored to our game. We don't want to set the Vertex Buffer, set the Index Buffer, set the Textures, enable this, disable that just to do a simple draw call in our Renderer System. Let's put all that low level stuff in our Framework. And I'll just call this part of the Framework "Draw". Why? Well, all it does is set everything up for drawing and then draw it. It doesn't care what it draws, where it draws, why it draws. That is left to the Renderer System.This might seem like a weird thing, we want speed in our engine right? More Abstraction Layers = Less Speed. And you would be right, if it were the 90's. But we need the maintainability and can live with the barely noticable speed loss for most parts.How then should our Draw Framework be designed? Simply put, like our own little API. is a great example of this.Important things to keep in mind:Keep it well documented. What functions do we have? When can they be called? How are the called?Keep it simple. Easy Functions like
drawMesh(Mesh* oMesh) or loadShader(String sPath) will make you happy in the long run.Keep it functional. Don't be too specific. instead of drawButtonSprite, have a drawSpritefunction and let the Caller handel the rest.What do we gain? Alot:We only need to setup our Framework once and can use it in every System we need (GUI, Renderer....)We can easily change the underlying API's if we chose, without rewriting every System. Switch from OpenGL to DirectX? No Problem, just rewrite the Framework Class.It keeps the Code in our Systems clean and tight.Having a well documented Interface means, one person can work on the Framework, while one person works in the System Layer.We will probably end up with something like this:My rule of thumb of what goes into the Framework is rather simple. If I need to call an external Library (OpenGL, OpenAL, SFML...) or have Data Structures / Algorithms every System needs, I should do that in the Framework.We now have our first Layer of Lasagna done. But we still have this huge ball of Spaghetti above it. Let's tackle that next.MessagingThe Big Problem however remains. Our Systems are still all interconnected. We don't want that. There are a multitude of ways of dealing with this issue. Events, Messages, Abstract Classes with Function Pointers (How esoteric)...Let's stick to Messages. This is a simple concept that is still very popular in GUI programming. It is also well suited as an easy example for our Engine.It works like a Postal Service. Company A sends a message to company B and requests something be done. These companies need no physical connection. Company A simply assumes that company B will do it at some point. But, for now, company A doesn't really care when or how company B does it. It just needs doing. Heck, company B might even decide to reroute the message to company C and D and let them handle it.We can go one step further, company A doesn't even need to send it to someone specific. Company A simply posts the letter and anyone who feels responsible will process it. This way company C and D can directly process the request.Obviously, the companies equal our Systems. Let's take a look at a simple example:Framework notifies Input System that "A" was pressedInput translates that Keystroke "A" means "Open Inventory" and sends a Message containing "Open Inventory"GUI handles the Message and opens the Inventory WindowGame Logic handles the Message and pauses the GameInput doesn't even care what is being done to it's Message. GUI doesn't care that Game Logic also processes the same Message. Were they all coupled, Input would need to call a function in the GUI System and a function in Game Logic. But it doesn't need to anymore. We were able to successfully decouple this using Messages.How does a Message look like? It should at least have some Type. For example, opening the inventory could be some enum called OPEN_INVENTORY. This suffices for simple Messages like that. More advanced Messages that need to include data will need some way to store that data. There are a multitude of ways to accomplish this. The easiest to implement is using a simple map structure.But how do we send Messages? Via a Message Bus of course!Isn't it beautiful? No more Spaghetti, just good ol' plain Lasagna. I deliberately put our Game Logic on the other side of the Message Bus. As you can see, it has no Connection to the Framework layer. This is important to avoid any temptation to "just call that one function". Trust me, you will want to sooner or later, but it would break our design. We have enough Systems dealing with the Framework, no need to do that in our Game Logic.The Message Bus is a simple Class with References to every System. If it has a Message in queue, the Message Bus posts it to every System via a simple handleMessage(Msg msg) call. In return, every System has a reference to the Message Bus in order to post Messages. This can obviously be stored internally or passed in as a function argument. All our Systems must therefore inherit or be of the following form: class System{public:
void handleMessage(Msg *msg);
switch(msg-&type)
//// Example
//case Msg::OPEN_INVENTORY:
MessageBus *msgB
//// Usage: msgBus-&postMessage(msg);} (Yes, Yes, raw Pointers...) Suddenly, our Game Loop changes to simply letting the Message Bus send around Messages. We will still need to periodically update each System via some form of update() call. But communication will be handled differently.However, like with our Frameworks, using Messages creates overhead. This will slow down the engine a bit, let's not kid ourselves. But we don't care! We want a clean and simple Design. A clean and simple Architecture!And the coolest part? We get amazing things for free!The ConsoleEvery Message is pretty much a function call. And every Message gets send pretty much everywhere! What if we have a System that simply prints every Message that is send into some Output window? What if this System can also send Messages we type into that window?Yes, we have just given birth to a Console. And all it took us is a few lines of code. My mind was blown way back when I first saw this in action. It isn't even tied into anything, it just exists.A console is obviously very helpful while developing the game and we can simply take it out in Release, if we don't want the Player to have that sort of access.In-Game Cinematics, Replays & DebuggingWhat if we fake Messages? What if we create a new System that simply sends Messages at a certain Time? Imagine it sending something like MOVE_CAMERA, followed by ROTATE_OBJECT.And Voila, we have In-Game Cinematics.What if we simply record the Input Messages that were sent during the Game and save them to a file?And Voila, we have Replays.What if we just record everything the Player does, and when the game crashes, have them send those data files to us?And Voila, we have an exact copy of the players actions that lead to the crash.Multi-ThreadingMulti-Threading? Yes, Multi-Threading. We decoupled all our Systems. This means, they can process their Messages whenever they want, however they want and most importantly, wherever they want. We can have our Message Bus decide on what thread each System should process a Message -& Multi-ThreadingFrame Rate FixingWe have too many Messages to process this Frame? No Problem, let's just keep them in the Message Bus Queue and send them the next Frame. This will give us the opportunity to ensure that our Game runs at a smooth 60 FPS. Gamers won't notice the AI taking a few Frames longer to "think". They will however notice Frame Rate drops. Messages are cool.It is important that we meticulously document each Message and it's parameters. Treat it like an API. If you do this right, every developer can work on different Systems without breaking anything. Even if a System should be offline or under Construction, the Game will still run and can be tested. No Audio System? That's fine, we still have Visuals. No Renderer, that's fine, we can use the Console...But Messages aren't perfect. Sadly.Sometimes, we DO want to know the outcome of a Message. Sometimes we DO need them to be processed immediately. We need to find viable options. A solution to this is to have a Speedway. Apart from a simple postMessage function, we can implement a postImmediateMessage function that get's processed immediately. Handling return Messages is far easier. Those get send to ourhandleMessage function sooner or later. We just need to remember this when posting a Message.Immediate Messages obviously break Multi-Threading and Frame Rate Fixing if done in excess. It is thus vital to restrict yourself to limit their usage.But the biggest Problem with this System is latency. It isn't the fastest Architecture. If you are working on a First Person Shooter with twitch like response times, this might be a deal breaker.
Back to Designing our ArchitectureWe have decided to use Systems and a Message Bus. We know exactly how we want to structure our Engine.It is time for Step 4 of our Design process. Iteration. Some functions might not fit inside any System, we must find a solution. Some functions need to be called extensively and would clog up the Message Bus, we must find a solution.This takes time. But it is worth it in the long run. It is finally time to Code!Step 4. Where to Start Coding? Before you start coding, read the Book/Articles
by Robert Nystrom.Other than that, I have sketched a little roadmap you could follow. It is by far not the best way, but it is productive.If you are going with a Message Bus type of Engine, concider coding the Console and the Message Bus first. Once those are implemented, you can fake the existence of any System that has not yet been coded. You will have constant control over the entire engine at every stage of developement.Concider moving on to the GUI next, as well as the needed Draw functionality inside the Framework. A solid GUI paired with the Console will allow you to fake all other Systems even easier. Testing will be a breeze.Next should be the Framework, at least it's interface. Functionality can follow later.Finally, move on to the other Systems, including Gameplay.You will notice, actually rendering anything related to Gameplay might be the last thing you do. And that is a good thing! It will feel so much more rewarding and keep you motivated to finish the final touches of your Engine.Your Game Designer might shoot you during this process however. Testing Gameplay through Console commands is about as fun as playing Counter Strike via IRC.ConclusionTake your time to find a solid Architecture and stick with it! That is the advice I hope you take away from this Article. If you do this, you will be able to construct a perfectly fine and maintainable Engine at the end of the day. Or century.Personally, I enjoy writing Engines more than doing all that Gameplay stuff. If you have any questions, feel free to contact me via . I'm currently finishing up another Engine using the methods I have described in this Article.You can find .
从零开始构建游戏引擎(一):消息机制
版权所有,禁止匿名转载;禁止商业使用;禁止个人使用。
翻译:刘超(君临天下)
审校:梁君(君儿)第一部分——消息机制
作为开发者的,我们生活在一个伟大时代。每个人都能够使用大量的3A级游戏引擎,通过简单拖拽的方式创建游戏。这样看起来没有任何理由需要再重新写一个游戏引擎了,但是有一个世俗的问题是:你为何要开发游戏,而不是游戏引擎?
这篇文章主要针对独立开发者和小型团队,并且假设读者对面向对象编程方式(Object-Oriented Programming)已经有所了解。
我想要给正在从事引擎开发的工人人员提供一些建议并且使用一个简单虚拟的引擎来进行阐述。 为何要开发游戏引擎?
简短的回答是:如果能够避免,则不要开发游戏引擎?
生命何其短暂,以至于不能为每一个游戏开发一个游戏引擎(摘自:Sergei Savchenko所著的三维图形开发)。
目前主流的优秀游戏引擎,例如:Unity,Unreal和CryEngine都非常的灵活,用户可以使用它们创建几乎任何类型的游戏。对于一些特殊的开发工作,当然会有一些特殊的解决方案,像是Adventure Game Studio或者是RPG Maker,仅仅举一些例子。尽管消费级游戏引擎的成本不再是争论问题了。
开发一个你自己的游戏引擎仅仅只是为了考虑一些小众团体的原因: l你想要学习一个游戏引擎是如何工作的 l你想要的一些商业游戏引擎无法提供的功能,或者这些游戏引擎提供的解决方案不稳定 l你相信你可以让游戏引擎变得更好、变得更快 l你想要控制游戏引擎的开发
上述这些都是很好的理由,如果你正在阅读这篇文章,那么你可能会是因为上述原因。我的目标不是在这里进行冗长的辩论:关于我应该使用哪个游戏引擎或者我是否应该开发一个游戏引擎?所以,直切主题。 为何开发游戏引擎会失败?
诸位且慢,首先我告诉你不要去开发引擎,接下来再解释开发游戏引擎是如何失败的?非常好的开篇吧……
不管怎样,在你开始写第一行代码的时候,有大量的事情需要你来思考。任何正在开发一个游戏引擎的人所面临的第一个也是最重要的问题总结如下:
我想要尽快的看到一些能玩的游戏。
然而,当你越快的意识到在你真正的能够看到一些有趣的事情之前将会花费大量的时间时,那么你开发的游戏引擎会变得越来越好。
尽可能快的让你的代码展示一些图形或者是成品游戏,仅仅通过一些视觉上的内容来确保开发的“进程”,这是你开发过程中所面临的最大敌人。因此,不要急,慢慢来!
在开发过程中,甚至不要考虑从图形来开始入手。你可能已经阅读了大量的关于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撰写的游戏引擎架构。
开始对函数进行组织,考虑如何组合它们。例如,可以将如下情况 1Camera-&rotateYaw(floatyaw), Camera-&rotatePitch(float pitch)重新组织为: 1Camera-&rotate(float yaw, floatpitch);让它们尽量保持简单。过多的函数会让你变得疲于奔命。 考虑哪些函数是需要暴露给用户的(译者:即public函数),哪些函数在系统内部实现即可(译者:私有函数)。例如,你的渲染器需要在绘制之前将对象进行排序,其中排序的操作是不需要暴露的。你需要知道在绘制之前要进行排序,但是你并不需要借助于外部系统。 步骤3:程序构建
从这里开始的内容将会是有趣的并且重要的。
最简单的构建游戏引擎的方法是通过类来实现每一个系统,并且通过一个游戏主循环来调用子程序。看下来会是如下的样子: 1234567891011while(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函数。是一个很好的例子。
有一些重要的事情需要牢记: l保持良好的文档记录。我们开发了哪些函数?这些函数怎样调用,以及何时进行调用? l函数尽量简单。简单的函数会让你在长期的开发过程中保持愉悦,例如:drawMesh(Mesh* oMesh)或者loadShader(StringsPath)。 l保持函数的功能性。不要设计一些特殊的函数,例如:使用drawSpritefunction取代drawButtonSprite。具体功能让用户来决定。
通过上面的介绍,我们可以获得很多: l对框架进行一次配置即可以在我们需要的每一个系统中使用(GUI、渲染系统)。 l我们可以在不对系统进行修改的前提下,有选择的很容易的修改底层API函数。OpenGl和DirectX之间的切换也没有问题。仅仅需要重写框架类即可。 l可以让每一个系统中的代码保持简洁和紧凑。 l如果有一个很好的文档说明,一个人可以在开发框架的同时,另外一个人可以进行系统级别的开发。
经过上述调整,我们的系统看起来是这样的:
将哪些内容放入框架非常简单。如果需要调用一些第三方的库(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。这对于简单的消息已经足够了。更多高级的消息需要包含数据,使用某种方式来存储这些数据。有很多方式可以实现,最简单的实现方式是使用一个简单的映射结构。
但是,我们如何发送消息呢。当然是通过消息总线机制了。
是不是很漂亮?不再有“通心粉”的存在(译者:不再有一大坨的代码了),只有一些扁平的“千层面”(译者:取而代之的是分层系统)。我很小心的将游戏逻辑系统放在消息总线的另一头。正如你所见,它和“框架”层之间是没有关系的。这是非常重要的,对于避免“仅调用一个函数”的情况发生。相信我,你迟早会需要这样处理。但是这会打乱我们的设计。我们已经有足够的系统使用框架进行处理,游戏逻辑系统不需要。
消息总线是一个很简单的类,每一个系统都有一个消息总线的引用。如果在队列中有一个消息,那么消息总线会将这个消息通过一个简单的消息句柄函数发送给每一个系统。与之对应,每一个系统都有消息总线的一个引用,用于发送消息。这种方式可以通过内部变量或者函数传参的方式实现。
所有的系统需要从如下类派生而来:123456789101112131415161718class System{public:
void handleMessage(Msg *msg);
switch(msg-&type)
} private:
MessageBus *msgB
突然,游戏逻辑系统想要让消息总线派发消息。我们需要定期的通过一些更新回调函数来完成不同系统的更新。但是信息会进行不同的处理。
然而,使用框架,并在上层构建消息机制。毫不违心的说,这些都会对引擎的速度有所降低。但是我们并不关心,我们只想要一个干净简单的设计,一个干净简单的架构。
当然,最酷的事情是什么?我们可以很轻松的获得一些神奇的事情!诸位情况: l控制台
每一个消息基本上都是一个函数的调用。并且消息的发送无处不在。假如我们拥有一个可以简单打印每一个发送到输出窗口的消息的系统?假如这个系统同样可以将我们键入的消息进行发送?
是的,我们赋予了控制台活力。它所需要的只是几行代码而已。当我第一次看到这些的时候有所震撼。它并没有绑定任何东西,它本身就存在在那里。 l游戏内建动画,回放以及调试
假如我们需要“伪造”一些信息?假如我们仅仅需要一个在某一时刻发送消息的新的系统?想象跟随一个旋转对象,然后发送一个移动相机的消息。
瞧,我们有内建的游戏动画。
假如我们仅仅需要将游戏发送过程中的输入消息记录下来并且保存为一个文件。
瞧,我们有录制功能。
假如我们仅仅需要记录玩家的一切动作,并且在游戏崩溃时,将这些文件提供给玩家使用。
瞧,我们有玩家所有动作的准确备份来处理崩溃问题。 l多线程
多线程?是的,多线程。我们将所有的系统进行解耦。这意味着,它们可以在任何时候、用任何方式、更重要的是在任何地点来处理这些消息。我们可以让我们的消息总线来决定每一个系统应该在哪个线程中处理消息。这也就是所谓的多线程。 l帧率固定
如果我们在当前帧中有太多的消息要处理,怎么办?没有问题。我们将这些消息放入消息队列,然后交给下一帧处理。这将会让我们的游戏始终保持在60帧左右。玩家无法注意到AI系统花费了过长的时间去“思考”,他们只会注意到游戏帧率降低了。
消息机制相当棒!
为每一个消息和它的参数精细的撰写文档相当重要。把它们当作API接口对待。如果能够正确的做到,每一个开发者可以在不破坏任何事情的情况下在不同的系统上进行开发。即使假如一个系统需要离线工作或者正处于构建过程中,这个游戏仍然可以运行并进行测试工作。没有音频系统?我们还好有可视化系统。没有渲染系统,我们还好有控制台… 不幸的是,消息机制也不是完美的
有时候我们想要知道一个消息的处理结果,有时候我们想要消息马上被处理。我们需要找到一个可行的方案。一个解决办法是开发一条“高速公路”(译者:消息即使处理机制)。它是独立的简单的消息发送函数,我们可以通过postImmediateMessage函数来实现。该函数可以马上处理消息。处理返回的消息相对简单。这些迟早会发送给ourhandleMessage函数。我们只需要在发送时记住该消息即可。
如果过多的处理即时消息显然违背了多线程和帧率固定原则。因此要在使用过程中有所限制。
但是最大的问题是该系统会有延迟。本文介绍的并不是一个以运行速度为主的架构。如果你要开发一个快速响应的第一人称射击游戏,那么这是一个关键的问题。 回头设计我们的架构
我们决定使用系统和消息总线来开发游戏。我们已经完全知道该怎么构建我们的游戏引擎。
是时候进行设计阶段的第四步了。迭代开发。一些函数可能并不适合存在在系统内部,我们必须找到对应的解决方案。一些函数可能会被频繁的调用,这可能会堵塞消息总线,我们必须找到对应的解决方案。
这可能会花费一些时间,但是这在漫长的开发过程中是值得的。
终于要进行编程开发了! 步骤4:从何处开始编程
在你开始编程之前,请阅读RobertNystrom所著的书籍游戏编程模式。
除此之外,我草拟了一个技术路线路供你参考。它可能不是迄今为止最好的办法,但却非常有效。 l如果你要开发消息总线类型的游戏引擎,建议首先开发控制台和消息总线部分。一旦这些功能实现,你可以假设任何系统的存在,即使还没有完成代码编写工作。你将会在开发的每一个阶段对整个引擎进行掌控。 l下一步考虑开发GUI系统,同时考虑框架开发中所需的绘制函数。一个与控制台匹配的稳定的GUI系统可以让你更容易的开发其它系统。测试也变得轻而易举。 l下一步应该进行框架开发,至少先开发它的接口。具体实现函数可以稍后开发。 l最后,进行其它系统的开发。包括游戏玩法。
你会注意到,事实上渲染任何和游戏相关的内容可能总是放在最后去做。这会是一件好事!你会感到值得的,始终保持动力直到完成你的游戏引擎的最终开发。
你的游戏策划可能会在开发过程中打扰你。通过控制指令进行游戏可行性的测试和在IRC上玩反恐精英一样有趣。 结论
花费一些时间来寻找一个稳定的架构并坚持下去!这是我希望你能够从这篇文章中学到的内容。如果你能够这样做,你将会有能力在一天结束的时候构建起一个完美的、可维护的游戏引擎。否则,要花费一个世纪。
就我而言,我更愿意开发游戏引擎而不是做游戏相关的事情。如果你有任何问题,随时欢迎联系我。我正在使用本文介绍的方法来完成另外一个游戏引擎。
请在此阅读。
关于版权: 已获得作者授权,如下:
下载次数:10
分类:程序新手圈
请勿发表无意义的内容请勿发表重复内容请勿发表交易类内容禁止发表广告宣传贴请使用文明用语其它
淫秽色情政治倾向人身攻击抄袭剽窃广告刷屏恶意挖坟冒充他人其它
登录后参与讨论。点击}

我要回帖

更多关于 从零开始学app开发 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信