51testing 发表于 2007-11-16 17:15:11

经典游戏制作教程

1.游戏制作的主要流程
-------------------------------------------------------------------------------电脑游戏开发小组中的任何一个人(这个角色通常有策划担任),只要有了一个新的想法 或念头,就孕育着一个新游戏的诞生。在这个创意被充分讨论之后,再加上对其操作过程的趣味性及市场销售的可行性的预测等因素的准确判断,一个完整的策划方案才可能产生。在经过充分的讨论后,策划人员必须将讨论的重点写成文字,也就是提出完整的策划方案,经决策者同意认可后,才能进下一步的工作。这份策划方案就像一部电影的剧本,它必须完整地涵盖整个游戏的故事、流程、内容、方式、游戏画面、角色造型、 场景规划、人工智能、硬件配备、市场评估等。对整个游戏过程的详细描述及实施规划都应 记录在案。 当进入创作过程之后,策划还必须随时和美术设计师和程序设计员保持联系,以免游戏程序的编写失控。策划应能对游戏设置的内容与精神了如指掌,与各个小组及时沟通,并且控制整个游戏制作的进程。

2.游戏设计基本论
-------------------------------------------------------------------------------   
   要设计一个游戏,首先你必须要确定几个重要方针,第一是你要设计的游戏是属於那一种类型,第二是时代背景,第三是模式,第四是程式技术,第五是表现手法,第六是市场定位,第七是研发时间,在掌握上述七个方针之後,你就可以再做详细的规划内容及调配资源,那麽何谓是七项方针呢? 笔者以范例来说明之!
一、类型:
   所谓的类型是指这个游戏所着眼的一个游戏方式,通过这个方式来使玩者达到娱乐的目的,这个游戏方式有专有名词来各别予以命名,兹如下述:
(1) RGP角色扮演:
   这个类型的游戏以通过故事剧情牵引来使玩家能溶入主角所存在的一个世界,这类型态的游戏多半透过战斗升级系统及人物对话的方式来一步步完成设计者所布下的剧情路线,最具代表的作品有日本史克威尔所设计的 "太空战士系列" 及国内大宇资讯所设计的"仙剑奇侠传",当然还有很多部作品例如"神奇传说"等也是此中的佼佼者。
   在RGP的类型中,在近几年来又分支了几个类似的型态,例如说Blizzard的"暗黑破坏神"Dirblo"被定位为"动作RPG",因其动作成分相当高所至,而"神奇传说"、"超时空英雄传说"则被定位尽"战略RPG",只因战略成分比重较高所以又有别於传统RPG。
(2) SLG战略:
   谈起战略游戏,大家最耳熟能详的应是日本光荣公司所出品的"三个系列",KOEI的三国志风靡东亚,从一代进化到现阶段的六代皆为玩家们所津津乐道,而所谓的战略游戏则是透过经营→战争→扩大领土三个手段来蠃得游戏最终目标,一般而言动态成分少,最较偏重於花费脑力的游戏,但从WestWood的新型态战略游戏"沙丘魔堡"问世之後,战略游戏也有了重大的分野,一是以KOEI代表的三国志系列被称为回合制战略游戏,一是以WestWood代表的C&C及Blizzard所代表的魔兽争霸被称为即时制战略游戏,和回合制所不同的是,即时制拥有较多可由玩家与电脑互动的机会,比较不花费脑力,所进行的 方式是建设→生产→攻击→歼灭,在业界有句俏皮话是这样说的:「玩回合制游戏像是自己当了个大将军(元首),运筹帷幄决胜千里之外,而玩即时制游戏则像是个士官长(部队指挥官),只能一味的打打杀杀」由此你可以了解到这两个型态的异同的了。

(3) ACT动作:
   所谓的动作游戏其实就完全靠玩家的反应来做过关的条件,较有名的像DOOM、古墓奇兵、QUAKEⅡ 等,在动作游戏中也分支了相当多的类型,例如快打旋风、铁拳Ⅲ等被定位为格斗型态,主要游戏方式就是二人到四人互相对打一直到分出胜负为止,而DOOM、古墓奇兵则被定位为3D动作冒险游戏,主要目的为杀敌闯关,再来像阿比逃亡记、黑暗之心被定位为横向卷轴游戏,游戏方式就是以攻击跳跃等动作来走过一连串的关卡,表现方式多为2D卷动画面的方式在进行,再如飞龙骑士、极上疯狂大射击则被定为动作射击游戏,游戏方式就是闪躲射击冲过火网进而歼灭守关魔王为止,这些分支型态有共 通特点却又那样的不同,这也是动作游戏吸引人的重要原因。
(4) PZL益智:
   这类型的游戏以趣味性的思考为游戏的主轴,内容可以包罗万,思维模式也可朝物理性及逻辑性方向着眼,具代表性的是大宇资讯的"台湾十六张麻将"、"大富翁"、"仓库番"等,而棋盘式的思考方式着名的有"决战中国象棋"及光谱资讯的"五子棋大师"等,这些游戏入手容易且不分男女老少皆喜欢的特性,使得益智型态的开发较有市场,成本也较低。
(5) ADV冒险:
   冒险游戏的内涵多半脱离不了解谜的成分,是的!这类型的游戏让玩家抽丝剖茧的找出设在游戏背後暗藏的谜底,以顺利完成游戏,具代表作有恶灵古堡、异星搜奇、幽魂等,这类型的游戏年龄层较高,比较不适合国内厂商来研发。
   当你在构思一个新的游戏企划时即应预先想的所属意的类型,然後才进行下一步的计划,一般而言国内市场接受度最高的莫过於 RPG角色扮演类型,这也是为何国内厂商会如此的大力研发RPG型态的游戏。
二、时代背景:
   对於游戏美术来说是一个很重要的方针,因为决定一个时代背景所意味的是资料的搜寻工作方便与否,与美术人员在制定造形时需依据的范例;以国内市场来说多半能接受中国古代时代背景,基本上时代背景有好几种,例如说WestWood的红色警戒架构在公元2000年左右的未来,而魔兽争霸则定在虚幻的欧洲中古世纪中,三国志定位在汉朝末年,星海争霸架构在外太空世界,轩辕剑则定在春秋战国时代等。
时代背景绝对是企划人员在第一阶段规划整个游戏时已先决定好了,如此美术人员才能放心的去搜集资料。
三、模式:
   当决定好类型及时代背景之後,再来就开始要去构思游戏中所要呈现的模式,如假设你的背景订在古代中国,而类型是定为即时战略,这时你必去思考出游戏内容的进行方式,可能你的游戏需要生产的因素,这个因素是什麽? 可以是粮食、矿产及木材,也可以是火山能源、石油、太阳能或天然气等,随着你故事情节上的需要而去制定项目,在作战方式上你所设计的模式可能会去考虑到地形因素、天候因素及资源因素,而且会大量运用到各种战术及攻击方法等,因为如此所以同一种类型的游戏虽多,但模式上却各有特色各有偏重的游戏路线,也各自聚集了拥护者,这就是模式设定的一个重要性,切记千万不可去抄袭他人所定的模式,因为这样一来,当你所设计的游戏完成之後,眼尖的玩家们会把你的产品以过时抄袭为由而弃如敝履,这在这剧烈竞争的国内市场而言是无法存活太久的。
四、程式技术:
   无论你对一个游戏想得多好,架构设计多庞大,如果程式人员本身的技术无法配合的话,那其实一切还是流於空谈,所以在设计一个游戏之前必要先去徵询程式人员的意见,在现在这个环境中不仅程式人员要会Windows98及Wi-ndows NT相关技术,一个完整的系统分析及系统规划是不可缺少的,如此可以避免掉在程式中不可预期的错误出现,而且在一个游戏设计中最好有二个程式人员在运作,一个负责内部程式 (游戏核心引擎) ,一个负责外部程式(介面程式),这样方可发挥完整的战力。
五、表现手法:
      在这个环节中,企划人员、程式人员、美术人员要做完善的沟通及讨论,一般我们知道大部份的电脑游戏是256 色的系统,在这些游戏中对於色盘的控制有相当严苛的要求,为了达到最好的视觉效果,美术人员通常会向程式人员要求多重色盘的资源,而程式人员则会考量到切换时的状况及记忆体配置是否能完全充份,在系统上的问题确定之後,企划人员会提出呈现效果的建议,例如说爆炸效果的表现方式,由内而外扩张到消失的火焰激烈型或包容大量烟雾的燃烧型,这要由企划人员依故事内容来给予定义,同时以物理性逻辑给予美术人员一个建议,再由美术人员前去绘制。
   还有一个例子,以"C&C之红色警戒"与"AGO Empir世纪帝国"的海岸来说明,在"AGO Empir 世纪帝国"的海岸表现是静止的,海水不会流动,最多只有鱼在海中央跳跃,而"C&C"之红色警戒"的海岸表现手法是会流动的,但海中没有任何的特异之处,这两种表现手法各有各的好处及考量,但以笔者而言仍较偏爱"C&C之红色警戒"。
    游戏内容的表现手法通常伴随着同类型游戏间的相异处而有不同的评价及支持者,而不光是美术效果的表现手法,企划人员构思的游戏玩法及程式人员的程式表现都有密切的关系。
六、市场定位:
   不论你所设计的游戏构想如何的好,如果你没有去清楚的定位出你的市场走向,那麽到时制作完成的游戏软体可能会面临到销售不佳的窘状,所以在设计游戏之前你得知道你所定位的族群在那里,从下表中你可作一个市场定位的叁考:
年龄层               教育程度适合的类型内容
7~12岁 国小动作、益智较多趣味性、教学性
13~18岁 国中、高中动作、益智、 较多思考性质、图形精美化同 角色扮演、战略时又较多反射
19岁以上 低知识水平益智、动作较暴力及冒险、趣味性质,操 作简单
19~30岁 大专、大学以上角色扮演、战略富含多重思维性,可以影射周 、冒险、模拟、 遭事物,解谜及创造性运动
七、研发时间:
      这是企划人员在初步规划中的最後一个项目,针对上述的制作方针你必须对美术人员及程式人员安排一个完整的SCHEDULE,从这个SCHEDULE中去研判律发时间,从企划的角度来说,为了不使良好的点子被其他游戏公司抢先推出,同时也要避免推出後模式已落伍,一个游戏的研发最好在一年内,最多不可超过18个月,以成本控制的角度来说比较符合获利标准。
       假设你规划这个游戏需要一年的时间,那麽你就要去区分出美术制作时间 (第一线)及程式制作时(第二线)间的差异,并考量推出DEMO 版及游戏完成的时间,在适当时机打出游戏知名度,为游戏销售上打下一记商机。
制作流程
   一个游戏的制作如果不能充份控制整个作业程序,那即有delay 的危险,大家都知道游戏软体delay对於销售上的影响会有多大,所以如何尽量避免de-lay是每个游戏设计者应极力去避免的,而要去避免游戏开发作业上delay的情况最重要的是严密控管作业流程及计划表。
   那麽究竟游戏制作流程是一个什麽样的情形呢? 首先企划人员在执行制作的前一个月即要定出企划大纲及搜集可用资源,并经程式人员及美术人员确认後开始执行,我们以一个即时战略的游戏来说明,在制作分期程式人员即投入地图编辑器的撰写,而地图编辑器的逻辑设定要由企划人员先期规划,然後程式人员才根据企划人员的规划而进行程式写作。
   在此同一时期美术人员即开始分工合作,一般一个游戏工作小组会有四位美术人员,他们分别负责造形、人物动作、介面、地图四个部分来制作,但这只粗分法,国内游戏公司较常使用这样的组合,在国外美术人员分为造形、人物动作、介面、地图、片头、过场、後制分镜、场景等九大部份,每个部份皆可能都有二人以上在作业,并有一名监制在执行风格及水准的品质控管,这些人统一由後制人员来与程式人员做交图及配合修图等沟通上的交流,所以说後制作美术人员的成败实关系到整个游戏品质的高低。
由於程式人员在设计地图编辑器时需要利用到一些图素来做测试,所以地图图素设计人员要先一步绘制出程式人员所需要的图素,
   在程式人员测试通过之後方可进入大量生产的阶段,由於地图编辑器的设计者多半直接负责游戏引擎的制作,所以在初期企划人员便开始着手人工智慧AI的逻辑判断作详细的叙述,以期在程式人员撰写地图编辑器之後能立即作人工智慧AI的撰写,而在此同时负责撰写介面的程式人员亦与负责介面设计的美术人员作密切的配合,开始着手制作各个介面,因为介面不仅在游戏中是一个主司控制整个游戏的操作盘,同时也是一个游戏的外观,一个拥有优良创意的介面是很受 玩家喜欢的。 在测试地图编辑器时,程式人员亦需要利用移动物件(人)来测试地图上的障碍物判断及最短路径搜寻法,所以设计人物动作的美术人员在此时要先去做出一组人物动作供程式人员作测试,待程式人员把地图编辑器制作出来之後,人物动作设计的美术人员则只要不断的做并不断的把图给程式人员即可。


3.游戏设计十诫律
-------------------------------------------------------------------------------

Travis S. Casey

1. 编写你所喜爱的游戏
不要人云亦云。只要你和你的朋友喜爱就可以了。 同样道理,不要编写某个游戏主题仅仅因为它当前流行而已。编写你所喜欢的题材, 这样才能激发你的热情。
2. 经验是最好的老师
学习游戏编程最好的方法就是阅读大量的游戏程序。玩和分析这些游戏,然后设计 你自己的游戏或扩展游戏。我最主要的经验都是角色扮演类游戏,我的许多游戏范 例也来自它们,但思路却适用于所有类型的游戏。 我阅读过大量的RPG类游戏,粗略算算大约有七十多个。其中大部份我都玩过,精 通四十多个。不但玩和精通这些游戏,我还分析它们。是什么使得这些游戏好或者 不好?我如何修改它?哪些部份表现的出色?哪些部份不尽人意?为什么? 玩和分析过其它游戏后,我把这些知识用于我自己的游戏。比如在“超级英雄”类 游戏中,“斗士”和“英雄”在使用了“指数特性的换算法”取得较好的效果。如 果我想设计“超级英雄”类游戏时,我就知道“指数换算法”很可取。这样的分析 能给你许多被验证过的思想用于你的工作中。
3. 测试、测试、再测试
测试你的游戏,尽可能多次的玩。最好当你不在场的情况下,让别人来玩,过后再 告诉他们。(让别人当你不在的时候玩游戏称为:“盲测”) 还有,推敲你的规则。考虑假设情况,解决概率复杂性。比如,如果你正在设计一 个RPG,试着找出平均人们用弓箭从一米、五米、十米、五十米和百米范围内射中 人形大小目标的百分机率。对于二战游戏,检测你的监视器和解决一个小步兵摧毁 一辆坦克的机率。反复计算在不同的条件下,如:不同的地形、夜间等等。这将有 助于你找到在数学中出错的地方或建立了一个不好的假设。
4.学习背景知识
如果你想编一个中世纪的神奇游戏,就要去读中世纪的文学和历史。读有关魔法的 书及现有的中世纪传奇游戏。对其它类型的游戏也是如此,如果你想做一个越南战 争的游戏,就应去读有关战争的正史及野史,特别是战略、战术的分析。 所有的背景知识可以用于几种途径:首先,能帮助你创造出逼真的角色。另外它能 减少你在术语及背景知识方面,出现重大错误的可能性。当然,资料应该本身就很 有趣。如果对于所要学的都不感兴趣,那为什么还要编写这方面的游戏呢?
5. 正规教育
选一门介绍概率和统计的课。试着读一些游戏方面的数学理论。你可能觉得那没什 么用,但它能帮助透视算法的掌握。斟酌你的英语(或其它你发表游戏所用的语言) 当游戏描写得好的时侯,就更容易学,至少不能有大量的语法错误。 如果你想制作电脑游戏而没上过任何编程课时,不妨学几门。你可能学不到什么编 程序的具体东西,但一门好的课程可以教会你如何组织程序使之更容易维护和发现 错误。 建立一个“参考文献库”,它是一系列和你所制作游戏工程相关的游戏、书籍资料。 当你清晨三点突发灵感而图书馆却已关门的时侯,它将是非常有用的。
6. 抽取些时间
一个游戏就像孩子,当它刚出生时,它的父母总认为它是完美的。从你的游戏中抽 出些时间去得到一些新的观点,避免都耗在上面。一遍遍的重复这一过程。
7. 保留记录
确定你有一份以上的游戏拷贝。如果你是在电脑上输入的,就各保持一份硬盘和软 盘拷贝,另外再打印出一份清楚的最近版本(如每月打印一份,如果你干得快的话每 星期打印一份)。你不会觉得拷贝太多,因为你的好朋友们会来向你借或者想拥有拷 贝。而且这些拷贝能够减少你因为硬盘瘫痪或丢失笔记本等原因造成的丢失机会。 同样道理,保留旧版本的拷贝是有好处的。如果你在游戏测试时发现新的办法还不 如旧的好,而你却已将旧版本的扔掉了,这该怎么办?至少保留一份最后版本之前 的拷贝,同你当前版本的拷贝放在一起。
8. 其它注意事项
优秀的规划和书写是好的,但精美的视觉化说明对你的销售大有益处。如果你要自 己动手,就学一些桌面出版,或找一些现成的插图(比如:剪辑艺术或政府出版物) 或找别人帮你画些插图。 找个从事印刷业的人,和他探讨一些尽可能廉价的方法。低价格可以有助于销售, 低成本则有利于你的收益。
9. 记住这只是个游戏
不要因为制作游戏而忽略你的现实生活。如果有人不喜欢你的游戏,别介意。不用 担心别人窃取你的创意。记住第一条诫律,从你的所做中得到乐趣就行了。
10.没有第十条了 :-)
另外,这里有一些来自汤姆(“棱镜游戏”的主持者)的额外忠告,感谢汤姆!:
1.不断的创新是非常好的。如果你游戏中的所有东西都令人似曾相识,就好像是 偷来的。如果所有东西都与众不同又会让人感到陌生。常见题材单一独到的构思是 好的,但会使得你的游戏看来像个“变体”,而两个熟悉题材精明的创意则会使游 戏有新鲜感同时容易上手。因此不要试图彻底从新发明某样东西,而应把当前所拥 有的主意清晰化、简洁化的用于扩展关键观念的创新和趣味方面上。
2. 修正和雕琢你的游戏创意。测试不仅仅为了清除游戏和规则介绍中的错误,而 且就像一个讨论会,对照他们已经取得的东西,游戏设计师能够发现什么是真正所 要表达的。如果你将测试留到最后,这一发现将对你没有什么好处。如果你进行早 期的测试和经常从眼前试着发现这一游戏真正要表达的,你就能常常很大的改进这 一游戏。 “ Alpha”测试就像在问:“是否真有这个游戏?”“我得到它了吗?”“ Beta ” 测试看来在问:“是否用了最好的方法达到这一效果?”,“这是游戏的精炼吗? 或者它是否能被简化或删除?”“是否所有主要的游戏系统协同工作,给予了我所 期待的游戏体验?”“ Gamma ”测试又像在问“如何才能改善游戏的收支平衡 和介绍呢?”许多设计师停留在Alpha(生产一个吸引人但却是次品的游戏)之后或 者从Alpha直接到Gamma,跳过了Beta(生产一个好的但还不够完美的游戏)。 通常有必要靠你的亲密朋友/游戏小组及早的进行足够的批评性分析,帮助你发现如 何才能改进一个已经相当不错的游戏。
我的一些其它建议:
在我制作游戏过程中,从没有清晰明显的测试“阶段”。我倾向于每个阶段都做一 点。我修改一些系统,抛弃或替换一部分,改善其它的收支平衡和介绍,差不多是 在同时做的。这些部分来自于我所从事的主要游戏类型的设计-宇宙类RPG,你必须 在一个时间内干一个部分的事情。 关键在于去寻求达到你最好的作品。用不同的方法尝试,直到找到适合你的方法, 然后用它钻研下去。

4.游戏的剧情
-------------------------------------------------------------------------------

游戏剧情的重要是不言而喻的,特别是RPG游戏,相信广大玩家对"仙剑奇侠传" 熟得不能再熟了,这个游戏以剧情取胜(他的音乐也相当不错),各大媒体对他的 评价也都是以剧情为主,这个我就不多说了.
我也曾看过许多文章提到剧情的重要,这些文章说的相当好,相当有价值,但基 本上讲述的是剧情的要点及注意事项.而我将从另一个方面去分析游戏的剧情.
游戏的剧情是游戏的灵魂(当然除少数不需要剧情的游戏,如体育类,赛车等), 游戏通过各种各样的方法让玩家融入到设定的剧情上以打动玩家,但如果游戏 的剧情不吸引人,那么无论游戏的表现手法有多好也不能达到目的,但是怎样 的剧情才吸引人呢.
事实上中国与外国玩家有着很大的文化差异,这一点可以从各地所出产的游戏 上看得出来,欧美的游戏大多重视人物与场景的直实性,看上去就像电影,而东 方的游戏普遍追求漫画式的效果.但这两种风格哪种适合我们呢?答案是两种 都适合.不要忘记我们的玩家基本上是青年一代,对新事物的接受是相当快的, 对欧美的游戏我们的玩家接受得很快,从这可以看出国外的游戏制作是相当出 色的,就拿我自己举个例子,前两天我得到一款"生化危机2"的游戏,游戏开始 的动画可以说是扣人心弦,并且很好的衬托出了主题.而第二种风格的代表作 品就是"仙剑"了.
游戏所要表现的内容必须能够被玩家接受,而且还要有创新,这样的剧情才说 得过去.现在我们有一个很好的观察点,那就是电影,外国的制作精良的大片 挤进中国后给我们的冲击多么大,而中国传统题材的作品也给我们留下难忘 的印象.
我们不得不承认,在技术上我们与国外的游戏制作公司相比还差了一截,这 使得许多很好的题材我们不敢用,因为以现有的技术还不能很好的将他表现 出来,如果勉强还可能会起反作用(这是有例子的).
在这里我只是起一个开头的作用,什么样的剧情适合大家也不是一下能说清 楚,希望广大游戏爱好者能积极讨论这个问题,这也是我们中国游戏业现阶断 的一个重要的有待解决的问题.

5.角色扮演游戏的升级系统研究
-------------------------------------------------------------------------------


在一般的角色扮演游戏中,人物的成长是一件相当重要的事,无论是角色扮演游戏或是目前热门的策略型角色
扮演游戏(简称RSLG),这些升级系统都是游戏的一个重要部份。不过在一般的角色扮演游戏中,人物的升级
以及成长却有着很多种的处理方式。在本文中,笔者将为各位介绍各种角色扮演游戏中常用的升级方式,并且
分析各种作法的优缺点。
在一般的角色扮演游戏中,最常用的升级方式就是乱数式的成长方式。在这种模式中,当一名角色获得升级的
时候,程式会使用乱数来决定升级的各项指数,也就是说所有的升级数值都不是在控制中,而是依据一个乱数
表来决定提升的数值。这种升级的方式是如何处理的呢?
当人物到达升级的标准时,就会进入处理升级的副程式中,在这个副程式中程式会依设计者所定出的一个乱数
范围,来计算出这名角色所得到的升级指数,然後将这个数值加到需要增加的属性上。
在这种乱数决定升级的情况下,玩者所能够获得的升级数值,完全是由设计者订定的范围中求出,无论是升级
的上限或是下限都是在这个范围内,绝对不会有意外的情况发生,就算是设计者如何提高上限与下限,都不会
改变这些。这种作法虽然可以让设计者很轻松的订出升级的上下限,但是却不能控制升级时的不利因素,那就
是乱数的成份实在是太高了。若是有一名角色因为运气不好一直只有获得下限的升级数值,那麽它可能会比一
个一次就获得上限升级数值的角色要弱。举例来说,当这个乱数的范围是一到五的时候,若是角色甲和角色乙
分别获得上限和下限的升级数值,那麽会发生以下的状况:
┏ ┳ ┳ ┓
角色甲 角色乙
┣ ╋ ╋ ┫
LV1 10 10
LV2 15 11
LV3 20 12
LV4 25 13
LV5 30 14
LV6 35 15
┗ ┻ ┻ ┛
各位看看上表,是不是可以看到角色甲在第二级时的数值就已经和角色乙第六级的数值是相同了。由於乱数式
的升级方式会有这种不公平的情况发生,因此常会使得玩者的努力需要有一些运气的成份在里面;若是运气不
好,可能原本的努力都无法发挥所要的功效。
由於乱数式的升级方式有这样的缺点,因此有两种不同的改进办法,首先就是百分比制的升级方式。在这一种
办法里,角色在升级的时候还是使用乱数来进行,不过在每一个数字的出现比例上却做了一些调整。例如同样
的升级的范围还是从一到五,但是每一个数字的出现比例调整如下:
┏ ┳ ┓
数值 出现比例
┣ ╋ ┫
1 10%
2 20%
3 40%
4 20%
5 10%
┗ ┻ ┛
各位从上表中可以看到,在这一种处理方式上,每一个数字出现的比例做了一些调整。原本的乱数式中,每一
个数字的出现比例都是相同的,就以上面的例子来说,每个数字出现比例是百分之二时,因此上限和下限的数
值比较容易出现,发生不幸的情况比较多;但是在这样子调整後,上限和下限的数值出现的机会就减低了不少
,会发生不幸的情况就降低了。
虽然这样的作法可以降低不幸的发生机会,但是还是无法完全的克服所有的状况,因为还是有可能会发生相同
的状况,使得玩者陷入属性不佳的情况中。因此另外一种改良的方式~修正值升级方式就这麽出现了。
其实修正值的升级方式和原本的乱数处理法在计算的时候是完全相同的,只不过是它在升级到一个程度的时候
,会来做一次计算并且取出一个修正值,以免玩者因为运气不好无法达到升级的功效。
在这种作法上,上半部和乱数式的做法完全相同,唯一的不同是下半部的副程式。而这个副程式的作用就是在
帮一些升级时运气比较不好的玩者取得一点修正值。
我们就以前面所说的升级的数值是从一到五来做个例子,让玩者每升五级时就可以取得一点修正值。因此若是
一名角色在五次升级中都只有获得一点的升级值,那麽目前它的数值就是:
10 + 1 + 1 + 1 + 1 + 1 = 15
不过在我们的升级表内中等的数值是三,因此当角色升了五级之後,应该可以获得以下的数值:
10 + 3 + 3 + 3 + 3 + 3 = 25
这麽说来这名角色因为前五级的升级运气不好,因此少获得了十点的升级指数,所以我们就在这一次把这个缺
少的数值以修正值的方式补足,从修正值的计算式中可以得出:
25 - 15 = 10
就将这个数值加到角色的属性中,让角色不会因为运气太差而有不利的情况。若是角色在升级中都获得比较高
的数值,那麽修正值就是负的,也就表示不需要有修正值的存在了。
这种作法完全是为了不让玩者因为升级时运气不好使得属性太低,因此只能算是修正部份数值的作法,虽然不
能完全解决乱数式的问题,但是可以将不利的因素降低,因此在某些游戏里的确有采用这样的作法。
除了乱数式的作法外,还有一种是表列式的升级方式。在这种升级方式中,每一名角色的升级数值都是设计者
已经订好的,完全不会有任何的变动。它的好处是设计者可以完全掌控所有的升级状况,但是相对的这样子的
表格需要占掉较多的程式空间。
举例来说,某个游戏若是采用这种升级方式,那麽在它的记忆体中就需要有这样子的升级表格。若是游戏中有
七项属性会获得升级,等级共有一百级的变化,那麽基本上它就需要有七百个不同的数值表放在程式中。若是
一个数值用了两个位元(BYTE),那麽就需要用到1K左右的记忆体。如果说游戏中有四名角色,它们升级情况
又都是不同,那麽占掉的记忆体就将近有5K了。这麽算起来各位可能觉得不会很多,但是当这种资料越来越多
的时候,记忆体的消耗也就越来越多,使得程式的空间也越来越小了。
由於表列式的作法会使得升级的情况比较单调,因此大多数的游戏并不愿意采用这种作法,再加上这一类的作
法对於记忆体的占用空间也比较高,因此如果不是必要,大多数都不会用这种作法。
除了以上这些作法之外,还有一种就是指数型的升级方式。这种作法其实就是表列式的改良,因为它将升级的
表格简化成一个叁数,在升级的时候就依这个叁数来计算能够获得的升级值。现在我就举一个例子来示范。目
前有一名角色的属性以及升级指数如下:
o 生命:10 生命指数:10
o 法力:10 法力指数:10
o 力量: 3 力量指数: 2
o 智慧: 2 智慧指数: 2
o 反应: 2 反应指数: 2
o 体能: 4 体能指数: 2
o 运气: 1 运气指数: 3
那麽当他获得升级的时候,程式就会依这个升级指数来计算升到下一级时的各项属性值。因此在升了一级之後
,各项属性的数值就是以下的数字:
o 生命:20
o 法力:20
o 力量: 5
o 智慧: 4
o 反应: 4
o 体能: 6
o 运气: 4
用这种作法,在程式内不需要复杂的升级属性表,只需要几个简单的叁数就可以,若是能将各项叁数之间的关
系加以变化,并作一些运算,那麽可以使升级时的变化更多。举例来说生命的增加和体能有关,或是法力的增
加和智慧有关,那麽在计算起来时会有比较多的变化,使得整个升级的表现不会太单纯。
以上这些作法大部份的变化程度都不会很多,没有办法表现出一个人的成长情况。就像我们有时候会形容一个
人「大器晚成」或是说他「小时了了」这样子的情况都不能表现出来。因此後来又有一种成长曲线的升级方式
。在这种升级方式中,我们首先要订出几种不同的升级情况。像是:
A. 平衡成长
B. 大器晚成
C. 小时了了
要达成这种效果,我们需要将升级的总等级数分成几个阶段。我们以一个可以升到一百级的游戏来说,将每十
级分成一个区块,就可以订出这三种成长情况各要给它多少的数值。
其实这种曲线式的升级方式,在处理上和指数式的作法差不多,只不过指数式的作法一个人物每一种属性只会
有一个数值,这个数值是不会改变的。但是在曲线式的作法中,会依不同阶段有不同的升级指数,才可以造出
不同的成长情况。我们就以一名「大器晚成」的角色来说,这一类的角色在开始成长的比较慢,但是当人物成
长到一个阶段後,成长的速度就会加快,因此我们可能在前两个阶段只给他们一点的升级指数,後面几个阶段
再给他们较高的升级指数,使这名角色会在游戏後期升得比较快。
反过来说,若是要设计一名「小时了了」的角色,那麽我们在初期可以给他较高的升级指数,但是到了後期就
要给它较低的指数,如此一来就可以表现出这样的情况。
事实上,在游乐器中的「光明与黑暗续战篇」就曾经用过这一种作法,使得游戏中的每个角色都有各自不同的
特色。特别是有些属於大器晚成的角色,曾经因为初期成长的速度太慢而被玩者抛弃,但是後来知道这名角色
的特性之後,再回过头来训练的这种情况,正是这种曲线式升级的特色。这种作法使得角色除了单纯的数字属
性之外,还增加了一些隐藏的特性,会让游戏更有味道。
如果以笔者个人的喜好来说,我是比较欣赏曲线式的升级方式,因为这种方式比较可以隐藏角色的特色,也不
会因为数字的变化太过单调而让玩者觉得过死板。比起乱数式的不定性和升级指数式的单纯来说,这一种作法
可以说是兼具了两种的特色,同时还有全新的表现,是一种不错的升级方式。只惜目前国内的游戏很少使用这
种作法,大多还是采用乱数式的作法,对於国内玩游戏的玩者来说,实在是有些可惜,因为大家没有办法体会
到这种作法的优点。

6.游戏中的智能系统处理
-------------------------------------------------------------------------------

游戏中的智能对手
 AI在游戏中最普通的形式是创建计算机控制的对手。因为大多数游戏是单人游戏,所以要设计游戏者在游戏中必须战胜对手。为了达到这个目的,你可以使用某种类似A*搜索的简单AI算法,以帮助对手穿过迷宫向游戏者发起进行。你也可以使用简单的算法预测游戏者的反应。
 但是,记住没有必要创建世界上最强大的对手。你的对手只要能给游戏者提供足够的挑战性就可以了。还有,要注意游戏的内容。例如:一个战棋式RPG游戏中策略占的是主要地位;而在纯RPG中故事情节和角色开发就更重要一些。千万不要因为计算机对手太强大而让游戏者们陷入失败的泥沼。
游戏中的非智能对手
 通常,在游戏开发中AI技术是与计算机对手紧紧联系在一起的。这是因为早期的大部分类似角棋的游戏是一对一的。但是,任何好的探险游戏或RPG 游戏开发者都知道,AI同样可以用于非对手角色。例如:如果你正在建立一个RPG游戏并且你想让你的世界活起来,这就是说,让城市里的人以智能的方式活动,那么你可以使用某种算法确定在一天中的某个时候,角色应该在那里。你可以使用类似于AI算法如A*来辅助你将一个对象从一处移动到另一处并绕过障碍物。
游戏中的智能系统
游戏中的AI在本质上是最具有模仿性的,但它们基本上是依赖一些AI要素。 你可以将所有具有决策功能的对象在一个游戏中融合为一个整体。例如:在一个战争游戏中,你的各个部分可以依据各自所处的具体环境来作出各自的AI决策。
  使用这种方法,你得把精力集中于怎样在各个独立的决策个体之间建立联系,以及这些联系怎样才能使游戏成为融会贯通的整体。是用一个高级决策影 响其它决策,还是各个决策个体之间平等地互相影响呢?举个战争游戏来说, 你有十辆坦克,它们的思维模式基本相同。所以它们都决定去攻击敌人阵营中 HP值最低的一辆坦克。但是这时其中一辆坦克说:“这个敌人归我了!”那么 剩下的九辆坦克就应该依据这条信息各自调整下一步的攻击目标。当你建立智能个体时,要考虑在一个智能系统整体环境下,它应该如何行动。


7.电脑游戏中的人工智能制作
-------------------------------------------------------------------------------

 电脑游戏随着硬件执行效率与显示解析度等大幅提升,以往很多不可能或非常难以实现的电脑游戏如此都得以顺利完成。虽然电脑游戏的呈现是那么地多样化,然而却与我们今日所要探讨的主题,人工智能几乎都有着密不可分的关系。
 在角色扮演游戏中,程序员与企划人员需要精确地在电脑上将一个个所谓的“怪物”在战门过程中栩栩如生地制作出来;所以半兽人受了重伤懂得逃跑,法师懂得施展攻性法术。
 目前能让人立刻想到与人工智能有密切关系的游戏有两种:
一是所谓的战棋/策略模拟游戏,二则是棋弈游戏。人工智能的比重与深浅度,在不同的游戏类型中各有不一。有的电脑游戏非标榜着高人工智能不可,不然没有人买;有的则是几乎渺茫到让玩家无法感觉有任何人工智能的存在。            
 导向式思考
 AI最容易制作的的方式,同时也是早期游戏AI发展的主要方向就是规则导向或称之为假设导向。在一些比较简单的电脑游戏中,程序员可以好不困难地将游戏中的规则与设定转化成一条条的规则,然后将它们写成电脑程序。让我们以角色扮演游戏为例。决大多数的企画在设定所谓电脑怪物时,所设定的属性通常有以下几种:
  生命值 攻击力 防御力 法力  属性
 最后一个“属性”是我在设定时喜欢增加的项目之一。透过这项属性的设定,我可以把怪物设定成“贪生怕死的”,也可以把战士设定为“视死如归”。以目前我们所掌握的资料,在战门系统中的大纲如是诞生了:                          
规则一
if (生命值< 10) // 边临死亡了吗 
{  if (属性== 贪生怕死)               
   结果 = 试图逃跑               
  if (有任何恢复生命值的物品或法术可用)      
   结果 = 使用或施展相关物品或法术       
}                                                 
规则二
 
if (可施攻击性法术 && 有足够法力)
{                        
   结果 = 施展攻攻击性法术             
}                        
 由以上一连串的“如果--就--”规则设定,建立了最基本的AI。说这样的制方式只能建立基本AI其实并不当然正确。只要建立足够及精确的规则,这样的方式仍然有一定水准的表现。
 规则导向的最大优点就是易学易用。在没有深奥的理论概念的前提下,仍有广大的使用群。所以很多老道的玩家常常没两下就摸清楚敌人的攻击策略,移动方式等等。

51testing 发表于 2007-11-16 17:15:44

推论式思考 相信曾经接触过电脑语言课程,或是自习过相关书籍的朋友们,都曾曾经听过一个著名的程序,那就是井字游戏。用井字游戏作为讨论AI的入门教材,我个人觉得是最适当的例子。或许有人还不知道井字游戏怎么玩。只要任何一方在三乘三的方格中先先成一线便胜利了。我们在前面谈过的规则导向,在这里也可以派得上用场。
 if任何一线已有我方两子&&另外一格仍空//我方即将成一线吗
  结果 = 该空格                     
 if任何一线已有敌方两子&&另外一格仍空//防止敌方作成一线 
  结果 = 该空格                     
 if任何一线已有我方一子&&另外两格仍空//作成两子    
  结果 = 该空格
 有一次我在某本电脑书上,同样地也看到某些以井字游戏为介绍的范例。不同的是,我几乎看不到任何规则导向的影子。但在仔细分析该程序码后,我得到了极大的启发,原来AI是可以不用这么多规则来制作的。它用的方法正是在电脑AI课程中重要的概念:极大极小法。我在这里只说明这法则的概念。继续以井字游戏为例,电脑先在某处下子,接着会以假设的方式,替对方下子,当然,必须假设对方下的是最佳位置,否则一切则毫无意义。在假设对方下子的过程中,自然又需要假设我方的下一步回应,如此一来一往,直到下完整局游戏为止。 底下是节录书中的程序片段:                       
 
bestMove(int p, int*v)
{   int i; 
   int lastTie;                  
   int lastMove;                 
   int subV;                                   
/*First, check for a tie*/            
    if (isTie()) {              
     *v=0;               
     return(0);              
   };
/*If not a tie, try each potential move*/
 for (*v=-1, lastTie=lastMove=-1,i=0;i<9;i++)
  {
   /*If this isn't a possible, skip it*/          
   if (board!=0) continue;
   /* Make the move. */
    lastMove=i; 
    board=p;                             
   /* Did it win? */                       
    if (hasWon(p)) *v=1;                     
    else{                             
   /*If not, find out how good the other side can do*/
     bestMove(-p,&subV);                      
   /* If they can only lose, this is still a win.*/
      if (subV==-1) *v=1;       
   /* Or, if it's a tie, remember it. */         
       else if (subV==0){                 
          *v=0;       
          lastTie=i; 
          };                          
       };                              
/* Take back the move. */           
           board=0;          
/*If we found a win, return immediately
     (can't do any better than that)*/     
  if (*v==1) return(i);                     
/*If we didn't find any wins, return a tie move.*/         
  if (*v==0) return(lastTie);                      
/*If there weren't even any ties, return a loosing move.*/     
  else return(lastMove); 
};    
 国外的一些论坛曾举行过256字节的游戏设计比赛。作品非常多,其中有一件作品正巧也是井字游戏。作者用区区两百多行就写了与上述程序演算方式完全相同的作品,可见功力确实了的。另外,我也很希望类似的活动能在国内推展起来。对了,在这样的比赛条件限制下,除了汇编语言外,几乎没有其它的选择了。       .386c                        
  code      segment byte public use16      
          assume cs:code, ds:code      
                            
          org   100h            
                            
  tictac     proc  far             
                            
  start:                       
          push  cs             
          pop   ds             
          mov   ax,0B800h     ; 清除屏幕
          mov   es,ax       ;    
          xor   di,di       ;    
          mov   cx,7D0h      ;    
          mov   ax,0F20h      ;    
          rep   stosw       ;    
          xor   cx,cx       ;    
          mov   dl,5            
  loc_1:                       
          call  printBoard         
  loc_2:                       
          mov   ah,8        ; 等待按键
          int   21h             
                            
          movzx  bx,al            
          sub   bl,31h       ; 如果不是1..9
          jc   loc_2       ; 则重新输入 
          cmp   bl,8              
          ja   loc_2              
          cmp   data_1,al          
          jne   loc_2              
          mov   byte ptr data_1,'x'     
          dec   dl               
          jz   short loc_3           
          mov   al,'o'             
          call  bestMove            
          mov   ,al             
          call  isWin   ; 判断是否已取得胜利 
          jnc   loc_1              
  loc_3:                          
          call  printBoard           
          mov   ax,4C00h            
          int   21h               
                              
  data_1     db   '12'              
  data_2     db   '3456789'            
  data_3     db   0                
                              
  tictac     endp                  
                              
                              
  printBoard   proc  near              
          mov   si,offset data_1        
          mov   di,548h             
          mov   cl,3              
                              
  locloop_4:                       
          movsb                  
          add   di,5              
          movsb                  
          add   di,5              
          movsb                  
          add   di,133h             
          loop  locloop_4            
                              
          retn                  
  printBoard   endp                  
                              
                              
  isWin      proc  near              
          mov   bx,1              
          mov   bp,3              
          call  sub_3    ; 检查横向是否完成 
          inc   bx               
          inc   bx               
          dec   bp               
          dec   bp               
          call  sub_3    ; 检查纵向是否完成 
          call  sub_4    ; 检查斜向是否完成
          clc
          retn                  
  isWin      endp                  
                              
  loc_5:                         
          stc                   
          retn                  
                                                            
  sub_3      proc  near              
          mov   ah,3              
          mov   si,offset data_1        
  loc_6:                         
          mov   di,si              
          call  sub_5              
          add   si,bp             
          dec   ah               
          jnz   loc_6              
          retn                  
  sub_3      endp                  
                             
  sub_4      proc  near              
          mov   di,offset data_1       
          inc   bx              
          call  sub_5             
          mov   di,offset data_2        
          dec   bx               
          dec   bx               
          call  sub_5              
          retn                  
  sub_4      endp                  
                              
                              
  sub_5      proc  near              
          mov   cl,3              
                              
  locloop_7:                       
          cmp   ,al             
          jne   short loc_ret_8         
          add   di,bx              
          loop  locloop_7            
                              
          add   sp,4              
          jmp   short loc_5           
                              
  loc_ret_8:                       
          retn                      
  sub_5      endp                      
                                  
  bestMove    proc  near                  
          mov   bx,31FEh                
          mov   cl,9                  
          mov   di,offset data_1            
                                  
  locloop_9:                           
          cmp   ,bh     ; #empty?        
          jne   short loc_12  ; #no, skip       
          mov   ,al                 
          pusha                      
          call  isWin      ; #CY: Win       
          popa          ;            
          jnc   short loc_10  ;            
          mov   bl,1                  
          mov   si,di                  
          mov   ,bh                 
          retn                      
  loc_10:                             
          pusha                      
          xor   al,17h ; good! toggle 'o' / 'x'
          call  bestMove                
          mov   data_3,bl                
          popa                      
          mov   ah,data_3                
          neg   ah                   
          cmp   ah,bl                  
          jle   short loc_11              
          mov   bl,ah                  
          mov   si,di                  
  loc_11:                             
          mov   ,bh                 
  loc_12:                             
          inc   bh                   
          inc   di                   
          loop  locloop_9                
                                  
          cmp   bl,0FEh                 
          jne   short loc_ret_13            
          xor   bl,bl                  
                                  
  loc_ret_13:                           
          retn                      
  bestMove    endp                      
  code      ends
          end   start


7.游戏引擎 小谢
-------------------------------------------------------------------------------

早想写一点游戏设计的文章与大家交流,一是经验的问题,二是公司正在紧张的游戏制作期,实 在抽不出多少时间,一直没有动手,今天忽然头脑发热,写了一段,以后准备陆续写一些游戏创意,策 划,制作,流程管理,和制作工具等方面的文章供大家参考.
我们的游戏设计经验主要是冒险游戏和角色扮演游戏,但我们设计游戏工具时尽量适应其余题 材,不过是否可行未经检验. 写这篇文章的意图一是想为游戏界做点事,抛砖引玉吧,另外是公司正在寻找志同道合的战友, 我写一点文章交一交朋友,许多东西仅仅是我们的经验,不一定很好.参考而已吧
游戏设计工具包括游戏编辑工具和游戏引擎两块;
编辑工具:交互编辑游戏数据,生成游戏引擎所需的数据文件,包括以下几个功能块: 图像编辑,场景编辑,物品编辑,动画编辑,人物编辑,事件编辑等,具体介绍在以后的 文章介绍.
先从游戏引擎说起.
语言:VC5.0
操作系统:WIN95
图像引擎:DIRECT X 5.0
支持游戏风格:各种类型和视角以及多层次的冒险游戏和角色扮演游戏
整个游戏引擎包括以下功能块:
资源管理:图像库CIMGLIB,声音库CSOUNDLIB,通过编辑工具形成的资源文件来定义,每种资源 包括定义管理和一些操作接口.图像库图"像包括多种格式(BMP,GIF.AVI,FLC等)以及他形成的内存 格式定义,子图定义(每一张图片包括许多小图,需定义它的小图位置,当然可以自动生成),游戏需 要的特殊定义,比如行走,身体性质,中心定位点,触发区,可以根据自己的要求扩充各种性质定义. 图像最好允许图像组合定义.声音库包括WAV和 MID的定义和再现. 资源由IMGLIB.DAT和 SOUNGIMG.DAT定义,调试版本中最好不要将资源打包,而是指向正常的 文件名,发行版本中再打包,这样修改和不同工作人员协调容易一些,否则最好有一个自己的资源 管理器.我们在调试版本中数据文件采用文本描述格式.许多数据可以手工编写而不需要专门的 编辑工具.
资源管理对象还包括内存管理,比如设置时间阀释放长期不用的资源.
声音管理:CSOUND,包括Creat(),Sound(char *fileName...),SetPos(),等,DirectSound有一 些函数,我们要做的是封装简化,减少对外的接口.
窗口系统:接管标准窗口系统,一个完善的游戏引擎最好有一个自己的界面系统,至于简单还是 复杂根据自己情况具体分析,一个具备基本功能的界面系统1000行程序就可以对付下来,需要窗口 系统的原因是一般的图像引擎不支持标准窗口,二是可以便于移植到别的操作系统.在我们的游戏 引擎中,游戏只是窗口系统的一种特殊控件(CWINGMCTR),因此可以实现多窗口游戏等特殊要求.
CWINGMCTR是一种特殊控件,通过他来控制游戏.包括控制和显示.
图像引擎:所使用的图像引擎的管理,我们使用的是DirectX,包括Creat(),CreatSurface(), OutToScr(),Bilt()等对外接口;他不是游戏的重点,我们尽量将图像引擎细节封装起来.
图像管理:这是处理图像的中心,一般处理游戏显示喜欢以某种图像引擎为中心来设计,我觉得 最好设计自己的对象来封装别人的图像引擎,这样不会因某引擎而受限制,移植也比较容易,我们虽 然使用的DIRECT X ,但实际上对外的接口是一种CPICPAGE的界面,他不但包括DIRECT X 的surface, 也包括标准的位图,AVI界面,GIF动画界面,以及自定义的格式,他将各种类型的图像统一起来,对外 使用统一操作,比如DRAWTEXT,BILT,LINE等标准图形图像操作,以及扩充的ALPHA通道,透明度等操 作.为了减少内存的需求,特别是16M高彩,不要将全部图像使用DIRECT X的表面,对一些刷新不多的 图像,比如背景,可以使用标准256色位图,甚至一种GIF表面,需要时再解压,我们还使用一种单色位 图用来从背景中抠图,比如一所场景中一棵巨大的树,只要不是动画,我们可以用单色抠图的形式从 背景中扣除来作为另外一层,这样我们可以大大降低图像的内存需求.因此采用全部手绘(或3D场景), 而不是小图拼贴的场景成为可能.通过各种手段可以节约60%的内存需求.
CPICPAGE可以通过TimeTrace()以及多线程来改写内容,比如AVI的改变.
游戏控制:这部分包括显示和控制,由CGAME->CGAMEPAGE->CGMOBJ对象组成,CGAME是总控对象, 包括许多CGAMEPAGE游戏页,CGAMEPAGE是一个具有连续场景的游戏片断,有点类似于游戏的一关, CGAMEPAGE由一系列CGMOBJ组成,CGMOBJ是游戏的基本对象,由他继承出地图,物体,物品,人物,武器, 动画,触发器,多媒体按钮等特殊
游戏对象,这是一个根据游戏要求不断丰富和改写的部分,对外的 接口是:SendDraw(),Draw(),TimeTrace(),AcceptMsg(),SendNetMsg(),AcceptNetMsg()等,他是通 过CWINGMCTR来调用,每种对象有许多控制参数,对象之间允许通讯,以及有自己的生长死亡发展的 控制,尽量做到对象与外界减少直接接口,通过消息实现交流.
对象分为两类:景色对象和活动对象,
景色对象定义了组成场景的元素,包括背景和前景两层,可以是由整个图片组成或由RPG常用的 图片拼贴法的组成,它的特点只作为背景或前景,活动物是在他们的之间活动,一般定义后不做改变, 也不做控制,由于支持图像界面多格式,所以我们可以方便地使用AVI或 GIF动画作为背景来增加场 景的效果和真实性.景色还包括了行走性质定义,我们采用的是8x8为一单元,每个单元定义了一种 性质,比如平地,草地,障碍物等.
活动对象是在背景和前景之间活动,他们之间有相互的位置关系,前后关系随着位置改变会不断 改变,因此他在所属的CGAMEPAGE中次序是动态的.对象的关系一般是由Y轴定义,由于要支持斜视角和 复杂的地形结构,光靠Y轴是不够的,我们引入了地基线的概念,通过在地基线之上还是之下来判断前 后关系,地基线的定义在图像定义中描述.活动对象有复杂的参数,可以接受外界消息,可以有自己的 各种反应.我们在引擎中使用了一种描述语言来描述他们的反应,比如对鼠标击打,人物经过等产生 参数改变,发声,对话等的回应.描述语言将作为专门的一章来介绍. 游戏显示过程是这样的, 在每次刷新期时窗口的游戏控件调用他所属的游戏页 CGAMEPAGE->SendDraw(); 游戏页将要显示的对象按前后次序送往窗口,同时注明此对象是否改变,窗口分析改写的区 域,调用每个对象的Draw()接口来刷新活动的区域,为了增加速度,并不是显示所有的区域,而是只改 写活动区域,因此我们设计了一个CCLIP的对象来管理刷新定义,它的原理是将表面分为16*16的单元, 最终显示时计算出优化后的多个剪切区域.整个窗口系统和每个游戏控件拥有自己的CCLIP对象.另 外一项增加速度的方案是游戏控件拥有一个比显示窗大两倍的显示页,这样场景滚动时只要将显示 位置改变即可 ,不用刷新所有区域. 游戏控制的过程是这样的:AcceptMsg()来接受各种消息调用脚本来改变自己参数和状态并影响 别的对象,另外每次时钟来时,调用每个对象的TimeTrace()来改变状态,比如动画改变,运动轨迹改变, 观察周围的对象做出反应等.
系统控制模块:对系统的参数做出反应.不同的题材控制不一样,比如即时战略等.只要改写这部 分以及扩充游戏对象,引擎便能支持不同的题材.至于人工智能,智能行走,只是对象的方法,比较简 单,只是需要时间. 游戏控制部分比较复杂,每一种游戏对象都有许多控制的细节,在这篇文章里不做具体描述,以后 再说吧.
最后一个是网络模块:我们正在开发的是国内第一个图形化MUD游戏,网络是它的核心部分,介绍 网络的内容很多,需专门文章.我们使用的不是DirectPlay,使用的是WinSoct,考虑的是UNIX作为服务 器的需求.网络要解决的难点是安全,同步和数据压缩,这里要用到许多技巧.

51testing 发表于 2007-11-16 17:17:59

游戏是通过数据文件来定义:
数据文件格式:数据文件包括资源定义文件和游戏定义,界面定义文件,文件的数据格式我们采用 的是文本形式,类似于WEB的文本,这样的好处一是版本升级容易处理,二是可以减少前期对编辑工具的 功能要求,因为我们可以用文字编辑器处理大部分数据,然后有时间再设计一个强大的工具比较现实, 当然,最终提供给用户的是处理后的数据文件.他中间有一个转换模块.
游戏的运行流程描述(不是真正的过程,按DOS格式描述):
CreatGameWindow();//初始化window窗口
CreatDraw(hWnd); //初始化图像引擎
CreatSound();//初始化声音引擎
CreatAvi()//初始化AVI引擎
CreatNet();//初始化网络引擎
LoadGameData();//读取游戏定义数据,包括资源定义文件和游戏定义,界面定义
While(1)
{
WINTraceMsg();//处理系统消息,比如鼠标,键盘等
GameTimeTarce();//处理活动的游戏页的时间反应
WinPaint();//刷新游戏显示
OutToScr();
}
我们这里介绍的是单线程结构,许多部分可以用多线来加快游戏速度,但结构是一样的,就不多介绍了.
游戏引擎的系统分析是游戏设计技术方面的成功关键,是最容易走弯路的部分,希望我们的文 章能给大家一点启发,由于今天的游戏趋向于多类型综合,设计引擎时一定不要拘泥于某一单项题材, 我们在策划这套引擎时要求他支持的游戏非常广,甚至支持多媒体设计,这套引擎只要扩充或改写参 数管理以及游戏对象,便能支持各种风格的2D类游戏.将来我们要做的是一套可以交互设计各种游戏 的开发平台,当然不是<<游戏工厂>>似的玩具.
今天就写到这里,这只是对引擎结构的大概介绍,其中每一点将来都有详细的描述,欢迎同志商讨. 我们尽量回答朋友们的意见,欢迎加入我们的队伍.


8.脚本语言浅介
-------------------------------------------------------------------------------

  今天,我想先以一套游戏使用的技术为蓝本,导入实务设计的范畴。你听过“ZZT”这套共享游戏吗。
  如果没有,别太在意,这是91年发行的文字模式角色扮演游 戏。我们不谈游戏本身,而把焦点放在游戏的开发系统当中。ZZ T虽然老,所引用的“物件导向”设计概念可是一点都不含糊。
  为什么需要额外设计一套游戏专属的程序开发系统?利用现有的程序语言难到不可以吗?首先,现今常见的程序语言中,大 多半都是通用语言,当然也有不少专用语言,严格说来,没有一 套游戏设计的专用语言。(不过,早在八位电脑时代,已有一套 名为“游戏制作者”的软件,它允许使用内建的语言来开发游戏 )就目前游戏主要使用的C语言以及汇编语言来说,程序员必须 从无到有,制作出游戏的引擎系统。
  如果有一套游戏开发系统,制作游戏(特别是续集)会大幅 度降低程序设计制作游戏所花费的时间与精力。让我们来看看几个实际的例子:Origin的创世纪系列(人物行程管理、对话皆有 专属系統)等。只要游戏开发系统规划得适宜,一旦制作完成, 对日后制作的游戏程序,绝对有大补益。
  我不想在这里就编辑器、编译器之间的差异以及设计的详细原理做讨论。虽然这些都与设计一套游戏开发系统有密切关系, 但这些都属于“系统程序”课程的范畴,我们只需了解就行了。   将程序与游戏资料独立安排有许多好处。你不会因为仅需要修改 游戏资料,就必须重新进行“修改代码、编译、连接”等繁琐的 过程。让我们来看一段ZZT游戏中,某商店老板的“控制”程序            
@Vendor
#end                      
:touch      “事件”触发(接触该“物体”)
   "Hello, you must be new to town!   \
   If you want to be successful     讯息显示
   around here, you'll need to buy  
   some supplies!"           /
                     
!ba;Ammunition, 3 shots.........1 gem   \
!bt;Torch.......................1 gem    选项控制
!bx;Advice......................Free   /
#end                   
                     
:b             选择第一项:购买弹药
#take gems 1 ca             
#give ammo 3               
"Here's your ammo... Happy hunting!"   
#end                   
 
:bt             选择第二项:购买火把
#take gems 1 ca             
#give torches 1             
"Here's your torch..."          
#end                   
                     
:bx             选择第三项:提出忠告
"It is whispered that soon        
if we all call the tune,        
then the piper will lead us to reason. 
                     
"And a new day will dawn         
for those who stand long        
and the forests will echo with laughter."
                     
          -Led Zeppelin     
#end                   
                     
:ca             错误处理(无力购买)
"You can't afford it!"          
#end                   
以上是ZZT程序片段
  你是否了解上面的程序,这类经过大量简化后的控制程序,我们把它称做脚本语言。脚本语言除了接近口语化的设计外,另 加上简化后的语法。(除了内建的命令外,通常仅需简单的逻辑 判断与数值计算即可胜任)因此用脚本语言制作游戏,不在是再 是非程序员不可的工作(除了系统本身的修订),企画人员也可 以很快地进入状态。另外,如果将来需要将游戏移植到其他平台 时,比起程序与资料的盘根错节的设计,利用脚本语言来开发的 游戏,只需要修改系统本身,脚本语言部分本身毋须更动,相形 之下出现问题的机会与范围就缩小了很多。      
  这次以ZZT游戏为例,就是想对脚本语言部分的设计作了概念性的介绍。

9.RPG游戏的对话系统
-------------------------------------------------------------------------------

我要和各位谈谈角色扮 演游戏中的最重要的一个部份~那就是交谈系统。若是各位 玩者接触的角色扮演游戏够多的话,应该会知道在角色扮演 游戏中有着单句式以及关键字两种完全不同的交谈系统,今 天我们就来看看这两个不同系统。
要怎麽分辨单句式和关键字系统呢?我们可以简单的从在 游戏中玩者和游戏中的人物交谈时是如何获得讯息来看。在 一般国内的角色扮演游戏中,经常使用的就是单句式的交谈 系统,也就是那种玩者只要和角色对话,对方就会一股脑的 将所有的消息告诉你,而玩者也就可以获得所有的讯息,这 种就是属於单句式系统(如大宇的轩辕剑系列、仙剑奇侠传 等等)。就算是有的游戏用比较刁钻的方式,会要求玩者多 问几次才会出现真正需要的讯息(像精讯的乱世伏魔录), 也不过是这一种系统的延伸使用罢了。
那麽关键字系统呢?我想最有名的应该就是创世纪系列, 在创世纪的前几代中,玩者需要自行输入各个关键字 ,来 询问各式各样的问题,随着玩者输入不同的关键字,就会有 不一样的答案。但是也因为这个原因,使得一 些英文程度 不好的玩者在接触那几代的游戏时相当的辛苦(反过来说, 也有一些玩者因为玩这个游戏而使得英文程度大增),也使 得许多的玩者不愿意接触创世纪。但是到了创世纪七代的时 候,由於采用了滑鼠来进行游戏,因此整个关键字系统就做 了很大规模的改进了,玩者可以由画面选择要问的关键字, 因此不再会有找不到关键字来问的状况发生了。
那麽我们来看看国内的几个采用关键字系统的角色扮演游 戏吧。在笔者的印象中,好像只有精讯的聊斋志异、大宇的 妖魔道、以及软体世界早期推出的一个侠影记而已。在这几 个游戏中,笔者觉得精讯聊斋志异的关键字系统是最成功的, 至於为什麽笔者会这麽认为,绝对不是因为笔者自己曾经叁 与过这个游戏的制作,而是从关键字系统的结构来看,这个 游戏才是真的使用到关键字系统的精髓。
所谓的关键字系统,正如同它的名称一样,是一个以关键 字来串连游戏剧情的交谈系统,因此玩者必需要能够从某一 个游戏中人物的口中获得某个关键字後,再使用此一关键字 到另一名人物的口中发挥作用,这才能说是使用了关键字的 妙用。在聊斋志异这个游戏中,玩者要获得第一座塔~金形 塔的八字真言,就必需要先从一位书生的口中获得四字真言, 然後再到酒店里找一位酒女,用这前四个字的真言向她询问 後四个字的真言。而所 谓的关键字系统的巧妙,也就是当玩者若是没有获得前一 个关键字的时候,在後面的那个人口中是无法取得下一个关 键字的。在聊斋志异这个游戏中,若是玩者不能先由书生的 口中套出前四个字的真言,那麽在酒女的对话选单中是不会 出现那四个字供玩者选择,因此玩者就无法获得後四个字了。 也就是说,关键字系统是一个凭着玩者选项不同会有不同情 况的对话系统。
那麽我们回过头来看看另外两个游戏的关键字系统吧,首先我们来看看大宇的妖魔道吧。在这个游戏中也有关键字系统,但是它的关键字系统所发挥的作用并不是在对 话上,而是当某些任务还没有解决时,这些关键字就会出现 在对话的选单中,让玩者可以选择它取得所需的资料,当这 一项任务或是事件解决了後,这个关键字就会从选项中消失。 这种作法并没有真正发挥「关键字」的作用,只是用它来提 供玩者多次获得相同的问题解答而已。
接下来我们看看侠影记这个游戏,这可以说是关键字系统 使用的错误范例,因为在这个游戏中,提供玩者选择的关键 字并没有影响其他地方事件的作用,反而把原来简单的可以 在单句中表现的讯息切成一段一段的文字,让玩者需要将所 有的关键字选择完之後才可以知道全部的讯息。再加上一些 关键字所得到的讯息又都是没有用的讯息,使得这个游戏的 交谈系统完全没有表现出关键字系统应有的特色。
现在我们就来探讨一下这两种系统在资料结构上究竟有什 麽不一样的地方。首先我们来看看单句式的资料结构 ,记 得在讨论剧情结构的那一篇文章中(第52期),笔者曾经 举过一个单线式剧情的范例吗?那种的结构就 是单句式交谈系统的标准结构。因为在单句式交谈系统中, 在玩者遭遇一名游戏中的角色时,角色会依据当时 游戏中的剧情发展情况,决定出要说那一句话,若是在剧 情没有改变的情况下,就会重复的使用这一句话。
在这种系统中,当玩者控制的角色和游戏中的角色交谈时, 程式会依据这名角色所相对的几个旗标,判断出目前该显示 出什麽样的讯息。若是同时有几个旗标都成立的话,也只会 以最优先的旗标为准。在这种系统之下,讯息的结构相当的 单纯,程式也只要将每一句对话编上相对的代码,然後用判 断式就可以决定出要显示出那一句讯息。同样的,这种系统 的资料结构也比较单纯。
接下来我们看看关键字系统的资料结构吧。在前面说明单 句式系统的时候,笔者曾经说过要以旗标来判断显示那一句 讯息,其实这个所谓旗标就是游戏中控制剧情目前发展到那 里的指标。同样的在关键字系统中这些旗标也存在着,只不 过除了它们之外还要再加上一组关键字的旗标,而这些关键 字的旗标就是这种交谈系统的重心 。
在游戏中,当玩者从其中一位角色的口中获得一个关键字 之後,这个关键字的旗标就会打开,之後若是有遇到对这个 关键字有所了解的角色时,在交谈的选单中就会出现这个关键字。
在这种系统中,交谈的时候,程式会先判断目前玩者已经 获得那些关键字,然後再看这一名交谈的对象对於那些关键 字会有反应,接下来在在选单中就会出现这些有反应的关键 字让玩者选择。在选择了某个关键字之後,相关的讯息就会 显示在画面上,同时若是有某个关键字会因为问了这一句话 之後打开,也会在讯息显示之後将 此一关键字的旗标打开。接下来我们看看下面这个例子:
在这个例子中,会有某甲以及某乙两个人,在游戏的过程 中玩者必需先从某甲的口中获得一句暗号,然後用这 个暗号向某乙询问,才可以获得所需的资料。因此在游戏中某甲的资料如下:
某甲【无关键字】:喔,是你呀。我相信你一定是想要加入这个秘密的组织,不过若是没有组织的口令的话,是不可能加入的。
◎在这句讯息後关键字「口令」打开
某甲【口令 】:你想要知道口令?好吧,那我就告诉你,我们的口令就是神出鬼没。
◎在这句讯息後关键字「神出鬼没」打开
某甲【神出鬼没】:不对不对,这句口令不是对我说的,必需要找到我们组织的某乙,然後对他说出这个口
令,就可以加入我们的组织了。
因此在这个地方我们可以看到,在开始的时候玩者和某甲交谈时会先获得「口令」这个关键字,接下来这个关
键字就会出现在交谈的选单中。而玩者选择口令这个关键字之後,就可以再获得进一步的资料以及「神出鬼没
」这个关键字。之後再以神出鬼没这个关键字向某甲询问,就可以得知这个关键字的使用方式。接下来我们再
看看某乙方面的资料:
某乙【无关键字】:对不起,我什麽事都不知道,
请你不要来打扰我好吗。
某乙【神出鬼没】:你知道我们的口令,那麽你可以加入我们的组织了。我们的任务是要推翻目前的政府, 希望你也为这件事尽一份力。
◎成功的加入判乱组织了
从这里我们可以看到,若是玩者在还没有获得神出鬼没 这个关键字的时候来找某乙交谈,那麽是无法从某乙的 口中获得任何的消息。但是当玩者从某甲的口中获得了「神出鬼没」这个关键字之後,在交谈的选单中就会出 现这个选项,选择後从某乙处就可以得到回应,也就可 以加入判乱组织了。这一种的交谈讯息结构,也就是关 键字系统真正能够发挥作用的地方。
在设计关键字系统的时候有一件事要注意,那就是需要 适时的将某些没有作用的关键字关掉。就以上面的例子 来说,当玩者成功的加入了判乱组织之後,这一部份的 事件可以说是已经结束了,此时就可以将「口令」以及 「神出鬼没」这两个关键字关闭。不过若是这两个关键 字还有作用的话,就不可以这麽轻率的关闭它。
说到这里也许有人会说其实这种剧情不需要用关键字系 统,就算是单纯的单句式结构就可以处理了。但是请各位 想想看,这里的剧情若是当玩者一和某甲交谈,某甲就将 所有的消息一股脑的都说了出来,那麽不是很没有 意思吗?除此之外,关键字系统在玩者被游戏卡 住的时候,还可以让玩者重复询问相同的关键字来获得讯息, 比起单句式结构那种不小心没有看到就会被卡住的情况来说, 是不是也显得比较人性化呢?
前面所说的都是关键字系统的好处,但是为什麽目前的游 戏很少使用关键字系统呢?那都是因为要设计关键字系统的 话交谈的资料结构就绘变得比较复杂了。各位想想看,原本 在规划交谈的讯息资料时只要针对每个游戏 中的角色,配 合不同的剧情段落配上讯息就可以。但是在关键字系统中, 设计者必需要将关键字妥善的分配到
各地的剧情对话中,才能使关键字系统真正的发挥作用, 因此在设计剧情的时候有比较多的困难。 除此之外,目前 国内的公司大多有将游戏推向国外的计划。由於中国字并不 是拼音系的文字,因此这些关键字 一但翻译成其它国家的 语言(像是韩文、英文等等)时,就会发生翻译出来的单字 变成好长一串的文字,若是 勉强的将一串文字删成适度的 长度时,又会有可能会文不对题的问题(这种情况就好像 魔法门三代中的问题被变成中文的问题那样奇怪),或是 因此要增加文字显示的区域。也因为这些原因,使得国内 的公司尽量的不去 碰这样的系统,以免自找麻烦。
事实上关键字系统其实是个扩展性相当大的系统,就连游乐器的游戏中也曾经有采用过这样的系统。正因为它对於剧情的掌握比单句式的要强,以及它更结构化的 资料结构,它绝对是一个完美的交谈系统。


10 完成制作的关键:推动力
-------------------------------------------------------------------------------
我最近听到一个说法:每开始制作50个游戏,最终只有一个游戏能制作完成。这个说法可以由几个原因来解释。制作者可能太超前了以致于在技术上完成不了;项目太复杂了或制作者们在协同工作时有分歧并导致开发停止。还有更多的原因显示了这种可能性:为什么98%的游戏不能最终完成。
然而,我坚信以上的这些问题的影响都比不上在进度中失去推动力。成功的游戏制作者与失败的游戏制作者之间的区别并不是因为前一组成员更有才能,更聪明或更有经验。仅仅是因为前一组成员能完成他们的游戏。

1.聪明,才智和经验
聪明,才智和经验在你的游戏制作过程中和生活中将帮助你克服困难。让我们看看,简单地击键动作并不需要大脑的思考活动,但它需要一些你正在使用的计算机语言的知识,对操作系统的一些概念。大多数知识都能通过阅读帮助文件,相关的书籍,一些示例的源代码来获得,并且你并不需要透彻了解每样事件是如何运行的。
举个现实生活中的例子,录像机有很多复杂的功能,你可能并不会用这么多的功能,但你仍能很好地使用录像机,使用遥控板上的放,前进,后退这几个键来操作就足够了。其它功能你并不需要掌握如何操作,那么你并不需要。编程也一样,做很多事情并不需要透彻地了解每个方面。当你进入一个新的领域,通常会阅读相关的资料来获得所需的信息。我学习一门计算机语言的方法是:简单地运行示例程序并不断地修改它,运行它,看看运行结果,来最终了解这种语言的。
我并不暗示你不需要去努力学习,只是举例说明这些知识并不是决定性的。如果你不了解你的游戏软件和操作系统将怎样配合工作,那么你就需要去了解操作系统本身。尽可能多地去了解你的操作环境,直到你感到能够自如地工作,只会偶尔遇到几个问题时为止。

2.麻烦和推动力
社会的各行各业中,一个项目的完成都需要参与者的努力工作,项目完成所需的时间越长,人们就越可能会遇到更多的麻烦。游戏制作过程也不例外。
游戏制作工业具有多变的本质,大众对游戏画面的要求,对某种游戏类型的喜爱,对游戏趣味性的要求,会经常发生变化。所以在制作期间及时了解业界的最新动态和互相交流是必须的。否则当你开始制作一个游戏,经历过一个较长的开发周期后,你制作的游戏可能已经落伍了。
当你是独立制作游戏时,每次当你遇到一个问题时都可能会落入困境,特别是没有外部的支持时。制作中若有一个阶段达不到预期的目标,都可能会令你泄气。这对独立游戏制作来说是致命的。
对不同的人来说,解决困难的动机是不同的。对有些人来说可能是需要成功以得到其它人的尊敬,或者得到一些报酬,金钱或其它形式的。对其它人来说,动机仅仅是一种创作自己心目中游戏的渴望。无论你的动机具体是怎样的,你都需要依赖它,在制作过程解决遇到的各项麻烦。忽视麻烦或中止开发一段时间,这简直和取消项目是一样的。所以制作者必须习惯于面对各类难题。
保持推动力的一个方法是不断地尝试制作新的事物。每当你制作完成一个作品后,无论对你的自信心还是工作经验来说,都会更上一个台阶。如果你做了大量的工作但不能及时地看到结果,这会令你很不满意,并逐渐丧失推动力。如果你能及时地看到你的工作对作品的改变,那么制作人与作品之间的这种互动性很容易就能使你保持较高的推动力。这就是为什么强烈建议那些游戏制作的新人从一个小项目开始的原因。长周期的大项目往往需要一周或一个月的时间才能看到变化,这往往使新手变得沮丧,并最终不能完成他们的第一个游戏。

3.中断一会
在工作中适当地中断一会是十分重要的。如果你不适当地休息一会儿,你的注意力会渐渐变得分散,工作效率会下降。我曾看过关于注意力方面的研究报告,它指出大多数人在40分钟持续的做某件事后注意力将变得分散。很明显,每隔40分钟就休息15分钟将会浪费大量的时间。然而仅仅是去拿些饮料喝或去察看有没有新邮件到来,或者做任何别的事,都会帮助你长久地保持对工作的热情。
在高强度的工作后,最好中断较长的一段时间。如果你遇到了一个特别困难的问题,那么最好在你开始工作前先休息一会,再解决它。短暂的休息能使你集中精力来工作,但是在你处理一件事的过程中最好不要中断,要一鼓作气。如果这个事件处理的过程很长,那么在完成一个阶段后再休息,并且做好中断点的记录,以便于下次能容易地接下做。当你在工作时,适当地中断一会对保持你工作的动力是很重要的。长时间的工作而不休息更有可能使你的游戏半途而废。
现在来了解什么时候该休息,什么时候该继续工作。如果你处在一个工作气氛很浓的环境中,而且制作的进度很快,那么这时你最好尽你的所能持续地工作,把你所有的热情都投入其中,渴望产品的最终完成日。产品的最终完成日就像灯塔的顶部一样,在那里,你能够回想起你在制作过程中所有的努力。要记住,当你离开你的工作时必须确定,这个中断点在你回来后能够很容易地继续下去。这意味着如果你是一个程序员,若你的代码在编译时有一些错误,那么最好不要把这些错误留到明天再去解决,因为这会使你忘记是什么原因导致了这些错误。

结论
在你制作游戏时会遇到很多的障碍,你必须学会面对它们。推动力确实是完成你游戏的关键所在,要牢记,成功的游戏制作者首先必须先完成他们的游戏。

11 十分钟学会 DirectX 中 的 COM 技术
-------------------------------------------------------------------------------

DirectX 是按照微软的COM(Component Object Model) 搭起来的。设计COM是希望它 能提供一个更安全,易升级,可移植的软件模块。COM用的面向对象的模式比一般的 C++更严格。例如,COM只能永远通过成员函数(member function)进行访问,并且不 能拥有公用数据成员(public data members)
COM异常严格地对待对象(object)和接口(interface). 而且就不让你直接访问对象, COM竟然都不给对象起名字,让你老老实实通过接口来访问对象。在DirectDraw编程 中,我们讲:“访问对象”,其实都是在访问接口。叫惯了也就不改了。
所有的COM接口都从 IUnknown 接口中派生出来。“I”打头是COM接口的标识(I for Interface). 所有的DirectDraw接口都以“I”打头,但莫明其妙的是在很多编程手 册中都把“I”略去。所以看书时要做到心中有爱(I)。
IUnknown 接口提供三个成员函数,其余所有的COM接口,都继承这三个函数。
AddRef()
Release()
QueryInterface()
AddRef() 和 Release() 支持COM的一个特色功能,名字叫“活着封装”(lifetime encapsulation). “活着封装”是一个协议,用来让对象在崩溃时(destruction)自 己负责自己的后事。
“活着的时候”(lifetime) 每个对象内部有一个值用来跟踪记载自己用过的指针(pointer) ,或引用(reference)。当这个对象建立时,该值为1。然后随着对它的调用/被调用, 该值递增。反之递减。当本身崩溃时(destroy itself),该值为0。
AddRef()就是用来加计数器的。你可能不用亲自调用它。你在用DirectDraw API 时, AddRef()就被自动调用了。
Release()则是对着干。减值。你常常要亲自用到它,因为程序可能会异常退出AddRef的 作用域(scope)。如出错控制中。
QueryInterface()用来问COM一个接口是否可用。如可用,则返回一个相应接口的指 针。
问对象是否支持一个接口用QueryInterface, 那么怎么问?当然要知道接口的ID。 我们用GUID来表示,GUID = Globally Unique IDentifier. (全局单一证认)。GUID是 一个128bits的数。所有DirectX中接口的GUID值都可在DirectX的头文件中找到。
这就是全部了。怎么样,有概念了吧:-) he he he.


12 windows 游戏设计时的单任务与多任务处理
-------------------------------------------------------------------------------
单 任 务 处 理
Windows 最 杰 出 的 功 能 之 一 是 能 够 同 时 运 行 多 个 程 序, 但 有 时 也 会 让 人 感 到 头 疼, 特 别 是 对 于 那 些 习 惯 于 完 全 控 制 计 算 机 甚 至 时 钟 频 率、 非 常 自 信 的 游 戏 程 序 员( 当 然, 我 们 的 确 在 乎 那 些 没 礼 貌 的、 在 退 出 时 不 恢 复 正 确 的 系 统 时 间 的 游 戏。 但 是 幸 好, 现 在 我 们 可 以 忘 掉 这 些 了)。
---- 在 多 任 务 环 境 下, 游 戏 程 序 员 需 要 注 意 三 个 大 的 负 效 应:
当 游 戏 失 去 焦 点 而 进 入 后 台 后, 其 执 行 不 得 不 被 挂 起( 可 以 在 Moby Dick Windows 中 使 用“ 中 止 的” 变 量 观 察 它 是 如 何 工 作 的)。 如 果 是 一 个 实 时 游 戏, 程 序 员 当 然 希 望 它 被 悬 挂。 但 在 回 合 制 游 戏 中, 当 玩 家 去 做 其 它 事 情 时, 程 序 员 可 能 不 希 望 计 算 机 一 方 作 任 何 动 作, 但 希 望 后 台 的 人 工 智 能(AI) 运 算 依 旧 执 行。

其 它 的 任 务 占 用 CPU 时 间, 结 果 造 成 我 们 不 能 一 直 控 制 游 戏 中 事 情 发 生 时 的 速 度。 我 们 将 在 后 面 讨 论 这 个 痛 苦 的 问 题。

每 当 游 戏 回 到 前 台, 程 序 员 不 得 不 重 画 游 戏 窗 口。Windows 并 不 负 责 记 忆 它 所 覆 盖 或 隐 藏 的 窗 口 的 内 容; 它 所 能 做 的 最 多 是 通 知 一 个 窗 口 需 要 重 画 其 客 户 区 域。 这 在 有 关 Windows 的 文 章 中 都 有 论 述( 参 见 WM_PAINT 的 内 容), 我 们 在 这 里 就 不 讨 论 了。 事 实 上,Moby Dick Windows 并 不 恢 复 其 自 己 的 窗 口; 我 们 将 在 讲 到 DirectDraw 下 的 双 缓 冲 时 看 它 是 如 何 实 现 的。
程 序 中 的 多 任 务
---- 尽 管 Moby Dick DOS 在 使 用 中 断 处 理 程 序 时 展 示 了 内 部 多 任 务( 或 者 说 多 线 程) 的 一 种 原 始 形 式, 但 是 该 程 序 仍 然 没 有 突 破 DOS 的 单 主 题 特 性, 即 在 一 个 时 间 只 做 一 件 事 情。 有 些 DOS 程 序 的 确 作 到 了 真 正 的 多 线 程, 但 是 那 需 要 非 常 巨 大 的 编 程 工 作。Windows 95 SDK 使 这 项 工 作 简 单 了 许 多, 把 线 程 放 进 每 一 个 游 戏 开 发 者 的“ 锦 囊” 之 中( 如 果 读 者 还 不 熟 悉 这 个 概 念, 那 么 简 单 说 明 一 下, 一 个 线 程 就 是 程 序 的 一 部 分, 它 执 行 时 独 立 于 其 它 的 部 分, 并 且 不 需 要 与 其 它 部 分 同 步。 线 程 不 是 由 中 断 来 驱 动 的; 它 们 只 是 在 每 一 次 Windows 给 它 们 CPU 时 间 时 继 续 其 执 行。)
---- 在 下 列 情 况 下, 可 能 要 考 虑 实 现 独 立 的 线 程:
允 许 后 台 AI。 就 算 是 用 户 正 忙 于 来 回 移 动(moving pieces around)、 打 开 对 话 框 等 事 情, 计 算 机 也 能 够 考 虑 其 下 一 步。 处 理 这 类 线 程 非 常 方 便, 因 为 它 不 需 要 与 其 它 的 事 情 同 步。

预 先 加 载 数 据。 例 如, 当 玩 家 正 努 力 向 下 一 级 奋 斗 时, 使 一 个 线 程 负 责 读 取 文 件 并 准 备 好 游 戏“ 世 界”。

给 予 时 间 紧 迫(time-critical) 的 任 务 优 先 权。 我 们 将 在 后 面 会 到 这 个 主 题。
游 戏 循 环
---- 游 戏 循 环 的 概 念 在 各 编 程 环 境 下 都 比 较 相 似。 第 一 步, 需 要 获 取 输 入: 可 以 是 轮 询 它, 等 待 它, 或 者 在 它“ 运 行” 时 通 过 中 断 或 一 个 消 息 队 列 拦 截 它。 第 二 步, 处 理 该 输 入, 并 且 把 它 变 成 一 个 在 游 戏 中 有 实 际 意 义 的 动 作, 如: 使 飞 机 倾 斜 飞 行 或 小 卒 向 前 走 一 步。 然 后, 把 结 果 显 示 出 来。 当 然, 在 这 个 主 题 中 也 要 求 精 雕 细 刻 和“ 变 奏 曲”, 包 括 计 算 AI 的 移 动、 把 控 制 权 从 一 个 玩 家 移 交 给 另 一 个 玩 家、 检 查 胜 负 等 等。
---- 然 而, 在 Windows 和 DOS 下 实 现 循 环 的 机 制 迥 然 不 同。 每 个 Windows 程 序 都 建 立 于 一 个 消 息 循 环 之 上。 尽 管 一 个 游 戏 循 环 可 以 建 立 在 消 息 循 环 之 上, 但 是 这 两 者 仍 有 本 质 的 差 别。
Moby Dick DOS 的 循 环
---- Moby Dick DOS 演 示 了 一 种 简 单 的 游 戏 循 环, 在 这 里 我 们 所 做 的 工 作 是: (a) 检 查 是 否 有 什 么 东 西 要 移 动, (b) 移 动 它, (c) 显 示 结 果。
while (!gamedone)
//调用时间程序 -如果时间未到,则没有任何响应。
AhabMoved = Move_Ahab();
//仅当 Ahab没有移动时移动 Moby Dick。
//否则它们可能擦肩而过却不能拦截。
if (!AhabMoved) Move_Moby();
//如果有任何一个移动,更新屏幕,
//并检查是否有胜利和失败。
if ((MobyX != OldMobyX) || (MobyY != OldMobyY)
|| (AhabMoved))
{
UpdateScreen();
if ((MobyX == AhabX) && (MobyY == AhabY)

&& (painted))
{
gamedone = 1;
cprintf("\a");
cprintf("You win!");
}
if (TimesUp <= 0) { cprintf("\a"); cprintf("Time's up!"); gamedone="1;" } if (raw_key="=" MAKE_ESC) { gamedone="1;" progdone="1;" } } //结束更新 } //结束游戏内部循环 (while !gamedone) Moby Dick Windows的循环 从表面看来,好像没有多大的差别: do { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message="=" WM_QUIT) break; //唯一的退出循环的出口。 TranslateMessage(&msg); DispatchMessage(&msg); } else { if ((MobyX !="OldMobyX)" || (MobyY !="OldMobyY)" || (AhabMoved)) { UpdateScreen(); if ((AhabX="=" MobyX) && (AhabY="=" MobyY) && (painted)) { C "You caught Moby! Play again?", "Call Me Ishmael", MB_ICONQUESTION | MB_YESNO, 0); if (C IDYES) InitializeGame(); else break; } } //如果有人移动了 } //如果屏幕已更新 } //结束循环 while (TRUE);
---- 前 面 已 经 提 到 过, 这 里 没 有 检 查 是 否 运 行 超 时, 请 忽 略 它, 笔 者 在 Windows 版 中 未 实 现 它 是 为 了 避 免 令 人 烦 恼 的 中 断。 中 断 并 退 出 无 限 循 环 的 机 制 有 一 点 儿 而 且 并 不 重 要。 我 们 把 精 力 集 中 在 消 息 循 环 本 身, 所 以 把 其 它 的 无 关 代 码 都 删 掉, 只 留 下 最 基 本 的 部 分:

do
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else DoSomething();
}
while (TRUE)

---- 这 是 一 个 非 常 典 型 的 消 息 循 环。 唯 一 有 点 特 殊 的 地 方 就 是 它 使 用 的 是 PeekMessage 而 不 是 GetMessage。
GetMessage 与 PeekMessage 的 比 较
---- 为 什 么 要 用 PeekMessage 呢 ? 原 因 很 简 单,GetMessage 等 待 一 个 消 息( 就 像 _getch), 而 PeekMessage 不 是 这 样( 就 像_kbhit)。 请 考 虑 下 面 的 循 环:

while (GetMessage(&msg, NULL, 0, 0))
{
// 我 们 并 不 进 入 括 号 内 部, 直 到 有 一 个 消 息
TranslateMessage(&msg);
DispatchMessage(&msg);
DoSomething()
}
// 当 GetMessage 返 回 NULL 时, 退 出 该 程 序
return msg.wParam;

---- 在 这 里,DoSomething 不 会 完 成, 除 非 一 个 消 息-- 或 许 多 消 息-- 被 放 入 队 列 中 并 被 处 理。 如 果 DoSomething 恰 好 产 生 一 个 消 息, 例 如, 如 果 它 更 新 了 屏 幕 并 且 因 此 而 产 生 了 一 个 WM_PAINT 消 息, 那 么 好 了, 水 泵 注 水 后 将 开 始 启 动 了。 要 使 DoSomething 可 靠 地 完 成 其 工 作, 这 并 不 是 一 个 好 方 法, 它 使 代 码 有 点 混 淆, 但 它 工 作 的 还 不 错。
---- 相 比 之 下,PeekMessage 则 无 论 是 否 有 消 息 在 等 待, 只 要 检 查 一 下 消 息 队 列 就 完 成 其 操 作(yields the floor)。 在 我 们 的 例 子 中, 我 们 实 际 上 是 使 用 PeekMessage 来 处 理 消 息 的( 通 过 分 发 它 所 找 到 的 每 一 个 消 息 并 使 用 PM_REMOVE 参 数 从 队 列 中 清 除 它)。 同 下 面 同 样 有 效 的 代 码 相 比, 它 要 更 加 直 接:

if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if (!GetMessage(&msg, NULL, 0, 0)) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else DoSomething();

---- 在 这 里 有 非 常 重 要 的 一 点 要 说 明, 我 们 的 伪 代 码 DoSomething 是 独 立 于 消 息 的; 无 论 队 列 中 送 出 的 什 么 消 息, 甚 至 无 论 有 没 有 消 息 在 那 儿, 它 都 将 执 行。 在 Moby Dick 中, 我 们 将 屏 幕 更 新 和 胜 利 条 件 的 检 查 放 在 这 里, 因 为 在 这 里 检 查 一 个 或 多 个 消 息 被 响 应 后 是 否 需 要 更 新 屏 幕 或 是 否 达 到 胜 利 条 件 很 方 便。
---- 那 么, 消 息 循 环 就 是 游 戏 循 环 吗 ? 从 抽 象 的 角 度, 是 的, 因 为 它 是 大 的 齿 轮, 带 动 那 些 小 的 齿 轮。 但 是, 尽 管 把 一 些 函 数 调 用 放 在 此 处 可 能 比 较 方 便,Windows 编 程 规 则 却 要 求 任 何 响 应 一 个 消 息 的 动 作 都 应 该 放 在 消 息 响 应 程 序 中( 就 是 说, 放 在 窗 口 过 程 中)。 在 一 个 实 时 游 戏 中, 绝 大 多 数 的 动 作 发 生 在 一 个 或 多 个 WM_TIMER 消 息 响 应 程 序 中。 回 合 制 游 戏 则 常 常 在 输 入 消 息 的 响 应 函 数 中 做 大 量 的 工 作。
---- 事 实 上, 程 序 员 经 常 会 发 现 在 主 消 息 循 环 内, 除 了 标 准 的 翻 译 和 分 发 消 息 任 务 之 外 什 么 事 情 也 没 做。 如 果 这 样, 就 可 以 回 过 头 来 使 用 GetMessage, 因 为 除 了 响 应 消 息 之 外, 什 么 事 也 不 需 要 发 生。
---- 总 结 一 下, 实 现 循 环 有 两 个 关 键 点:
Windows 消 息 循 环 与 游 戏 循 环 不 相 同。 游 戏 循 环 依 然 存 在( 至 少 在 概 念 上 如 此), 但 是 同 DOS 下 的 情 况 相 比, 它 的 部 件 在 代 码 中 更 加 分 散。

如 果 想 要 在 消 息 循 环 内 执 行 任 何 独 立 于 时 钟 消 息 和 输 入 消 息 的 代 码, 请 使 用 PeekMessage 而 不 是 GetMessage。
页: [1]
查看完整版本: 经典游戏制作教程