搜档网
当前位置:搜档网 › 游戏编程入门

游戏编程入门

游戏编程入门
游戏编程入门

游戏编程入门(by David Astle)

经常有人问我,没有编程经验的人该如何开始开发游戏。在此之前,我总是一个个的尽力回答。然而,后来提相同问题的人数增长到难以处理的地步。我决定,是时候把我所有的建议写成文章,作为一个大概。

这篇文章是针对那些想要开发自己游戏,但几乎没有编程经验的人。事实上,我假设读者没有任何编程经验。我主要讨论游戏开发的程序和设计方面,而不是艺术性。我也不准备讲述如何进入游戏行业(这方面已经有足够的资料),而只是让你逐步的开始开发自己的游戏。最后,我所指出的这条道路也并不能作为唯一的,或是最好的路径来学习开发游戏,但至少对我和一些人很有用。

选择一门语言

你要做的第一件事就是选择一门开发语言。你有很多选择,包括Basic,Pascal,C,C++,Java,等等。也经常会有人争论对于初学者那一门语言是最好的。对于这一系列流行语言的讨论,你可以参看John Hattan 的著作,What Language Do I Use?(我用什么语言?)

我的建议是以C和C++开始。有些人会说这些语言对初学者来说太高级了,但因为我自己就是学C++,我并不同意这一说法。而且,C/C++是当今使用最广泛的语言(译者认为应该是汉语。。。),所以你可以找到大量学习资料和帮助。你先学C或C++都无所谓,因为只要学了一个,再学另外一个就很容易。但是,如果你先学C++,请保证在学习面向对象编程之前能理解和使用过程编程(等编程熟练再去学习类)。(译者:C是过程性语言,C++是面向对象语言)

如果你开始学习C/C++,发现太难,那再学一个简单一点的也没关系,比如Basic或Pascal。但是我真的认为,如果你坚持努力,而且有好的资料,学C/C++应该没有太大问题。

你的下一个问题可能会是:“我该怎么学C/C++?”我很高兴你这样问。最好的办法是上课。有老师可以回答你的问题,帮助你产生很大进步,编程练习作业也可以保证你能用到所学的东西。

如果你不觉得上课是个好主意,那最好的办法就是买一些好书。不要花太多时间去选一本什么“超级宝典”或“万用全书”,因为你最终可能会买几本。我建议你去一家书店,然后拿几本比较入眼的C或C++书看,直到找到一本或几本你能看懂,并且可以拿来学习的。同时,你可能会想要一些更深入的,或者一些材料,但是你一旦对于这门语言有了一些了解,我相信你应该有自己更好的选择。

在这里,我有必要花一些时间,来说我看到很多初学者所关心的一个事情,特别是年轻人:没有钱买书和其他东西。首先,有很多免费资源可以利用,图书馆,Macmillan Computer Publishing,有成千上百的编程书籍。但是如果你真的想要成为一个好的程序员,还是应该投入一部分资金。应当想方设法(合法的)帮助你弄到一些钱。

网上也有很多C/C++的学习指南。但是我认为那只能作为补充而不是你自学的主要资源。

选择正确的编译器

你写的程序,或者代码,是以文本方式储存的,你甚至可以用记事本写C/C++程序。但是总需要有东西把他们转换成为可执行文件。对于C和C++,那就是编译器。

可用的编译器有好多种,包括很多免费的。选择一款自己适合的编译器很重要,免费的编译器就有这样的好处,你可以把它们试个遍,然后从中选择自己最喜欢的。然而,免费编译器比起商业版,可能会缺失一些功能和大部分服务。幸运的是,多数商业版编译器也兼售介绍版或学习版,这要便宜得多,通常功能却不见得少,唯一的限制是你不能发布用它编译的程序(短时间内你也根本用不着)。

总之,选择编译器取决于你能花多少钱,用什么操作系统,和为什么平台开发。如果要为windows开发,我强烈推荐Microsoft Visual C++。他强大的开发环境使得很多事做起来更方便,毫无疑问没有其他编译器更适合开发windows应用程序。如果你是个学生,你还可以折价买到。(译者:爽!)如果你在DOS 平台开发,你最好的选择可能是DJGPP,免费的哦~。

选择开发平台

尽管最终你很可能为好几个平台开发,总要先选择一个来学。当你在学这门语言,还没有接触到图像的

时候,你可能会想使用非图形用户界面的操作系统,比如DOS,Unix.这样可以避免接触高层,比如windows 编程,让你集中精力学习语言本身。

一旦你做好开发游戏的准备,那么,应该考虑是否改变平台,让我们来看看每个选项的特征。

windows:如果你想成为游戏行业的专家,或者如果你想让许多人来玩你开发的游戏,那么,这就是你要选择的平台。因为多数你的用户使用windows,而且现在我也看不出有什么改变的迹象。当今大多数的windows游戏都是由一种你可能听说过技术---DirectX---开发的。你可以DirextX这个库直接访问硬件,这意味着你可以开发高性能的游戏。

DOS:DOS过去是占统治地位的游戏平台,但是已经一去不复返了。尽管可能有一些特殊爱好者还在为DOS开发游戏,现在没有一个为DOS开发的商业游戏,他也将继续衰落,直到微软不再支持。如果你只是想开发游戏,还是不要选择DOS,如果你非要这么做,也不要太久。记住:由于存在大量DOS游戏开发的书,可能还有人辩护从这些书中学习DOS游戏开发。但是,windows游戏开发的书越来越多,那些辩解也变得越来越无力。

Linux:Linux是Unix的一种,由于很多原因后来变得流行,包括稳定性,价格,和反微软情绪。尽管Linux用户还是相当少,但是围绕着他的热情和不断增长的市场潜力使其也成为不错的选择。

Macintosh:MAC有大量忠实粉丝并不能说明什么,几乎每一个和我讨论的MAC狂热者都需要更多更好的游戏。我没有见过多少MAC游戏开发资源,但我相信还是有的,因此这也是一个选择。

consoles:console(就是PS,N64,DC等等)的游戏市场十分巨大,当然console游戏开发的前景也不错。然而以非商业的形式开发console游戏,出于各种原因,在现在似乎行不通。如果你开发console,很可能是在被商业游戏开发组雇用之后。

开始进入主题

现在是时候讨论开发游戏了。简单起见,我假设你选择用C/C++在windows平台开发,可能你选择别的,但大多数我说的还是有用的。

首先,甚至在你打算开始开发游戏之前,你必须很好掌握C和C++.你应该懂指针,数组,结构体,函数,可能还有类。如果你精通他们,就可以开始做游戏了。

这篇文章可能不能教会你所有关于开发游戏的东西。幸运的是,也没有这个必要。这方面有很多书,网上也有很多指南。https://www.sodocs.net/doc/22285276.html,应该有所有你想要的东西,这里我建议你怎么开始:找一本或几本书。对于windows游戏开发初学者,Tricks of the Windows Game Programming Gurus是个完美的开始。除此之外,还有许多好书.读透这些书,试试所有的例子,不懂得部分多读几遍。

网上指南作为补充。除了弄清书上读到的,他们还涉及一些书上没有谈及的主题。

向专家寻求帮助。如果你不能在书上或指南找到问题的答案,好好利用我们的论坛和聊天室。那里有好多专家愿意帮助别人。

这并不是要你按照顺序执行的,而是可以同时并且不断的重复执行。

光学习还是不够的,你必须运用你所学到的。从一个小游戏开始,然后在这个基础上前进。你可以看一下Geoff Howland's的著作:How do I Make Games?A Path to Game Development.

开始,自己独立工作。不要急着加入团队,那样只会减缓学习过程。而且一旦你有了自己的几个游戏,你可以为团队做出更大的贡献。

还有关于书,你应该不仅仅看游戏开发的书。为了能够开发出你看到商店出售的游戏,你应该钻研比大多数游戏开发书籍更高级的一些主题。有一些可以在网上找到,但你也应该选一些图形学,人工智能,网络,物理学等方面的书。计算机科学学位看来唾手可得,但因为你被迫上这些课的时候可能认为他们和游戏开发无关----你错了!

包装

这里有一些提示很有用

不要只积累知识,用它

你永远不会知道会理解一些东西知道你是用他们。用你学的东西作些演示。做作书上的练习。

多玩游戏

这样做会使你做出更好的游戏。而且可以减轻编程的枯燥。

帮助别人

能帮别人的地方尽量帮助别人,教别人的过程中自己会学到更多。

有始有终

不要有这样的想法:“我能够完成这个游戏了,但是我又有一个新的想法,那就直接做下一个。”你可以学得更多如果你完成他,你也可以用事实证明你不是只会空谈。所以,尽量不要做很大很复杂的游戏,直到你有了一定经验。

开始吧!你现在可以开始准备QUAKE4了。可能你不是不是很了解,但是至少应该知道如何开始这条道路,找多些资料,加上多年努力工作,他一定会实现!

MMORPG开发入门

原著:Radu Privantu

翻译:pAnic

2005年5月11日

原文出处:A Beginner’’s Guide to Creating a MMORPG

-------------------------------------------------------------------

译者序:这是一篇讲解如何开发一款MMORPG的入门文章,作者本人也是一款游戏的开发者,文中的内容源于实践,有很高的参考价值。很多人都想拥有自己的游戏,这篇文章对那些想自己开发游戏的人来说可能是一纸福音,也可能是一盆冷水。无论如何,开发游戏都不是一件简单的事情。以下是翻译正文:

-------------------------------------------------------------------

文章的中心是如何起步开发你自己的大型多人在线角色扮演游戏(原文:Massive Multiplayer Online Role Playing Games)(MMORPG)(译者注:俗称:网络游戏,网游)。针对的读者是经验和资源有限的开发者。读完文章之后,你应该懂得如何起步,还有一些关于什么是应该做的和不应该做的忠告。第一步是评估你的能力和资源。你必须对自己诚实,因为做你力不从心的事情会浪费你的时间并让你心灰意冷。

第一步:评估你的能力

必须的技能:

懂至少一种编程语言。迄今为止,C++因为性能和效率的优越性成为游戏开发者的首选。Visual Basic, Java或者C#可能也是不错的选择;

熟悉一种图形库。通常的选择是SDL,OpenGL,或者DX/D3D。(译者注:网上也有很多免费/付费引擎下载和出售);

选择一种网络通讯库。你可以从WinSock,SDL_net,或DirectPlay中选择。(译者注:很多人喜欢开发自己独特的网络库,这并不复杂,似乎ACE也是一种选择);

对游戏开发有大体的经验。例如,事件循环,多线程,GUI设计,等等。

强烈推荐的技能:

C/S结构通讯;

多平台开发。你可能希望设计一个MMORPG,尤其是服务器能运行在多种操作系统。为此,我推荐使用SDL,OpenGL和SDL_net;

网站开发。如果你想让用户通过网站查看玩家统计,服务器信息和其他信息,这是必须的。(译者注:其实网站可以交给其他人开发,如果有必要的话);

安全管理。你当然不想因为有人攻击你的服务器而浪费时间!

团队组织能力。你需要一个你能成功领导和管理的团队;

第二步:初步规划

我注意到很多人在不同的论坛发帖子寻找团队开发MMORPG。他们中的大部分是这样:“我们成立了一个公司/游戏工作室,需要3个美工,两个程序,1个音乐制作,等等。为了创新,不要看过去的MMORPG,你有全部的自由用来创造你想要的世界,等等。我们会在项目完成并赚到钱的时候付给你酬劳,等等”。不幸的是,以现有的技术和带宽,你无法拥有一个动态的世界。朝向无法到达的目标前进只会导致失败。正确的做法是拿出一些小规模的,功能性强的,可扩展的设计和构架。,

基本软件构架

首先,尝试创建一个简单的C/S模型,有如下功能:

创建一个新角色;

保存那个角色(服务器端);

用那个角色登陆;

能够和其他人交谈;

能在3D空间游览;

保存角色看起来简单,其实不然。例如,有两种方式保存角色:使用数据库服务或者使用文件。两者有各自的优缺点:

请点击查看详细优缺点对比

现在你决定了如何存储角色,你还得选择C/S通讯的网络协议:TCP还是UDP?,我们都知道TCP 速度慢,但是更准确,并且需要额外带宽。我实际使用TCP并没有遇到什么问题。如果你有充足的带宽,TCP是个好选择,至少对初学者是这样。UDP会很麻烦,尤其是对新手。记住,游戏或引擎的初步测试会在你的局域网进行,所有的包都会按顺序依次抵达。在Internet上无法保证这一点。虽然包会按顺序到达,但是有时候会丢包,这通常是个麻烦事。当然,你可以设计你的协议使得C/S能够从丢包中恢复。但这对初学者来说很痛苦,不值得推荐。

第三步:选择数据传输协议

又是看起来很简单,其实不然。你不能只是发送’’\0’’结尾的串。因为你需要一个通用的协议,能同时适用字符串和二进制数据。用0(或其他字符)做结束符是不明智的,因为那个结束符可能是你要发送的数据的一部分。此外,如果你发送20字节,然后再20字节,服务器极有可能收不到两个20字节的包。取而代之的是,它会一次性收到40字节,为了避免浪费带宽在不必要的头上。而且,你可以发送1KB的包,但服务器会以两个小包的形式收到它。所以你必须知道哪里是一个包的开始,哪里是结束。在“永恒大陆”(译者注:原文:Eternal Lands,本文的作者正在开发的一款MMORPG)中,我们用如下的方法:

Offset0:1字节表示传输的命令;

Offset1:2字节,传输的数据长度;

Offset3:变长,消息内容;

这种方法有一致的优点:所有的数据传输有统一的标准。缺点是有些命令有固定已知的长度,浪费了一些带宽。以后我们会改成混合的方法。

下一件事是决定服务器模型:“非阻塞soket,不使用线程”,或者“阻塞soket,使用线程”。两种方法(使用线程vs不使用线程)各有优缺点。

线程:

服务器响应会更加平滑,因为如果一个玩家需要大量时间(例如从数据库中读取数据),这会在它自己的线程中完成,不会影响其他人。(译者注:也许作者的意思是每个玩家都有独立的线程,但这对MMORPG 不太现实);

难以恰当的实现和调试:你可能需要大量同步,并且一个小疏忽就会导致灾难性的后果(服务器瘫痪,物品复制,等等);

可以利用多处理器;

无线程:

实现和调试更简单;

响应速度慢;

在我的公司,我们使用无线程的方法,因为我没有足够的资源和人力处理线程模式。

第四步:客户端

你打算做2D还是3D游戏?有些人认为2D游戏做起来简单。我两者都做过,并且我倾向于3D游戏更简单。容我解释。

2D下,你通常有一个帧缓冲,也就是一个巨大的象素点数组。象素点的格式会因显卡的不同而不同。有些是RGB模式,另一些是BGR模式,等等。每种颜色的bit数也会不同。只有在16bpp模式才有这个问题。8-bit和24-bit模式简单一些,但有他们各自的问题(8-bit颜色数太少(256),而24-bit速度更慢)。同时,你需要制作你的精灵动画程序,不得不自己排序所有对象,以便他们以正确的顺序绘制。当然,你可以用OpenGL或者D3D制作2D游戏,但通常这并不值得。并不是所有人都有3D加速卡,所以使用3D库开发2D游戏一般会带给你两者的缺点:不是所有人都能玩,你也不能旋转摄像机,拥有漂亮的阴影,和3D 游戏炫目的效果。

(译者注,目前绝大部分显卡都支持565的16bpp格式,这个也成为目前16位色的业界通用格式,有不少文章和代码都是讲述这一格式下图像处理的,尤其是使用MMX技术)

3D的途径,正如我所说,更简单。但是需要一些数学(尤其是三角)的知识。现代的图形库很强大,免费提供了基本的操作(你不需要从后到前排列对象,改变物体的色彩和/或帖图都十分简单,对象的光照会按照光源和它的位置计算(只要你为它们计算了法向量),还有更多)。并且。3D给了你的创作和运动更多的自由度,缺点就是不是所有人都能玩你的游戏(没有3D卡的人数可能会让你大吃一惊的),并且,预渲染的图片总是比实时渲染的更漂亮。

(译者注:市面上想买不支持3D的显卡目前很困难,只是高性能的3D卡价格也不低)

第五步:安全

显然,不能相信用户。任何时候都不能假设用户无法破解你精巧的加密算法(如果你使用了的话)或者协议,用户发送的任何信息都要通过验证。极有可能,在你的服务器上,你有固定的缓冲区。例如,通常有一个小(可能是4k)缓冲区用来接收数据(从soket)。恶意用户会发送超长数据。如果不检查,这会导致缓冲区溢出,引起服务器瘫痪,或者更坏的,这个用户可以hack你的服务器,执行非法代码。每个单独的消息都必须检查:缓冲区是否溢出,数据是否合法(例如用户发送“进入那扇门”,即使门在地图的另一端,或

者“使用治疗药水”尽管用户没有那种药水,等等)。我再次强调,验证所有数据非常重要。一旦有非法数据,把它和用户名,IP,时间和日期,和非法的原因记录下来。偶尔检查一下那个记录。如果你发现少量的非法数据,并且来自于大量用户,这通常是客户端的bug或者网络问题。然而,如果你发现从一个用户或者IP发现大量非法数据,这是明显的迹象表明有人正在欺骗服务器,试图hack服务器,或者运行宏/脚本。同时,决不要在客户端存储数据。客户端应该从服务器接收数据。换句话说,不能发送这样的消息“OK,这是我得物品列表”或者“我的力量是10,魔法是200,生命值是2000/2000”。而且,客户端不应收到它不需要的数据。例如:客户端不应该知道其他玩家的位置,除非他们在附近。这是常识,给每个人发送所有玩家会占用大量带宽,并且有些玩家会破解客户端从中获取不公平的利益(像在地图上显示特定玩家的位置)

(译者注:就像传奇的免蜡烛外挂)。所有这些似乎都是常识,但,再次,你会惊奇的发现有多少人不知道这些我们认为的常识。

另一个要考虑的问题,当涉及到安全:玩家走动的速度必须在服务器计算,而不是客户端。

(译者注:这是重要的原则,但是会耗费大量服务器资源。魔兽世界没有这样做,它采用类似其他玩家揭发的形式掩盖这个事实,导致加速外挂可以用,但是在有其他玩家的时候会暴露)。

服务器应该跟踪时间(以ms为单位)当客户最后一次移动的时候,并且,移动的请求如果比通常的极限更快到来,这个请求应该被抛弃。不要记录这类虚假请求,因为这可能是因为网络延迟(也就是玩家延迟,过去的10秒内发送的数据同时到达了)。

检查距离。如果一个玩家试图和100亿公里以外的玩家交易(或者甚至在另一张地图上),记录下来。如果一个玩家试图查看,或者使用一个遥远的地图对象,记录它。小心假的ID。例如,正常情况下每个玩家都会分配一个ID(ID在登陆的时候分配,可以是持久的(唯一ID)。如果ID在玩家登陆的时候赋予9或怪物被创建的时候),显然可以用玩家数组(保存玩家)的位置(索引)作为ID。

所以第一个登陆的玩家ID是0,第二个是1,依此类推。现在,通常你会有一个限制,比如说2000个索引在玩家列表里。所以如果一个客户端发送一条命令类似:“查看ID200000的角色”,这会使服务器当机,如果没有防备的话,因为服务器会访问非法的内存区域。所以,一定要检查,就像这样:"if actor id<0or if actor id>max players然后记录非法操作并且断开玩家。如果你使用C或者C++,注意或者定义索引为’’unsigned int’’并且检查上限,或因为某些原因定义为int(int,默认是有符号的),记得检查<0and>max。没有做这些会严重挫伤你和其他用户。类似的,要检查超出地图坐标。如果你的服务器有某种寻路算法,并且客户端通过点击地面来移动,确保他们不要点击在地图外部。

第六步:获得一个团队

制作游戏需要大量的工作(除非是个Pong and Tetris游戏)。尤其是MMORPG。你无法单靠自己。理论上,一个完整的团队组成是这样:

至少3个程序员:1个做服务器,两个客户端(或者一个客户端,一个负责工具,例如美术插件,世界编辑器,等等)。有6个程序员是最好的,更多就没必要了。这取决于你的领导能力。最少一个美工,2到3个更合适。如果这是个3D游戏,你需要一个3D美工,一个2D美工(制作帖图,界面,等等),一个动画师,和一个美术部负责人。美术部应该由有经验的人组织和安排,除非你就是个艺术家。

少数世界构建者:创建所有地图是个漫长的过程,并且直接关系到游戏的成败。再次,你需要一个世界构建部的负责人。你的世界需要协调一致,所以不能只有一个意气用事的人。

一个网站管理员是必须的,除非你精通网站设计,并且愿意花时间做网站。音效和音乐不是必须的,但是有音效和音乐的游戏比没有的会更吸引人。

一个游戏经济系统设计师.。你也许觉得那很简单,可以自己来做,但事实上那是最复杂的工作之一。如果经济系统设计不良(比如物品没有平衡,资源在地图上随意放置,等等。)玩家会觉得无聊并且退出游戏。我们早期的进展存在很大的问题,尤其是因为经济系统主要是由我(一个程序员)设计的,它没有被恰当的计划。于是,我们花费了两个月来重新思考和建立一整个新的经济系统。这需要一次完全的物品清除。我告诉你,玩家会很不乐意你删除他们的物品。幸运的是,大部分玩家赞同这个想法,但是这么多小时的争论,妥协,解释和时间的浪费还是让我们丧气。以后会更多。

如前所说,你需要一个10~15人的团队,不包括协调员和管理者。这10~15人必须是有经验的。如果都是新手就不值得,因为你需要花大量时间解释要做什么,怎样做,为什么他现在的做法不好,等等。

一开始就凑齐10~15人几乎是不可能的。不管你在不同的论坛发多少帖,你也无法找到合适的团队成员。毕竟,如果一个人熟练于他/她的领域,为什么在你无法拿出任何东西的时候他/她要加入你的团队?很多人有远大的想法,但是实现它们需要大量时间和努力,所以他们宁可从事自己的工作也不会加入你。那如果你需要10~15人,但是无法让他们加入你的团队,你如何才能制作一款MMORPG呢?好,事实上,你一开始不需要所有人都到位。你真正需要的是一个程序员和一个美工。如果你是个程序员,只要找个美工就可以了。请求懂美术的朋友帮忙,花钱请大学生/朋友做一些美术或者其他工作。

现在你有了一个美工,你期待的游戏的样子,现在可以开始实现了。一旦你有了可以运行的C/S引擎,一些用来展示的截图(或者更好,玩家可以登陆你的世界,四处走动,聊天),更多的人会愿意加入你的团队。更恰当的是,除非你使用独有的技术,否则你的客户端可以开源。许多程序员会加入(作为志愿者)一个开源工程而不是非开源项目。而服务器不应该开源(除非你打算做一款完全开源的MMORPG)。

其他一些忠告:在有东西可展示之前,不要夸大你的游戏。最惹人烦的事情之一就是一个新手发一个“需要帮助”的请求,要求一个巨大的团队加入他的游戏制作,解释这个游戏到底有多酷。一旦你拥有了网站广告(通常是在一个免费主机),你会看到一个吸引人的导航条,包含“下载”,“截图”,“原画”(译者注,原文:Concept art,概念艺术,在游戏应该指美工的原始设计),“论坛”。你点击下载链接,然后看到美妙的“建设中”页面(或者更糟糕,一个404错误)。然后你点击截图,得到同样的结果。如果你没有东西给人下载,就不要放下载链接。如果没有截图展示,不要放截图链接。然而更好的是,在工程进展10%(程序和美工)之前,不要浪费时间在网站上。

第七步:打破某些神话

你无法制作MMORPG,只有大公司才可以。

我不同意。虽然制作一款像魔兽世界(World of Warcraft),无尽任务2(Ever Quest2),亚瑟王的召唤

2(Asheron’’s Call2),血统2(Lineage2),和其他一些游戏对一个小的自发团队是不可能的,但是做一款像样的游戏还是可以的,只要你有经验,动机,和时间。,你需要1000小时的编程来制作一个可运行的测试版,

大概10~15k小时完成几乎完整的客户端和服务器。。但是作为团队领导者,你不能只编程。保持团队团结,解决争执,维护公共关系(PR),技术支持,架设服务器,惩罚捣乱分子,自由讨论,等等都是你的职责。你可能会被非编程的任务淹没。你很可能需要上班/上学,这减少了你花费在项目上的时间。我们很幸运,没有成员离开团队,但是如果这种事情发生,那的确是大问题。假设你的美工半途离开。或者更糟糕,他/她没有给你使用他/她作品的许可。当然这可以通过和他们签订合同来解决,但找另外一个美工仍然很麻烦。一个工程中有两种不同的美术风格也是问题。

需要大笔金钱(通常4-6位数)用来架设一个MMORPG服务器.

当然,这不是真的。我见过专业服务器,1000GB/月,不到100美元/月(2~300美元的初装费)。除非你的数据传输协议设计非常不合理,1000GB/月对一个1000玩家在线(平均)的服务器来说足够了。当然,你还需要另一个服务器做网站和客户端下载(客户端下载会占用大量流量,当游戏变得流行的时候)。我们的客户端有22MB,有时候会有400GB/月的传输量。而我们还没有很流行(仍然)。另一件事,我们不需要另一台专用服务器开启这个工程。ADSL/cable服务器可以胜任,直到你的同时在线人数达到20~30。然后要么找一个友好的主机公司,用广告交换免费主机,要么就只能自己掏腰包了。

制作一个MMORPG很有趣。

这不是真的。你可能认为每个人都会赏识你,玩家会支持你,你标新立异,并且,当然,很多玩家都玩你的游戏。玩家可能让人讨厌。即使是完全免费的游戏,他们也能找到理由抱怨。更糟糕的是人们经常会抱怨矛盾的事。战士会抱怨升级太难,商人会对战士掠夺大量钱财很失望。如果你减少怪物掉落物品,有些玩家就会威胁说要退出游戏。如果你增加,同样的一群人会不满新手能更简单赚钱的事实。真是左右为难。改革和改进是必须的。如果你决定改变某些东西,例如给加工物品增加挑战性,有些人会说太难了。如果你不做,他们又会说太简单无味。你会发现满意的玩家通常不会说什么并且感到满意,同时破坏者会怨声载道。

MMORPG的经济比单机版难以平衡的多。在单机游戏,你可以逐渐改良武器,只要玩家进展,他/她可以使用更好的装备,丢弃(或者卖掉)旧的。另一方面,在多人游戏里,这种观点不成立,因为每个人都试图得到最好的武器,而跳过低等级武器。大部分玩家宁可空手省钱,直到他们能买游戏中最好的武器。经济系统设计要参考相关的文章。

迄今为止我列举的所有事情,加上额外的工作和挑战,足以让你在决定涉足这个工程之前三思而行。你必须知道你的决定意味着什么。

总结

希望这篇文章能给你足够的知识。我的下一篇文章将介绍如何建立一个经济系统(更明确的,要避免哪些错误),还有一些调试服务器和客户端的信息。

关于作者

这篇文章作者是Radu Privantu,永恒大陆(Eternal Lands)https://www.sodocs.net/doc/22285276.html,的主程序和项目规划,永恒大陆是一款免费,客户端开源的MMORPG。作者可以通过chaos_rift@https://www.sodocs.net/doc/22285276.html,联系。

斜45度游戏开发(一)

作者:Flysky2005

注:写的十分粗糙,如果看不懂请对照KgameV1.0源代码浏览。

最好的游戏末过于RPG游戏了,但如果赢得大众的好评,那么必须要采用(甚至说现在是一种标准)斜45度地图、人物游戏引擎,下面我们分别展开分析。

一.地图数据结构

Soft的《圣剑英雄传二》定义的就不错,可以参考它的。

定义地图结构以前,我们要先定义Tile结构,分两种情况。

(1).物品、景物等按NPC处理(不规则处理,如“魔力宝贝”):

typedef struct{

bool IsGround;//是否显示地表

short GroundPicNum;//地表图片页面编号(0~59),,,(0-55)页为静态,(56~59)页为动态

short GroundPicX;//地表材料在地表图片上的横坐标(以格子为坐标)

short GroundPicY;//地表材料在地表图片上的纵坐标(以格子为坐标)

short Block;//阻碍标志(ID_BLOCK_T,ID_BLOCK_F)

short Hook;//陷阱标志(ID_HOOK_F,ID_HOOK_T…………)

char HookScriptName[28];

char Reserve;//保留位,(我估计我们肯定还有想不到的一些信息,以后可以在这里添加,以免地图编辑完成之后,再修改cell结构时可避免重新编辑地图文件)

}stCell;

具体我也不说什么了,注释都很清楚!

(2).物品、景物也按Tile处理但NPC按不规则处理(如:“仙剑奇侠转”,强烈不推荐!)

typedef struct{

bool IsGround;//是否显示地表

short GroundPicNum;//地表图片页面编号(0~59),,,(0-55)页为静态,(56~59)页为动态

short GroundPicX;//地表材料在地表图片上的横坐标(以格子为坐标)

short GroundPicY;//地表材料在地表图片上的纵坐标(以格子为坐标)

bool IsObject1;//是否显示物品1

short Object1PicNum;//地表图片页面编号(0~59),,,(0-55)页为静态,(56~59)页为动态

short Object1PicX;//物体材料1在物体图片上的横坐标(以格子为坐标)

short Object1PicY;//物体材料1在物体图片上的纵坐标(以格子为坐标)

bool IsObject2;//是否显示物品2

short Object2PicNum;//地表图片页面编号(0~59),,,(0-55)页为静态,(56~59)页为动态

short Object2PicX;//物体材料2在物体图片上的横坐标(以格子为坐标)

short Object2PicY;//物体材料2在物体图片上的纵坐标(以格子为坐标)

short Block;//阻碍标志(ID_BLOCK_T,ID_BLOCK_F)

short Hook;//陷阱标志(ID_HOOK_F,ID_HOOK_T…………)

char HookScriptName[28];

char Reserve;//保留位,(我估计我们肯定还有想不到的一些信息,以后可以在这里添加,以免地图编辑完成之后,再修改cell结构时可避免重新编辑地图文件)

}stCell;

太清楚了,我也不解释了!

下一步就是地图了,地图的处理不管是什么方法大同小异.

int ID;//地图编号

char Name[32];//地图名称(地图文件名)

int Width;//宽度(以格子为坐标)斜

int Height;//高度(以格子为坐标)斜

int MapStartX,MapStartY;//左上角坐标

stCell**Cell;//动态格子

char FileName[32];//当前地图文件名

char ScrFName[32];//地图初始化脚本文件名

char Reserve[4];//保留位,(我估计我们肯定还有想不到的一些信息,以后可以在这里添加,以免地图编辑完成之后,再修改Map_struct格式,破坏原先编好的地图文件)

//这里先不用看,到了那里再说。

LPDIRECTDRAWSURFACE7lpDDS_MapBack;//地图临时保存点

LPDIRECTDRAWSURFACE7lpDDS_TMouse;//Tile鼠标

int MapBx,MapBy;//用来优化机器

这样不就介绍完了吗?简单吧,不过后面的越来越难,做好心理准备。

二.坐标转换

各大文章在这里都快讲疯了,我不想说推理方法了,你记住以下函数就可以:

//斜45度的坐标转换成屏幕坐标

inline void MIToMD(int Dx,int Dy,int&Ix,int&Iy)

{

Ix=(TileWidth>>1)*(Dx-Dy);//转换为绝对坐标x

Iy=(TileHeight>>1)*(Dx+Dy);//转换为绝对坐标y大菱形

}

//屏幕坐标转换成斜45度的坐标

inline void MDToMI(int Ix,int Iy,int&Dx,int&Dy)

{

Dx=int(0.5*((Iy<<1)+Ix)/(TileWidth>>1));

Dy=int(0.5*((Iy<<1)-Ix)/(TileWidth>>1));

}

#define TileWidth32//每个Tile的宽

#define TileHeight16//每个Tile的高

三.绘制地图

绘制地图有什么难的,错!地图绘制好了FPS能提高数倍。

传统的地图绘制方法:

for(int i=0;i

{

for(int t=0;t

{

绘制。。。。。

}

}

经过改进我这样做

if(MapBx!=x&&MaxBy!=y)

{

for(int i=0;i

{

for(int t=0;t

{

绘制。。。。。

更新MaxBx和MaxBy

备份地图(存成图片)

}

}

}

else

{

还原地图(取出图片)

}

这样空闲状态的FPS就会提升,什么?还想提升,这样办吧!

推理方法从略(其实我也不记得的,春天写的):

#define MapDMX int(0.5*((ScreenHeight<<1)+ScreenWidth)/(TileWidth>>1))+1//最大画出点X #define MapDMY int(0.5*(ScreenHeight<<1)/(TileWidth>>1))+1//最大画出点Y

#define MapDSY int(0.5*(-ScreenWidth)/(TileWidth>>1))-1//最小画出点Y(特殊)

if(MapBx!=x&&MaxBy!=y)

{

for(int Y=IntSizeL(MapStartY+MapDSY,0);Y

{

for(int X=IntSizeL(MapStartX-1,0);X

{

绘制。。。。。

更新MaxBx和MaxBy

备份地图(存成图片)

}

}

else

{

还原地图(取出图片)

}

其中IntSizeL取两个数大的,IntSizeS取两个数小的!

速度优化到极限了,好了,先写到着,下一次我再说遮挡。

斜45度游戏开发(二)

作者:Flysky2005

上一篇我们讲了地图的结构和地图的基本的绘制,这个属于较简单的步骤,我做这些也不是特别的麻烦,如果你是老手,那么1天对你来说已经足够,我们这一组文章也没有讲任何的优化技巧,如果你想优化,还是需要在实际中摸索,当然,我有时间也会写的。

这一篇我们主要研讨地图的物品、景物、NPC建立,并说一下遮挡的简单实现方法。

NPC\主角结构:

主角和NPC在一起比较好,也便于管理(再次声明:作者已经不使用这些方法,这些方法只适用于初学者!),结构如下:

struct stRoleC

{

//判断

bool IsNPC;//是不是NPC

//----------Role基本--------------

char*Name;//NPC名字

bool VS;//是否有这个人物(是否可见)

int x,y;//人物的XY坐标值

RECT roler;//人物矩形

int face;//NPC面向的方向0.下1.上 2.左 3.右char*facePic;

//----------属性值-------------

int HP,MP;//目前的HP,MP

int MaxHP,MaxMP;//满HP和MP

int Level;//目前的等级

int Exp;//目前的经验

int MaxExp;//满血的经验

//----------寻路相关--------------

bool move;

int movex,movey;//移动目标点

//----------NPC相关---------------

char*EScrFName;//当触发人民的脚本文件名

};

struct stRole

{

//判断

bool IsNPC;//是不是NPC

//----------Role基本--------------

char Name[32];//NPC名字

bool VS;//是否有这个人物(是否可见)

int x,y;//人物的XY坐标值

int oldx,oldy;//人物上一步的XY值

RECT roler;//人物矩形

POINT PicS;//距像素坐标的偏移

int face;//NPC面向的方向0.下1.上 2.左 3.右bool facejd;//上一步的站向

char facePic[64];

int facej;//NPC方向的脚步0123

vectorzd;//遮挡列表

//----------属性值-------------

int HP,MP;//目前的HP,MP

int MaxHP,MaxMP;//满HP和MP

int Level;//目前的等级

int Exp;//目前的经验

int MaxExp;//满血的经验

//----------寻路相关--------------

bool move;

vectorPath;//各个步数

int PathSteps;//步数

int PathCSteps;//已经走过步数

int movex,movey;//移动目标点

//----------NPC相关---------------

char*EScrFName;//当触发人民的脚本文件名

bool WalkLoop;//是向前走还是倒退

//----------Surface--------------

LPDIRECTDRAWSURFACE7lpDDS_ROLEP;//Role的图片

LPDIRECTDRAWSURFACE7lpDDS_ROLEL;//Role的logo

};

这两个结构其中stRole是内部储存的结构,而stRoleC是对外的接口,其实你完全不必那样,用一个也可以,具体的完全随你便。

注意:以下的代码和KGameSrc一点关系也没有。

景物的建立:

struct st_scene

{

int x,y;//房子等东西右下角的坐标

int Width,Height;//物品的大小,利用这个可以选出房子等左上角的坐标

LPDIRECTDRAWSURFACE7lpDDS_SCENEPIC;//房子等的图片

unsigned short b_isablock;//0:不阻碍1:全部阻碍2:使用阻碍表

struct{unsigned int data;}**v_block;//阻碍表,比如房子,门那里是不遮挡的

//注意:因为我们构件的是<<魔力宝贝>>式,所以陷阱并不是归这里

};

我也注释了,应该很好看吧。

遮挡问题:

这个确实比较棘手,如果是单格遮挡,可以看云风的遮挡算法,可我们是多个格的,怎么办呢?

根据几位前辈的讨论,他们想出了使用X+Y算法(画家算法),但当时不是很成功,我就做一下补充。

首先,把地图上的一切东西(不管是物品还是主角)串成一个链表(遮挡链表),然后根据X+Y的值有小到大(左上到右下)排列,最后绘制,这样就完成了,很简单吧(哪位可以帮我画一下图,感激不尽.)

优化方法也有:那就是这个景物的排列让地图编辑器排列,然后这个不动了,光人物在链表中的位置移动,这样就完成了快速遮挡。

好了,基本的东西我也写完了,由于现在我在做操作系统,语言说的太急,下一次我会把更好的优化代码和例子程序给大家,谢谢大家观看!

脚本系统的实现

作者:Flysky2006

终于又有时间写文章了,这回我们要实现一个简单的脚本系统(很简单的)!

说实话,原来《电脑报2004年合订本》有个很好的直角90度游戏开发系列,里面的游戏脚本系统讲得还可以,如果你对本文还存在疑惑,可以看那一个游戏开发系列。

和以前一样,我在脚本系统中没有掺合任何关于DirectX的内容,只讲理论!

看看KGameV1.0(2005年5月初次在gameres中出现,就是13岁开发游戏的那个文章:))的脚本系统实现.

首先是脚本类:

class CScript

{

char Name[64];

vectorVARA;

bool ScrIsFill;//脚本运行时是否使用黑屏

string str;//一行完整的指令

string M_CMD;//M_CMD命令,M_CAN临时储存参数

string M_CANX[MAX_CANX];//参数数组

string M_CAN,M_RIGHT;

string M_VAR;//变量

string M_value;//变量,如果为空,就为"="右边去掉";"的数据,否则为"="右边脚本的返回值

string M_IFO,M_IFT;

int CanB[MAX_CANX];//参数是布尔的话在这里储存

public:

CScript();

~CScript();

bool LoadScript(char*FileName);

void ScriptXCanC();

void ShowERROR(string M_CMD);//显示错误

void FenJScript();

void RunScript();

void FenJV ar();

bool BScriptBg(char*FileName);

bool AddVar(string Name,string value);

bool ClearVar();

};

其实就只有这些东西,读取一个脚本,弄出一行的内容,分析一行的的内容(这时候边分析边执行相应的函数),添加一个变量,删除一个变量,等等.....

读取脚本的实现(其实这个KGameV1.0写得并不好,我自己承认的,但起码做出来了,呵呵!)

bool CScript::LoadScript(char*FileName)

{

ifstream is;

int ZS;

int sta,end;

if(IsFile(FileName)==false)return false;//直接返回,因为不存在

IsNextLine=true;

strcpy(Name,FileName);

is.open(FileName);

if(IsNextLine==true)getline(is,str);

ZS=int(str.find("//"));

str=str.substr(0,ZS);//去掉注释语句

replace_all_distinct(str,"'","");//自己写的替换函数

replace_all_distinct(str,syh,"");//自己写的替换函数,syh是双引号的ASCII码

if(stricmp(str.c_str(),"return;")==0||stricmp(str.c_str(),"return();")==0)break;

else RunScript();

//IF语句的实现

if(stricmp(M_CMD.c_str(),"If")==0)

{

int IfP=2;

//If(X(Y))肯定条件,相符运行语句

if(M_CANX[0].find("(")!=-1)

{

sta=int(M_CANX[0].find("("));//比如IF(XX[(]XX))

end=int(M_CANX[0].find_last_of(")"));//比如IF(XX(XX[)])

M_IFO=M_CANX[0].substr(0,sta);//获取IF里的命令

M_IFT=M_CANX[0].substr(sta+1,end-sta-1);//获取if里的变量

//strcpy(SMessage,M_IFO.c_str());

if(M_IFO==M_IFT)IfP=0;//0为通过

else IfP=1;//1为不通过

}

//If(X[Y])否定条件,不符运行语句

if(M_CANX[0].find("[")!=-1)

{

sta=int(M_CANX[0].find("["));//比如IF(XX[XX])

end=int(M_CANX[0].find_last_of("]"));//比如IF(XX[XX])

M_IFO=M_CANX[0].substr(0,sta);//获取IF里的命令

M_IFT=M_CANX[0].substr(sta+1,end-sta-1);//获取if里的变量

if(M_IFO!=M_IFT)IfP=0;//0为通过

else IfP=1;//1为不通过

}

if(IfP!=2)

{

if(IfP==0)

{

while(str!="}")//什么时候结束

{

if(IsNextLine==true)getline(is,str);

ZS=int(str.find("//"));

str=str.substr(0,ZS);//去掉注释语句

replace_all_distinct(str,"'","");

replace_all_distinct(str,syh,"");

if(str.find("return;")!=str.npos||str.find("return();")!=str.npos){break;break;}//结束脚本

else RunScript();

//二级IF

//完成

}

}

else

{

while(str!="}")//什么时候结束

{

getline(is,str);//到"{"这一行

}

}

}

IfP=2;

M_IFO.clear();

M_IFT.clear();

}//IF结束

//结束

}

M_IFO.clear();

M_IFT.clear();

is.close();

return true;

}

其实IF语句的实现占了代码比较多的比例,简单的方法就是ifstream这个东西有一个读取一行的函数substr,具体的自己研究一下吧,也可以用KGame V1.0的代码学习.

运行了这段代码后,脚本就被切成一行一行的(此时"//"和这行后面的内容早已删去),下一个函数:

void CScript::RunScript()//运行一行脚本

{

int VarVa=int(str.find_first_of("="));

int sta=int(str.find_first_of("("));

int end=int(str.find_last_of(")"));

M_VAR=str.substr(0,VarV a);//获取变量

M_CMD=str.substr(VarV a+1,sta-V arVa-1);//获取命令

M_CAN=str.substr(sta+1,end-sta-1);//获取全部参数

M_RIGHT=str.substr(VarVa+1,strlen(str.c_str()));

for(int i=0;i

{

replace_all_distinct(M_CAN,"["+VARA[i].Name+"]",VARA[i].value);

replace_all_distinct(M_CAN,VARA[i].Name,VARA[i].value);

}

for(int i=0;i

{

sta=int(M_CAN.find(","));

M_CANX[i]=M_CAN.substr(0,sta);

M_CAN.replace(0,sta+1,"");

if(stricmp(M_CANX[i].c_str(),"False")==0)CanB[i]=0;

else if(stricmp(M_CANX[i].c_str(),"True")==0)CanB[i]=1;//布尔变量读取

}

if(M_CMD.length()!=0)FenJScript();//执行脚本

if(M_V AR.length()!=0)FenJVar();//分析变量,因为脚本有的有返回值

}

我的代码风格不是很好,凑合看看吧!

这段就是把这一行给拆开,M_CMD就是gggg(1fs,fs)的gggg,M_CAN是1fs,fs的字符串,M_CANX就是一个数组,里面存贮着一个个参数,比如这里M_CANX[0]="1fs",M_CANX[1]="fs",注意我使用的是STL的string,具体的资料可以上网查.

把命令和参数分开了,下一步就是执行了.

if(stricmp(M_CMD.c_str(),"Close")==0)RunMessage(MS_Close,0,NULL,0,0,0,0,NULL,0);//命令2,关闭引擎

else if(stricmp(M_CMD.c_str(),"NewMap")==0)//创建地图

{

strcpy(Tempc,M_CANX[0].c_str());t5=atoi(Tempc);

strcpy(Tempc,M_CANX[2].c_str());t1=atoi(Tempc);

strcpy(Tempc,M_CANX[3].c_str());t2=atoi(Tempc);

strcpy(Tempc,M_CANX[4].c_str());t3=atoi(Tempc);

strcpy(Tempc,M_CANX[5].c_str());t4=atoi(Tempc);

strcpy(Tempc,M_CANX[6].c_str());t6=atoi(Tempc);

strcpy(Tempc,M_CANX[1].c_str());

strcpy(tmpc,M_CANX[7].c_str());

RunMessage(MS_NWMAP,t5,Tempc,t1,t2,t3,t4,tmpc,t6);

}

else if(stricmp(M_CMD.c_str(),"ReadATile")==0)//读取一个TILE资源到表面

{

strcpy(Tempc,M_CANX[0].c_str());t1=atoi(Tempc);

strcpy(Tempc,M_CANX[1].c_str());

RunMessage(MS_RAT,t1,Tempc,0,0,0,0,NULL,0);

}

else if(stricmp(M_CMD.c_str(),"NewV ar")==0)AddVar(M_CANX[0],M_CANX[1]);//创建变量

else if(stricmp(M_CMD.c_str(),"ClearVar")==0)ClearVar();//清除所有的变量

.....//后面的大同小异

再声明一次,这个脚本系统只是我学习C++之作,现在绝对不会这样写了,使用KGameV1.0这个源代码也是迫不得已,因为目前我只对这个源代码熟(虽然圣剑得更好,但他的实现太麻烦)

可以看出这里基本都重复了,就是把这些参数给消息系统(当然现在我的实现方法不会说的:))

剩下的就是变量的处理了,看到这部分代码上面不明白的代码基本就明白了!

void CScript::FenJVar()

{

if(M_value.length()==0)

{

M_value=M_RIGHT;

replace_all_distinct(M_value,";","");

}

for(int i=0;i

if(V ARA[i].Name==M_VAR)V ARA[i].value=M_value;

M_value.clear();//返回清空

}

bool CScript::AddVar(string Name,string value)

{

stVariable TV ar;

https://www.sodocs.net/doc/22285276.html,=Name;

TVar.value=value;

VARA.push_back(TVar);//压这个变量

return true;

}

相关主题