游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

使用Game Maker创建Zool游戏的操作方法

发布时间:2011-05-19 00:45:31 Tags:,,

作者:Jacob Habgood

游戏邦注:本文摘自《The Game Maker’s Companion》一书的章节,主要讲述使用Game Maker重制16位游戏Zool的方法。

Gremlin Graphics曾为游戏世界带来很多视频游戏产品,如上世纪80年代的Monty Mole和Jack the Nipper,90年代早期的Premier Manager和Zool,以及90年代末的Actua Soccer和Loaded。

可能年轻的你之前没听说过《真宝珠小子》(Zool)这款游戏,但你若知道它是Amiga电脑的“招牌游戏”的话一定会大吃一惊,它在当时的名气堪比世嘉的《索尼克》和任天堂的《超级玛丽》。Zool是Amiga1200配套游戏中最热卖的产品,还被推荐到每天的主打游戏列表。当时还出现了Zool的续集——Zooz(Zool的女友)和Zoon(Zool的异形狗)。

然而,与当时一样流行的几款游戏不同,Zool的知名度随着Amiga平台的没落而逐渐降低了。不过,我们决定重新还原这款在平台游戏竞技史上风光无限的作品。

设计一个Ninjalien

1991年,Gremlin中有位名叫Adrian Carless的年轻美工接到了创作一个堪与刺猬索尼克匹敌的视频游戏角色。他后来成为Gremlin公司的游戏设计高手并在接下来的10年里一直是位多产的游戏设计先锋。所以,还有谁比他更有资格来解释Zool的起源呢。

“我们原先是要设计一个能在16位的日本游戏里具有影响力的平台游戏,还有一个能成为Amiga的吉祥物(之前都是空白)。工作室的老板Ian Stewart向我们简单描述说他想要一个外星角色,这个角色可以做出其它角色无法完成的事情。这可是个主题很广泛的话题——经过集体讨论后,我设计出了一个有着多维空间的忍者,只要我们知道它从哪里来,我们就可以将它呈现出来,它的名字是后来才想出来的。它最初叫做ZOON,但我又把它改为ZOOL(我从没想过Zuul,直到后来有人提起它)。

在外观方面,我们原先是希望它能在Amiga有限的游戏界面(48*48像素且只有16种颜色)中清晰地显示出来;其次考虑如何让它的形象栩栩如生。Zool看起来像是由2个几何球体以及几根木棍制作而成。我希望它能像《炸弹人》里的人物一样看起来很精简,用一点点突出显示来展示它的外表。

首先是眼睛,Zool就如只拥有很少装饰的细小人物,它的嘴巴(如果算有的话)藏在面具后面,我希望它有一双能传神的大眼睛。猫的眼睛看起来很灵活而且难以摸透,所以我们就给了Zool一双猫科类眼睛。(本来要设计3只眼睛的,但我们后来觉得还是传统的2只比较恰当)。它的眼睛也很明亮,我希望它能一直炯炯有神,所以玩家能看到它坚定的眼神,或者遇到危险时的害怕。由于它的眼睛占身体的比例很大,所以眼睛是Zool最有特色的部分之一,我甚至连设计Logo都是围绕着眼睛来做文章的。

Zool身上主要是黑色(忍者也是黑色的,对吧)和绿色(外星人默认的颜色,虽然我尝试过另外一些选择),以及一个来帮助显示它移动的亮关颜色。头部和身体的黑色线条突出它的旋转,手和脚上的红色用来跟踪它的四肢。四肢做成绿色主要是要防止它在做一些快速旋转时全身都和黑色混在一块。我们认为塑造出来的Zool形象与我们一开始所设定的快节奏的游戏主题结合得很好。最终任务形象和有着多维空间的忍者形象十分接近——有活力、多面手、又有点神秘。

图解 3-1: 我们所希望创建的Zool世嘉五维视图的屏幕截图

图解 3-1: 我们所希望创建的Zool世嘉五维视图的屏幕截图

Zool在Amiga上的成功意味着它也适合移植到其它游戏平台,每一个新版本的游戏控制系统和核心机制都各有千秋。我们将把SNES/世嘉五维版本作为主要参照标准,将其作为这款游戏的后续版本。因为没有打算完全照搬原版游戏,所以我们将结合Game Maker的优势,开发自己这个版本的游戏,这个版本的Zool将拥有以下一些行为特征:

*在平台的支持下,可以站立、行走、滑行、滑倒和踢蹬。

*在地心引力的影响下会有跳跃、降落和旋转。

*在进行以上所说的动作时还可以射击。

*粘附或爬行于垂直表面。

*在上述情况下会有死亡、刀枪不入和收集物品等特征。

我们会关注和Zool移动相关联的行为,因为它们是创造核心机制而使玩家获得游戏快感的原动力,让我们在开始动手前先整体概括下前期筹备工作。

长期挑战

开发一个和Zool一样复杂的游戏并不容易——即使在Game Maker上也是如此,它需要一个拥有专业程序员、美工和设计师的团队花费数月来创造一个原创游戏。当然他们可能没有Game Maker这个工具,但想在一个章节里尝试和解释整个游戏的开发也不现实,所以我们分成4个章节来阐述。除非你拥有和超人一样过人的集中力,否则也别想单兵作战就创作出整个游戏。在The Game Maker’s Apprentice是能找到一些花几个小时就能创造出来的游戏范例,但这在游戏开发领域里并不多见。我们这次是要制作一个在全世界的繁华商务街出售的专业游戏,所以会遇到更多的

挑战。你最好要有足够的集中力,建议你在每个章节结束后奖励一下自己的辛苦工作。我们同样也会奖励你——在每个新的章节开始时会有一页新的Zool的滑稽搞笑版面。

可供调用的资源

现在你应该要熟悉更多制造游戏的日常工作内容,比如载入和创造新的资源。用来制作Zool的资源确实很多,所以你会看到我们所提供的最初游戏版面——它拥有游戏界面、背景、一些物体甚至是测试空间。这些物体没有行为能力,所以游戏实际上也不能做什么,但我们会提供给你一些更简单的单调任务。我们还会从已经创作出来的内容继续下去,这样你就不会错过一些重要的细节。

以下的游戏界面来源于为游戏的世嘉五维控制版面而创作的原始涂画图像(游戏邦注:北美称之为世嘉五代)。

图解 3-2:一张世嘉五维的游戏界面版,它们在单色背景下显示出来的过程很有趣

图解 3-2:一张世嘉五维的游戏界面版,它们在单色背景下显示出来的过程很有趣

多格视窗

或许你会习惯性认为你的级别被划分为很多尺寸相同的方格,因为Game Maker就经常这样运行。原始的Zool游戏也将风景划分成很多方块。每个方块大概是16*16像素,以致在不同的整合里,

一小部分视窗可以在被重新安排后产生很多等级,在Zool时代,由于硬件平台上的记忆量有限,通过这种方法来从很小的视窗里构筑级别变得极其普遍。图3-3展示了《Sweet World》的视窗,它也是这款游戏里碰到的第一个敌对世界,我们将会在这本书里经常用到这种视窗。

Game maker支持视窗设置来表示等级。你可能已经用过,或许甚至在The Game Maker’s Apprentice里的Koalabr8也用过。视窗设置是用来创造风景的可视部分,但是这些视窗相互之间没有交互行为(不能有行为动作),所以处在顶部的不可见物体会掌握好交互行为。也就是说任何一组视窗可以充分利用相同的物体行为:不需要在你游戏里增加数百个物体,也能提供视觉的多样化。

图解3-3: 一些来源于SNES版游戏的视窗

图解3-3: 一些来源于SNES版游戏的视窗

探测视窗和空间

1. 从背景资源里打开tile_sweet。它有个Use as tile set选项,所以Game Maker自动将它细分成大概500个不同的16*16视窗,方便你重新组合来创造一等级的视觉构图。必须指出的是,不同于单一的游戏界面,视窗没有可移动的原点。他们的原点实际上是X=0,y=0;因此所有相应的界面振动蒙板(在平台集合里)的原点也是X=0,y=0,以便和视窗的精确度相匹配。

2.从空间资源里打开room_test察看可用于视窗设置的范例等级。如果你选择设置标签,你将看到整个空间的尺寸是2048*640像素,用滚动条来查看,它和原始Zool等级的大小不一样,但对于我们的测试这个大小已经足够了。

3.现在转到tiles标签(见图3-4)。从这里你可以看到我们的tile_sweet视窗设置显示在左手边的标签里,右手边是空间设置。你需要拓展标签的右边缘,以便可以看到视窗设置的所有视窗。你可以左击视窗设置里已经选中的视窗,在空间里左击来放置视窗的copy。与处理对象的方法一样,你可以通过使用鼠标右键来删除视窗。使用视窗设置可以创造一个风景区域,但之后要记得撤消你的操作改变(可用撤消按钮或在关闭空间时选择“不保存所做修改”)。现在你就知道自己需要多少时间和精力来将这些小的等级组成整体了。

4.此空间里还有两个分开的视窗图层:一个将在Zool后面出现的背景图层(1000的深度),一个将在Zool前面出现的前景图层(1000的深度)。通过在一些布景条目的前后位置呈现Zool,使游戏呈现更多的3D效果。你可以在Current tile layer的下拉菜单里点击挑选这些不同的图层编辑,也只能在目前选中的图层里进行增加或删除。

在此提示你只能看目前正在编辑的视窗图层,不去勾选在空间属性条里(看图3-4)里位于放大镜隔壁的下拉菜单里的选项。当tiles被选中时,只显示目前的视窗图层;当tiles未被选中时,所有的视窗都被隐藏起来。

5.现在让我们转向空间属性里objects标签以及未勾选的Show Tiles选项。所有美丽的图像都会消失,你也能看到定义着等级物理实体零件的绿色碰撞物体。所有这些碰撞物体有0级的深度,所以它们可以在两个视窗图层里呈现(游戏邦注:因为它们也未勾选Visible设置,所以当你在编辑程序里运行游戏时它们也是不可见的)。

6.虽然不好表达,但当我们通过放置增加的碰撞物体来让空间里的左右界限得到扩张,这种做法没有实际意义可言。我们想让空间像无限制的循环等级一样来工作,由此帮助Zool能在空间里平滑地缠绕在分界周围,而不会突然在空间外倒塌。这也意味着你突然会从Game Maker那收到信息说在空间外有很多物体,你是否想删除它们,很显然你要选择No。

图 3-4. 不在空间里展示视窗可让分别从前景和背景里分别浏览视窗图层,同时显示碰撞物体

图 3-4. 不在空间里展示视窗可让分别从前景和背景里分别浏览视窗图层,同时显示碰撞物体

7.最后,到Objects资源文件夹里打开平台群组,让物体自我检查。你将看到一系列的碰撞物体已经被创造并被指派到适当的界面。前三个”标准“物体,相信你在测试空间里已经看到很多这样的例子了:

obj_platform (绿色)——这个物体被用来体现组成供Zool行走的平台横截面。他们也被用来创造Zool不能穿越的地面坚硬部分的下层。

obj_wall(蓝色)——用来表示组成那些供Zool攀登和依附的峭壁的垂直墙面。

obj_ledge(一般是看不见的紫红色的薄线)——用做Zool跳上时能穿越,也能在上面行走的水平平台。

接下来3个只被作为父对象使用,所以你永远不会看到放置在风景之上的这些物体的实例。它们通过有效的途径将相似的物体组合在一起。

obj_ramp(黑色)——就是用来上下斜走的平台。你将在匝道组合里找到放置在等级里的实际匝道(即拥有倾斜的碰撞面具)。

obj_slope(灰色)——斜坡是由冰块制作而成的陡峭的匝道,它很光滑,你将在斜坡组合里找到放置在等级里的真正斜坡(也有倾斜的碰撞面具)。

obj_solid(不是子画面)——这是一个用来在风景里将所有物体当做坚实部分的父对象。

8.注意到所有匝道组合里的对象都把obj_ramp设置为他们的父对象,以及所有斜坡组合里的对象都把obj_slope设置为他们的父对象,你便能想到这种“父子关系”只是某种意义上的关系。故所有的匝道都是“某种意义上的obj_ramp”,所有的斜坡都是“某种意义上的obj_slope”。

9.还有很多专为另外碰撞对象设置的“某种意义上”的关系。obj_slope对象(代表冰冷的斜坡)将obj_ramp设置为它的父对象,因为斜坡也是“某种意义上”的光滑的匝道。obj_ramp和obj_ledge都把obj_platform看作是他们的父对象,因为他们也是“某种意义上的”平台。最后,obj_platform和obj_wall都把obj_solid看作是他们的父对象,因为他们也是“某种意义上的”坚硬物体。图3-5展示了用于风景对象的存在父子对象关系的组织体系图。

10.关键是这让我们可以拥有对象的碰撞看作是“某种意义上的”某些东西。比如,一个拥有obj_solid的碰撞包含每一个风景对象,一个拥有obj_platform的碰撞包含来自防御墙的每个部分。

图3-5:“某种意义上的”的层次体系图是由每个对象的母体设置定义的

图3-5:“某种意义上的”的层次体系图是由每个对象的母体设置定义的

父子对象关系

如果对父子对象关系的使用看起来有些混淆,那么就试着记住在Game Maker里为某个对象给出父对象会产生两种不同的效果。

1.子对象继承了其父对象所有的事件和行为(除非它有一个相同的事件,在这种情况下子对象的事件就覆盖住其父对象的事件了)。

2.所有拥有父对象的碰撞事件(比如obj_solid)都是由其子对象(比如obj_platform)的碰撞触发而成的。

一个外星的状态

你应该不会对我们将在此游戏中再次充分利用状态对象感到惊讶。当我们制作Fishpod时,我们有个清晰的准则:每个不同的行为或者现象都由他自己的状态对象来负责。这对于一般游戏如Fishpod来说没问题,但当事情比较复杂的时候,实际情况也就不一样了。我们一开始就知道Zool有7个单独表象动作,但我们需要添加几个攻击动作。如果我们运用和外部动作一样的办法,则有可能意味着Zool会有10个甚至更多的状态对象。在这些不同的表象中会有很多相同的行为,同一动作会产生很多重复和微笑差别的视觉效果(比如,在Fishpod游戏中看obj_pod_jumping和obj_pod_falling的平台碰撞事件)。我们的事件将包含比在Fishpod里更多的动作,而且如果复制型工作可以修改的话将非常危险:忘记修正错误就好像是在向别人推荐那些错误。

现在我们在状态对象中运用Parents来减少重复的编码和分享共同行为。为一个对象制造出一个父对象,在要求有区别性行为时,允许它继承所有它的事件或行为,或者用自我覆盖那些事件。

然而,复杂的继承体系可能很快变得难以跟随,因此如此多的父对象以及继承的使用会因为太多的状态对象而成问题。

但坏处就在于能使用多少的机制如状态机械指令或继承多少操作程序,这并没有强硬或快速的准则来指引你,而且你必须培养这方面的直觉。然而,你应该时刻记住他们的出现是为了让你的生活更加便利——这不同于其它的程序理念。“因为你能”而去使用parents,状态对象以及其它的程序机制,这种想法有些偏颇。你应该思考它是使你的生活变简单了还是更难了!

最后,我们要试着将我们的状态组合成一些包含共同行为的状态对象。有8个与移动相关的可组合成4个不同状态对象的状态(一个用于每个表面动作,两个用于攀爬)。与一些小数目的parenting相结合来处理和所有状态相同的行为,我们相信这将提供一个有效和易管理的结果:

*obj_zool——用于所有Zool状态对象的父对象,它将管理在所有状态里和Zool行为相似的动作。

*obj_zool_land——将管理所有与陆地移动相关联的状态和行为:站立、行走和滑行。

*obj_zool_air——将管理所有与空中移动相关联的状态和行为:跳跃和降落。

*obj_zool_wall——将管理所有与墙壁移动相关联的状态和行为:攀附和攀登。

*obj_zool_ice——将管理在冰滑斜坡上向不同方向滑行的状态和行为。

我们也能绘制出这些状态对象以及他们之间的关系,用来作为完成那些状态行为的向导(见图3-6),这看起来很复杂,对吧?别担心——你不用去记忆它,绘制出一个这么复杂的图表是为了帮助我们在某些时候集中在一个状态上,没有必要去担心结构本身有多复杂。在一个图表里要去捕捉和这个系统一样复杂的详细资料是不可能的,但它应该能帮助我们计划总体思路。

图解 3-6: 一个有限的状态机械图表,帮助我们实施Zool

图解 3-6: 一个有限的状态机械图表,帮助我们实施Zool

红色箭头提示碰撞,蓝色箭头提示玩家之间的互动。“支持/不支持”表示Zool的下面是否有个平台。

提示——我们准备以在游戏中定义一些可用的常量来开始。和Fishpod例子一样,除非注册了Game Maker8的Pro版本,不然你是无法创造这些常量的。如果你正在使用Game Maker的Lite版本,那么在Zool对象里,你也能通过在Create事件里使用Set Variable(变量设置)动作(control标签)来达到同样的效果。每个变量都要和常量一样拥有同样的名字和数值。但有一点和常量不同,这些变量只能在Zool对象里存取,而常量是全局性可用,但这对于例子的剩余部分没有区别。

外星人的前面几个步骤

有了一个方案,我们就可以开始为游戏设计一些功能。虽然是个全新的思路,但我们将从为8个不同的移动状态设置常量开始,以及为4个不同的状态对象创建空白对象。

设置状态常量和状态对象

1. 确保你有一个zool0.gmk的纯版本,然后在“资源”目录里“定义常量”。

2. 创建下面的状态常量以及相应的数值:ZSTATE_STAND=0,ZSTATE_WALK=1,ZSTATE_JUMP=2,ZSTATE_FALL=3,ZSTATE_CLIMB=4,ZSTATE_CLING=5, ZSTATE_SLIP=6,ZSTATE_SKID=7。为了在后面的游戏里用其它字母来定义状态,我们已经事先用Z(表示Zool)来前缀这些状态常量。

3. 打开文件时,创建两个左右常量:FACE_LEFT=-1,FACE_RIGHT=1,注意我们用负数值来代表左边,正数值来代表右边,这将很有用,因为我们可以在x-axis(正如x-1是左边的像素,x+1是右边的像素)来充分利用这些数值和移动的关系。

4. 创建一个叫做obj_zool的新对象,给它spr_zool_stand_right的界面,并将它的深度设为-1(就在0前面)。

5. 多创建4个对象资源叫做obj_zool_land,obj_zool_air,obj_zool_wall以及obj_zool_ice。给每一个对象相应的子画面:陆地行走、空中跳跃、攀爬墙壁和冰上滑行。子画面的朝向不是很重要,但如果你使用了错误的子画面,那么你创作出来的Zool就有可能不会那么栩栩如生了。设它们的深度为 -1并把obj_zool作为他们的父对象。

6. 打开测试空间,把一单独的obj_zool放在空间左边的底部以便它正站在地板上面的半空中。

现在让我们为Zool创造一些简单的行走功能。我们会先设置父对象——它和所有Zool的状态对象拥有一样的功能,所有的子对象将从其父对象中继承事件和动作。我们为所有的状态对象制造的父对象平台,其主要功能是为Zool的当前状态绘制合适的子画面。父对象也会成为在每一个图层上创造而成的第一个Zool对象,因此我们也会运用它的“创造活动”来为每个变量设置默认值。

Zool的父对象

1. 为obj_zool添加一个“创造活动”,并通过“设置变量动作”(控制标签)来使变量面向FACE_LEFT。我们会和之前一样使用这个变量来跟踪Zool的朝向,在此将它设置左边方向,这样当Zool开始一个图层时都会默认朝左。

2. 在main1标签里选择“改变例子”动作,将其更改为obj_zool_land,并将“开始事件”设为yes。最后一个选项确保目前对象(obj_zool)的“破坏”和我们将要改变的对象(obj_zool_land)的“创造”都是此动作的一部分(在那个命令下)。过去里我们只提到“创造”一次,正如我们不会经常使用“破坏”一样。

3. 添加一个“另外”,“外面空间”事件,并把“Wrap Screen”动作(“移动”标签)以及“方向”设置为水平位置。我们有一个相对小一点的空间,所以这会协助我们将可用的空间最大化。要记住我们已在空间的边缘外添加一个平台对象,以保证Zool能平滑地弯曲。

4. 添加一个“绘制”活动。将此“绘制”活动添加在父对象里,不但可以关闭这个对象的绘制功能,也能关闭其所有子对象的绘制功能。因此下一步我们将为所有不同状态的子画面添加绘制活动工具——否则Zool将会消失。

5. 包含一个“测试变量”动作(控制标签),用来测试变量朝向是否等同于FACE_RIGHT的数值。

6. 立即用“起始块”来紧接着上一个动作,在测试过程中将下面的动作组合在一起。

7. 包含一个“测试变量”动作,用来测试变量状态是否等同于ZSTATE_STAND的数值。

8. 立即用“绘制子画面”(绘制标签)来紧接着上一个动作,用来在相应的位置X=0,Y=0绘制spr_zool_stand_right。

9. 包含一个“测试变量”动作(控制标签),用来测试变量状态是否等同于ZSTATE_WALK的数值。

10. 立即用“绘制子画面”(绘制标签)来紧接着上一个动作,用来在相应的位置X=0,Y=0绘制spr_zool_walk_right。

11. 添加一个“结束块”(控制标签)动作,来终止所有活动的组合行为。

12. 现在重复5-11的步骤,用FACE_LEFT来代替FACE_RIGHT以及左朝向的子画面来代替右朝向的子画面。完成后,此事件将包括图3-7显示的动作。

图解 3-07:Zool父对象中绘制活动的当前状态

图解 3-07:Zool父对象中绘制活动的当前状态

提示——如果你在那个时候运行游戏,它将马上退出。为什么?你可以这样思考:obj_zool的“创建”行为将实例改变成obj_zool_land,因此分别进行着obj_zool的破坏行为和obj_zool_land的创建行为。由于obj_zool_land从obj_zool中继承创建行为,它的创建行为也将变成obj_zool_land。此产生了一个无限循环的动作,所以Game Maker会中止游戏。

Zool的陆地状态对象

1. 重新打开obj_zool_land并添加一个“创造”活动,包括一个“设置变量”的动作(控制标签)来将变量状态设置为ZSTATE_STAND。

2. 添加一个“重力设置”(移动标签),将方向调整至270(向下)并将重力调整为0(关掉)。所有陆地上的移动都会和停止了的重力协同工作,这使事情变得简单。

3. 添加一个“摩擦力设置”(移动标签),将摩擦力设置到1。这为Zool添加了一个阻力,可以通过每次降低1像素来降低它的速度。

4. 添加一个“垂直速度”动作,将垂直速度降低到0。确保Zool在之前的状态里能停止任何垂直运动。

5. 添加一个键盘、左边事件和一个水平动作速度(移动标签),将水平速度设置在一个相应的数值——-2.5。当左边按键被锁住时,这能迫使Zool向左边移动。此移动力量比摩擦力量要大,所以Zool每一步会增加1.5像素的速度。

6. 添加一个“变量设置”动作(控制标签),将“变量”状态设置为ZSTATE_WALK。

7. 添加另一个“变量设置”动作,将“变量”朝向设为FACE_LEFT。

8. 为键盘重复5-7的动作,右边事件包含同样的3个动作。这要求将水平速度的关系值设为2.5,变量状态设置为ZSTATE_WALK以及变量朝向设置为FACE_RIGHT。

9. 添加步骤、开始步骤活动以及一个设置变量动作来将变量状态设置为ZSTATE_STAND。你可能会觉得将此放置在“开始步骤”里,会意味着Zool经常在站立状态里以被绘制的形式终止,但我们将在下一个环节里解释为什么这种情况不会发生。

10. 为obj_solid添加一个碰撞事件。我们在不同碰撞对象里建立的parent继承模式意味着obj_solid将包含碰撞对象,以及固体对象(平台、墙壁、窗台、匝道和斜坡)中的任何一种。

11. 添加一个“变量设置”动作(控制标签),设Variablex为xprevious。

12. 添加另一个“变量设置”动作,设Variabley为yprevious。这两个动作能使Zool在发生碰撞之前回到位置上。

13. 添加一个“移动联系”动作(移动标签),和“方向”动作来设置方向,最大值设为-1,以及所有对象的对立设置。

14. 最后,添加一个“水平速度”动作并将水平速度设为0。这为我们提供了和Fishpod游戏里一样的碰撞事件。现在的obj_zool_land对象应该要和图 3-8一样。

图解3-08:陆地Zool状态对象的当前状态

图解3-08:陆地Zool状态对象的当前状态

提示——如果你现在运行这个游戏,那么之前的问题已经自我解决了。obj_zool_land现在有它自己的创建活动,能覆盖它的parent;所以避免再次发生无限循环的现象。

前进命令

在前进之前,我们有必要在Game Maker里看下活动的命令,因为它对解释不同活动的整合如何工作极其重要。如果你曾经好奇过为什么在Game Maker里有3种不同的步骤事件种类,这应该能有助于解释这种情况。在每个游戏步骤里,Game Maker为不同的对象执行不同的活动。然而,在进入下一个事件之前,它为每一个对象运行每一个活动。它所采取的命令如下:

*所有对象的任何一个开始步骤事件。

*所有对象的任何一个预警事件。

*所有对象(在命令里)的任何一个键盘、按住键盘以及松开键盘事件。

*所有对象(在命令里)的鼠标按钮、按住按钮以及松开按钮事件。

*所有对象的任何正常步骤事件。

*【这时,所有事例基于他们目前的速度和方向,被设置在新的位置上】

*所有对象的任何碰撞事件。

*所有对象的步骤结束事件。

*最后,所有对象的绘制事件。

有件你开始在Game Maker里创作游戏时被遗忘的事情是,什么时候一个特定的按键没有被按住。你在键盘里、左边事件里开始移动某些东西,你又添加键盘、事件来使它停止。 不幸的是事件只在键盘上没有任何按钮被按住时才听从使唤。所以当你开始将对象移向左边时,你可以通过按住键盘上的任何按键来使它继续向做移动——甚至是表示向右的这个箭头!同样,在所有状况

下使用松开键盘事件的解决办法也很费事。

因此针对Zool,我们已经利用事件命令的优势来解决这类难题。我们在开始步骤事件里将状态设置为ZSTATE_STAND,然后创造键盘,左边和键盘,右边事件,来将状态重新设置为ZSTATE_WALK。如果没有按键被按住,那么此状态会在任何步骤、碰撞、结束步骤或绘制事件里保持着ZSTATE_STAND,因为他们发生在键盘事件之后。左右按键将适当地设置状态,而其它任何按键将对结果没有任何影响。

提示——事实上,在Game Maker8的Pro版本里,你可以添加你自己的触发事件,来侦测你喜欢的按住/松开按键组合。

空间视图

如果你那时想试着运行下游戏,你将发现你的整个空间会呈现在一个巨大的窗口里。幸运的是,Game Maker提供了一种浏览方式,这样你就能改变一次能看到的空间多少;甚至可以通过空间周围的特殊对象来自动浏览空间。我们知道整个测试空间的尺寸是2048像素宽x640像素高(看图3-9),但我们每次想看多少像素呢?Zool的原始版本(Amiga)分辨率只有320×256像素,这无法提供一个宽阔的Zool周围环境。我们将试着大方一些,调整我们的纵横比来适应宽屏检测,如此提供给玩家一个512×300像素的空间浏览窗口。然而,当代的检测分辨率比它还要高,所以这造成了在你的桌面上出现的窗口还是相当小。因此,我们决定将此浏览窗口的分辨率双倍提高到1024×600像素——要是你有一个真正高分辨率的桌面并且想要完全效果,你将经常收到全屏显示的效果。

建立空间视图

1. 从空间资源文件夹里打开room_test并选择视图标签。检查启用视图作用的选项,从下面的窗口里选择View 0。窗口下面的设置现在都参考View 0。

2. 检查“当空间开始时可见”这个选项,将空间里的视图设为X=0,Y=0,W=512,H=300。你会在空间里看到一个长方形的轮廓,这就是将被复制的视图的边界。视图里X和Y的位置事实上并不相干,因为我们将从下面的Zool对象位置来确定此视图的位置。

3. 将屏幕的端口设置为X=0,Y=0,W=1024,H=600,所以如果你觉得之前步骤是复制,那么这个步骤就是粘贴了。它将把X和Y的位置粘贴在玩家的窗口里,但将它原始的宽度和高度扩展了两倍。

图角3-09:整个Zool的测试空间,连同出现在左上角的玩家浏览区域的尺寸

图角3-09:整个Zool的测试空间,连同出现在左上角的玩家浏览区域的尺寸

图角3-10:此视图的浏览画面设置

图角3-10:此视图的浏览画面设置

4. 从靠近Object following的下拉菜单里选择obj_zool,将Hbor(水平边框)设置为256,Vbor(垂直边框)设置为160,Hsp和Vsp(水平和垂直速度)都设为-1。因为obj_zool是所有其它Zool对象的parent,所以此视图将适用于Zool对象的任何一种。边框值决定Zool的间距需从视图未开始移动时的视图边缘开始算起。将这些数值设置为视图的宽和高的一半意味着Zool将一直处于视图的中心位置。速度数值允许你为视图的移动速度设定一个限制值,-1数值表示没有限定速度。

5. 你现在的视图标签设置应该和图3-10一样。

现在当你运行你的游戏时,你应该有了一个更好视图的Zool,因为它在图层周围移动(虽然是在半空中并且速度很快),当碰到墙壁时它会停止(但能穿过匝道)。如果还有其它问题,你将会在CD的章节3/游戏指南里的zool1.gmk文件里找到关于这个阶段的游戏版本。

让忍者着陆

很显然,在稀薄的空气里行走不合逻辑,所以我们将对Zool的速度作一些限制,而后我们会让它在陆地上行走。从前面的章节里我们懂得要限制移动物体的速度,以免它移动过快而穿过其它物体(与时间抽样没有关联的问题)。我们将在步骤事件里解决这个问题,因为只要键盘事件一改变Zool的速度,这个问题就会发生。我们将把步骤事件放在obj_zool里(父Zool对象),因为此事件将会被所有的Zool对象所继承——这节省了我们在每个对象里复制相同动作的时间。

限制Zool的速度

1. 使用资源目录里的“定义常量选项”来定义两个新的常量(如果你使用的是Game Maker Lite,就在obj_zool创造事件里定义两个新的变量)。将都把MAX_HSPEED和MAX_FALL_SPEED设为14.

2. 为obj_zool添加步骤和步骤事件,并添加一个变量设置动作。用数值来设置变量的垂直速度和数值的min类型(MAX_FALL_SPEED,vspeed)。这里的min是Game Maker的一个功能,它返回参数的数值(数值用逗号隔开)。所以当垂直速度小于MAX_FALL_SPEED时,min功能将返回垂直速度的数值,并且此反映将把垂直速度设为垂直速度的数值(事实上没发生任何变化)。可是,当垂直速度大于MAX_FALL_SPEED时,min功能将返回MAX_FALL_SPEED,并且此反映将垂直速度设为MAX_FALL_SPEED,如此,min功能限制Zool的下降速度到最大值每步骤14像素。

3. 添加另外一个变量设置动作,这一次设置变量的水平速度和数值的median类型(-MAX_HSPEED, hspeed, MAX_HSPEED)。这里,median是个不同的功能,它计算当水平速度在以尺寸为顺序时被安排的参数的中间值。所以当水平速度小于MAX_SPEED而大于-MAX_HSPEED时,中间值将会是水平速度。然而,如果水平值大于MAX_HSPEED时,中间值将变成MAX_HSPEED;而且如果水平速度小于-MAX_HSPEED时,中间值就变成了-MAX_HSPEED。这样我们可以把水平速度保持在每步骤-14像素和+14像素中间,只需运用单一动作。

注意——我们必须记住如果我们为其它每个Zool对象(把obj_zool作为它的父对象)创造一个步骤事件,那么新的步骤事件将会代替这个已被继承过的步骤事件。这意味着我们的速度夹合动作将不再为对象所用,Zool将很有可能再次快速移动。

如果你正在玩这款游戏,Zool会受限于敏感的奔跑速度——还会注意到当它快速移动时如果你松开按键,它会滑行被慢慢停下来。这是由于当你没有运用外力来迫使他移动时,摩擦阻力会让它慢慢停下来。下面,我们开始为obj_zool_air实施行为,并在平衡里添加一点重心引力。

我们的目的是想把一个“正常”的重心引力设置为每个步骤向下降落2个像素。从逻辑上讲,obj_zool_air必须总是和相同的重心引力相同,这是不可改变的。然而,很多这种类型的游戏平台添加一个功能是,当你继续按住跳跃键而不是立即松开按键,此游戏角色会跳得更高。所以我们将重心设置得弱一点(每步骤1.5像素),但玩家以开始按住空格键的时Zool能跳得更高一点,但只要玩家松开空格键,它马上回到正常的重心引力。所以为了得到跳跃的最高高度,玩家必须在Zool跳跃的期间一直按住空格键。

Zool的空中状态对象

1. 重新打开obj_zool_air并添加一个创建事件。包括一个变量设置动作(控制标签),用来设置状态的变量和ZSTATE_JUMP的数值。

2. 添加一个“设置重心引力”动作(移动标签),将方向设为270,重力设为1.5。此设置确保Zool在跳跃时,每个步骤的最初降落值是1.5像素。

3. 添加一个“摩擦力设置”工作并将摩擦力设为0。我们不希望空气中存在任何阻力。

4. 添加一个步骤、结束步骤事件和变量测试动作(控制标签),将变量设置为垂直速度,数值设为0,操作设为比以往要大。

5. 立即通过“起始块”来紧接着这一步,将以下的动作组合在一起。

6. 添加一个“变量设置”动作,将变量设为状态,数值设为ZSTATE_FALL。

7. 添加一个“引力设置”动作(移动标签),将方向设为270,重心引力设为2,这个设置确保Zool在降落时每个步骤的向下拉力是2个像素。

8. 立即通过“结束块”来继续下一个动作,用来结束组合在一起的事件。

9. 通过obj_solid来添加一个碰撞事件。此事件里的前面4个动作将和obj_zool_land里的obj_solid非常相似。你需要将Zool设置回归到它的上一个位置,移动它到目前方向里的接触位置(与所有的对象相反),然后停止它的移动。试着自己做这个动作并回到obj_zool_land(或者图 3-8)看一下,以便确保你的步骤是正确的。你的最后一个动作必须是个固定移动动作,而不是水平速度,来在两个方向停止移动。

10. 在这4个动作之后,添加一个检查对象动作(控制标签)来检查对象,obj_platform的相对应位置是X=0,Y=1(在Zool下面)。

11. 紧接着下一个动作时“改变例子”(main1标签),将其改变成 obj_zool_land并且表现事件(损坏或创造)。这将使Zool遇到碰撞后回到陆地状态,如果他受支撑于一个平台。

12. 添加松开按键、空间事件和包含一个引力设置动作(移动标签),将方向设为270,重心引力设为2。

此松开键盘事件看起来有点奇怪,因为没有相对应的按下键盘事件,但是玩家在Zool跳跃这个状态时已经按下空格键了。当玩家松开空格键来创造一个高度跳跃效果时,此松开按键事件设置使重心引力回到正常状态。这里我们需要做一些改变来使obj_zool适应新的对象。

修改Zool的Parent对象

1. 重新打开obj_zool并再次选择绘制事件。在FACE_RIGHT的组合动作里,添加一个新的变量测试动作(控制标签),来检查变量状态是否等同于ZSTATE_JUMP的数值。

2. 紧接着这一个步骤,添加“绘制子画面”动作(绘制标签),将spr_zool_jump_right绘制在X=0,Y=0的相对位置上。

3. 添加另一个“变量测试动作”(控制标签),来检查变量状态是否等同于ZSTATE_FALL的数值。

4. 紧接着这一个步骤,添加“绘制子画面”动作(绘制标签),将spr_zool_fall_right绘制在X=0,Y=0的相对位置上。

5. 现在运用spr_zool_jump_left和spr_zool_fall_left在FACE_LEFT的组合动作里创造这4个新动作的左边锋等效量。绘制事件的动作要和图3-11一样。

6. 挑选obj_zool的创造事件并编辑改变例子的动作,将其改变为obj_zool_air rather,而不是obj_zool_land。

如果你现在运行此游戏,那么Zool应该会正如你期望的,下降到地面并在上面奔跑,但我们真的跳上去来看我们新的动作的效果如何。这要求对陆面状态对象做一些改变。

图解3-11:Zool的parent对象的绘制事件的新状态

图解3-11:Zool的parent对象的绘制事件的新状态

修改Zool的陆面状态对象

1. 重新打开obj_zool_land并添加一个新的按住按键、空格事件。包含速度垂直动作(移动标签),将垂直速度设为-22。

2. 包含一个改变例子动作(main1标签),将其改变为obj_zool_air和表现事件(破坏和创造)。

3. 添加一个新的步骤、结束步骤事件。记住此事件将在对象所有的移动和碰撞已经解决后,但被绘制之前发生的,所以如果它的新位置有要求的话,这是一个改变对象状态的好地方。

4. 包含一个检查事件动作(控制标签),来检查对象、obj_platform,不是开始在X=0,Y=0的相对位置上(在Zool下面)。

5. 紧跟着下一个步骤——改变例子动作(main1标签),将其改变为obj_zool_air,并表现(破坏或者创造)事件。这将允许Zool在没有平台的支撑下回到空中状态。

运行此游戏,你应该能看到Zool的第一个移动版本。你只能与固体以及有着甜蜜外壳的风景相互动,但如果你细心的话,你可以跳上最近的平台并再次落到地面。你将会在CD的章节3/游戏指南里的zool2.gmk文件里找到关于这时候的游戏版本。

祝贺你——第一部分结束了!

你已经走完了创作Zool游戏的1/4路程了!你已经为剩下的章节掌握了很多核心概念并完成一些重要的基础工作了。你已经纵览了外星风景,并且为Zool状态对象的完成规划好蓝图。你已经逐渐明白令人费解的parent继承理论,并协助我们的跨维忍者来开始它在美好星球上的前几步(跳跃)——这些都将被你的相机视图记录下来。

很显然,我们还有很长的路要走,但是,在你离开之前,你最好能够仔细想想在Zool滑稽的世界里将会发生什么,所以你或许会迅速浏览下一个章节,看看接下来会发生什么事情。事实上,会有一个关于一个运动员和一个骑士正和巨大的骨骼手战斗的有趣画面。然而,肯定会有一个完美的理由来解释为什么没有必要再读下去。(本文为游戏邦/gamerboom.com编译,如需转载请联系:游戏邦

Excerpt: Apress’ The Game Maker’s Companion – Zool: Taking It to the Nth Dimension

By Jacob Habgood

[This chapter from Apress' The Game Maker's Companion you'll learn how to reconstruct popular 16-bit franchise Zool in Game Maker. Visit apress.com to check out the full book and many more game development titles.]

Gremlin Graphics was responsible for bringing a menagerie of video-game franchises to the gaming world, including the likes of Monty Mole and Jack the Nipper in the 1980s, Premier Manager and Zool in the early 90s, and Actua Soccer and Loaded before the turn of the millennium. If you’re young enough not to have heard of Zool, then you may be surprised to know that he was once seen as the gaming mascot for Amiga computers in the same way that Sonic was for Sega and Mario is for Nintendo. Zool was

a best-selling title that was bundled with every new Amiga 1200, as well as being ported to all the major game consoles of the day. There was even a sequel that featured Zooz (Zool’s girlfriend) and Zoon (Zool’s alien dog).

However, Zool’s popularity declined with that of the Amiga platform he was associated with and, unlike his contemporaries, he’s not been seen or heard of since. Until now, that is, for we will continue our journey by reviving this lost icon of platform gaming history for one last candy-fuelled excursion…

Designing a Ninjalien

Back in 1991, a fresh young artist at Gremlin was asked to come up with an idea for a video-game character to rival Sonic the Hedgehog. That artist’s name was Adrian Carless. He went on to become Gremlin’s lead game designer and spent the following decade as the prolific studio’s game design front-man. So who better then to explain the origins of Zool than the man himself:

We originally set out to make a platform game that took influences from early 16-bit Japanese games, with a character that could become a mascot for the Amiga (which we saw as a vacant position). The Studio boss, Ian Stewart, gave us a simple brief: he wanted an alien character, and he wanted him to do things that no other character could. This was a pretty wide open theme, so after a bit of brainstorming I came up with the ‘Ninja of the Nth Dimension’ line: the theory being if we knew where he came from, we could start to visualize him. His name came last of all. I think he was originally called ZOON, but I changed it to ZOOL (and never thought about Zuul in Ghostbusters until it was mentioned later).

Visually, the first goal was to make him easy to read in the confined sprite constraints imposed by the Amiga: 48 x 48 pixels and just 16 different colors. The second goal, which fell out of the first, was that he’d be easy to animate. When you look at Zool, he’s made of two basic geometric spheroids, with a few sticks. I wanted him to have a stripped down look, similar to that seen in Bomberman, with just a little more highlighting to help define his shape.

First came the eyes. As Zool was such a minimal character with few adornments, his mouth (if he had one) hidden behind his mask, I wanted his eyes to be as large and expressive as possible. Cats are agile and inscrutable, so he got a pair of feline eyes (three, the first time I drew him, but we decided the traditional two were better). His eyes were also his brightest feature. I wanted them to ‘pop’ at all times, so the player could see the determined look in his eye, or the look of panic when he was precariously balanced. As his eyes were so large and prominent in proportion to the rest of him, they pretty much became his stand-out feature by default, and I even designed the logo based around them.

Zool would be a combination of black (ninjas wear black, right?) and green (default alien color — though I tried a couple of alternatives), with a highlight color to help sell his movement. Black stripes around both his head and body helped with rotation animations, and the red at his wrists and ankles helped with tracking limbs.

The limbs were made green to prevent him turning into a black mass when he did some fancy rotation. I think the end result fitted well into the fast-paced game we were attempting to create. The final character was dynamic, versatile and a tad enigmatic — everything you want from a Ninja from the Nth dimension.

Figure 3-1. A screenshot from the Mega Drive version of Zool to show what we’re aiming for

The success of the original Zool game on the Amiga meant that it was ported to numerous other gaming platforms as well. Each new version had a slightly different interpretation of the game’s controls and core mechanics. We will actually take the SNES/Mega Drive versions as our main reference point, as they were the later versions of the game. Yet we’re not aiming to be completely true to the original, so we will mix and match to suit Game Maker’s strengths as well. So in our version of the game, Zool will have the following behaviors:

Standing, Walking, Skidding, Slipping, and Kicking while supported by a platform.

Jumping, Falling, and Spinning under the influence of gravity.

Shooting at the same time as any of the above.

Clinging to and Climbing vertical surfaces.

Dying, Invulnerability, and Object Collection at the same time as any of the above.

We will concentrate on Zool’s movement-related behaviors to begin with, as they are central to creating a core mechanic that is fun to play. But before we get down to work, let’s get an overview of the job ahead.

Long-Term Challenge

Making a complex game like Zool is not an easy task — even in Game Maker. It took a team of professional programmers, artists, and designers many months to create the original game. Of course, they didn’t have Game Maker, but it would still be impossible to try and explain the development of the whole game in just a single chapter.

That’s why we have split the game into four chapters that divide it into more manageable chunks. Unless you have super-human powers of concentration, then you shouldn’t expect to create the whole game in a single sitting, either. Example games like those found in The Game Maker’s Apprentice were designed so that they could be made in just a few hours, but this is very unusual in game development. This time we are recreating a professional game that was sold in high-street stores all around the world, so it’s undoubtedly going to be more of a challenge. It’s going to require your full concentration to succeed and we recommend that you reward yourself with a treat (and a break) at the end of each chapter. We’ve even included our own little reward for you, in the shape of a new page from the Zool comic strip at the beginning of each new chapter.

Ready-Baked Resources

By now, you should be very familiar with the more routine tasks involved in making a game, such as loading and creating resources. There are going to be a lot of resources involved in making Zool, so you’ll be relieved to hear that we provide an initial version of the game that has all the sprites, backgrounds, some objects, and even a test room already created for you. None of the objects have any behaviors, so the game doesn’t actually do anything yet, but we’ve saved you some of the simpler, monotonous tasks. All the same, we’ll begin by looking at what has already been created so that you don’t miss out on any important details.

These sprites came from the original Deluxe Paint image files created for the SEGA Mega Drive console version of the game (known as the SEGA Genesis in North America).

Figure 3-2. One of the original sprite sheets from the Mega Drive version of the game. It is interesting to see how they were laid out in a 48×48 grid against a single color background (lines added)

Kitchen Tiles

You are probably quite used to thinking about your levels being divided into a grid of equal-sized squares, as this is the way that Game Maker usually works. The original Zool game also divided the landscape into squares. These were only 16×16 pixels so that a relatively small number of tiles could be rearranged in different combinations to produce a wide variety of levels. In Zool’s era, it was extremely common to construct levels out of very small tiles in this way because of the limited memory available on the hardware platforms. Figure 3-3 shows tiles from the Sweet World, which was the first enemy world encountered in the game and the one we’ll be using in this book.

Game Maker supports tile sets, as a way of representing a level in a similar way. You may have used them before, perhaps even in the Koalabr8 game in The Game Maker’s Apprentice. A tile set is used to create the visible parts of the landscape, but tiles are non-interactive (they can’t have events and actions), so invisible objects are placed on top to handle any interactive behaviors. It also means that any number of tiles can make use of an identical object behavior: providing visual variety without adding hundreds of objects to your game.

Figure 3-3. Some of the 16×16 tiles originally taken from the SNES version of the game

Exploring Tiles and Rooms

1. Open tile_sweet from the Background resources. It has Use as tile set selected so Game Maker automatically subdivides it into around 500 different 16×16 pixel tiles that you can rearrange to create the visual composition of a level. It’s worth noting that, unlike individual sprites, tiles do not have a movable origin. Their origin is effectively always X=0, Y=0, so all the corresponding sprite collision masks (in the Platforms group) also have their origins set to X=0, Y=0, to match up precisely with the tiles.

2. Open up room_test from the Room resources to see the example level we’ve created for you using this tile set. If you select the settings tab, you’ll see that the size of the entire room is 2048 x 640 pixels. Have a look around using the scrollbars. It’s not nearly as big as an original Zool level, but it’ll work fine for our testing purposes.

3. Now switch to the tiles tab (see Figure 3-4). From here, you can see our tile_sweet tile set displayed in the tab on the left hand side, and the room itself on the right. You’ll need to expand the right edge of the tab by some way in order to see all of the tiles from the tile set. You can change the currently selected tile by left clicking on the tile set, and then place copies of that tile by left clicking in the room. As with objects, you can also delete tiles again using the right mouse button. Have a go at creating an area of landscape using the tile set, but be careful to undo your changes again afterward (either by using the Undo button or by closing the room and selecting No when asked to save the changes). You can see how much time and effort went into putting just this small level together!

4. There are two separate tile layers in this room: a background layer (at a depth of 1000), which will appear behind Zool, and a foreground layer (at a depth of -1000), which will appear in front of Zool. This helps to give the game a more 3D effect by making Zool appear behind some items of scenery and in front of others. You can select between editing these different layers by clicking on the drop-down menu where it says Current tile layer. You can only add or delete tiles from the currently selected layer.

Tip To view only the tile layer you are currently editing, uncheck the Show Tiles option from the drop-down menu next to the magnifying glass on the Room Properties toolbar (see Figure 3-4). When the tiles tab is selected, this only shows the current tile layer. When the tiles tab is not selected, it hides all tiles.

5. Now, switch to the objects tab in the Room Properties with the Show Tiles option still unchecked. All the pretty graphics should disappear and you should now be able to see through to the green collision objects that define the physically solid parts of the level. All these collision objects have a Depth of 0, so they appear in-between the two tile layers (although they also have their Visible setting unchecked so they aren’t visible at all when you run the game — just in the editor).

6. It’s difficult to tell, but worth noting that we have placed additional collision objects so that the floor extends some way beyond the left and right boundaries of the room. We want the room to work as an infinitely looping level, so this helps Zool to smoothly wrap around the boundaries of the room without accidently falling down outside of the room. This also means you might occasionally get a message from Game Maker telling you that there are objects outside of the room and asking you if you want to delete them. Obviously, you need to select No when you get this message.

Figure 3-4. Disabling Show Tiles in the Room Properties allows you to view the foreground and background tile layers separately as well as reveal the collision objects

7. Finally, go to the Objects resources folder and open the Platforms group to examine the objects themselves. You’ll see that a whole range of collision objects have been created for you and assigned the appropriate sprites. The first three are “standard” objects and you should have already seen plenty of instances of these in the test room:

obj_platform (green) — This object is used to represent all the horizontal platform sections that make up the ground that Zool walks on. They are also used to create the underside of solid areas of ground that Zool can’t jump up through.

obj_wall(blue) — This object is used to represent all the vertical wall sections that make up the cliffs that Zool will climb and cling to.

obj_ledge(almost invisibly thin magenta line) — This object is used for horizontal platforms that Zool can jump up through but still walk on top of.

The next three are only used as parent objects, so you’ll never actually see instances of these objects placed on the landscape. They just exist to group similar kinds of objects together in useful ways:

obj_ramp(black) — Ramps are just platforms that have a slope up or down. You’ll find the actual ramps that get placed on the level (and so have the sloped collision masks) in the Ramps group.

obj_slope(grey) — Slopes are just steeper ramps that are made of ice, and therefore slippery. Again, you’ll find the actual slopes that are placed on the level (and have the sloped collision masks) in the Slopes group.

obj_solid(no sprite) — This is a parent object that is used for all objects that are considered to be solid parts of the landscape.

8. Notice that all the objects in the Ramps group have obj_ramp set as their parent and all the objects in the Slopes group have obj_slope set as their parent. You can think of this kind of parent relationship as meaning “a kind of.” So all the ramps are “a kind of” obj_ramp and all the slopes are “a kind of” obj_slope.

9. There are more “a kind of” relationships set up for the other types of collision objects. The obj_slope object (which represents icy slopes) has obj_ramp set as its parent because slopes are “a kind of” slippery ramp. Both obj_ramp and obj_ledge have obj_platform as their parent because they are “a kind of” platform. Finally, both obj_platform and obj_wall have obj_solid as their parent because they are “a kind of” solid object. Figure 3-5 shows the complete hierarchy of parent relationships used for the landscape objects. Take a good look at this and make sure that the parent relationships make sense to you before continuing.

10. Crucially, this makes it possible to consider collisions with objects that are “a kind of” something. For example, a collision with obj_solid now includes every kind of landscape object, and a collision with obj_platform would include everything apart from walls.

Figure 3-5. The hierarchy of “is a kind of” relationships as defined by the parent settings of each object

PARENTING

If all this use of parenting still seems confusing, then try to remember that there are two different effects of giving an object a parent in Game Maker:

The child object inherits all the events and actions of its parent (unless it has identical events, in which case the child’s events override those of its parent).

All collisions events with the parent object (for example, obj_solid) are now also triggered by collisions with the child object (for example, obj_platform).

An Alien State

You probably won’t be surprised to learn that we’re going to make use of state objects again in this game. When we made Fishpod, we had a very clear rule: every different behavior and/or appearance had its own state object to handle it. Now this may work fine with simple games like the Fishpod example, but it is not always practical when things get more complex. For a start, we already know Zool will have seven different appearances for movement alone, and we’ll need to add several more later for attack moves. If we apply the same rule about appearances, then that could mean ten or more different state objects for Zool. There would be many common behaviors between these different appearances, producing a lot of duplicated or subtly different versions of the same actions (for example, look back at the platform collision events for obj_pod_jumping and obj_pod_falling in the Fishpod game). Our events will contain a lot more actions than the ones in Fishpod and duplicated actions can be dangerous if they need to be modified: it’s easy to introduce errors by forgetting to change all of them.

Now we could use Parents as a way of reducing duplicated code and sharing common behaviors between state objects. Making one object a parent of another allows it to inherit all of its events and actions or override those events with its own when differences are required. However, complex inheritance hierarchies can quickly become difficult to follow, so too many parents and use of inheritance could be just as problematic as too many state objects.

The bad news is that there are no hard and fast rules about how much you should use such mechanisms as state machines or inheritance in programming, and you have to develop a gut instinct for it. However, you should always bear in mind that they are designed to make your life easier — not as some kind of programming philosophy.

It is never right to use parents, state objects, or any other programming mechanism just “because you can.” You should always consider whether it actually makes your life easier or harder as a result!

To this end, we have tried to group our states together into a smaller number of state objects that include common behaviors. There will be eight moving-related states in all (one for each appearance and two for climbing) grouped into four different state objects. We will simply store the current state in a variable, and manage this state variable from within the events and actions of the four state objects. Combined with a small amount of parenting to cope with behaviors common to all states, we believe this will provide an effective and manageable result:

obj_zool — The parent object for all Zool state objects. This will handle any behaviors that are common to Zool in all states.

obj_zool_land — This state object will handle all states and behaviors relating to moving on land: standing, walking, and skidding.

obj_zool_air — This state object will handle all states and behaviors relating to moving in the air: jumping and falling.

obj_zool_wall — This state object will handle the states and behaviors relating to moving on walls:clinging and climbing.

obj_zool_ice — This state object will handle the slipping state behavior relating to moving on icy slopes in all different directions.

We can also attempt to draw these state objects and the relationships between them as a guide to implementing the object behaviors (see Figure 3-6). It looks pretty frightening, doesn’t it? Don’t worry — you don’t need to memorize it, and the whole point of drawing a complicated diagram like this is to help us focus on one state at a time, without worrying about how complicated the overall structure is. It is impossible to capture the finer details of a system like this in one diagram, but it should help us plan our general approach.

Figure 3-6. A (Finite) State Machine diagram for our implementation of Zool. Red arrows indicate collisions and blue arrows indicate player interactions.

“Supported/Not Supported” refers to whether Zool has a platform directly below him or not

Note We are going to begin by defining a set of constants for use in our game. Just as with the Fishpod example, you won’t be able to create these constants unless you have a registered Pro version of Game Maker 8. If you are using the Lite version of Game Maker, then you can achieve the same effect by including Set Variable actions

(control tab) at the very start of the Create event for the parent Zool object. Each one should have the same name and value as the constant. Unlike constants, these variables will only be accessible in Zool objects, rather than globally (as constants are), but it makes no difference to the rest of this example.

An Alien’s First Steps

So with a plan in place, we can now set about implementing some functionality for our game. While it’s all still fresh in our head, we’ll begin by setting up constants for the eight different movement states we’re going to need, as well as creating empty objects for the four different state objects.

Setting up State Constants and State Objects

Make sure you have an unaltered version of zool0.gmk opened and go to Define Constants on the Resources menu.

Create the following state constants with their respective values: ZSTATE_STAND = 0, ZSTATE_WALK = 1, ZSTATE_JUMP = 2, ZSTATE_FALL = 3, ZSTATE_CLIMB = 4,ZSTATE_CLING = 5, ZSTATE_SLIP = 6, ZSTATE_SKID = 7. Note that we have prefixed all of these with the letter Z (for Zool) in case we need to define states for other characters in the game later.

While it’s open, create two more constants for facing left and right: FACE_LEFT = -1, FACE_RIGHT = 1. Note that we are using a negative value for left and a positive value for right this time. These will be particularly useful as we can make regular use of the relationship between these values and movement on the x-axis (as x-1 is one pixel to the left, and x+1 is one pixel to the right). More of that later.

Create a new object called obj_zool, give it the spr_zool_stand_right sprite, and set its depth to -1(just in front of 0).

Create four more object resources called obj_zool_land, obj_zool_air, obj_zool_wall, and obj_zool_ice. Give each object the corresponding sprite: walk for land, jump for air, climb for wall, and slip for ice. The facing direction of the sprite is not important, but if you use the wrong sprite, then you may end up with a Zool that doesn’t animate properly. Set their depth to -1 and make obj_zool their parent.

Open up the test room and place a single instance of obj_zool on the bottom left of the room so that he is standing in mid-air just above the floor.

Now let’s create some simple walking functionality for Zool. We’ll begin by setting up the parent object, which can contain any functionality that is common to all Zool state objects. All the child objects will inherit the events and actions of this object. One of the key functions we’ll make the parent object perform for all state objects will be to draw the appropriate sprite for Zool’s current state. The parent object will also be the first Zool object that gets created on each level, so we’ll use its Create event to set default values for variables as well.

The Zool Parent Object

1. Add a Create event to obj_zool and include a Set Variable action (control tab) that sets a Variable facing to FACE_LEFT. We will use this variable to keep track of which way Zool is facing just like before. Setting it to left in here makes Zool default to facing left whenever he starts a level.

2. Now include a Change Instance action (main1 tab) that changes into obj_zool_land and sets Perform Events to yes. This last option makes sure that the Destroy event of the current object (obj_zool) and the Create event of the object we’re changing into (obj_zool_land) are both called as part of this action (in that order). We’ve only mentioned the Create event in the past, as we don’t use Destroy events very often.

3. Add an Other, Outside Room event and include the Wrap Screen action (move tab) with Direction set to horizontal. We have a relatively small room, so this will help us to maximize the available space. Remember that we have added platform objects beyond the edges of the room so that Zool can wrap smoothly.

4. Add a Draw event. Adding this Draw event to the parent will not just disable drawing for this object, but all its children as well. So next we will include actions to draw the appropriate sprite for all the different states — otherwise Zool will not appear!

5. Include a Test Variable action (control tab) that tests if the Variablefacing is equal to the ValueFACE_RIGHT.

6. Immediately follow this with a Start Block action to group the following actions together under this test.

7. Include a Test Variable action that tests if the Variable state is equal to the Value ZSTATE_STAND.

8. Immediately follow this with a Draw Sprite action (draw tab) that draws spr_zool_stand_right at a Relative position of X=0, Y=0.

9. Include a Test Variable action (control tab) that tests if the Variable state is equal to the Value ZSTATE_WALK.

10. Immediately follow this with a Draw Sprite action (draw tab) that draws spr_zool_walk_right at a Relative position of X=0, Y=0.

11. Add an End Block action (control tab) to end the group of actions.

12. Now repeat steps 5-11, replacing FACE_RIGHT with FACE_LEFT and right-facing sprites with left-facing ones, appropriately. Once completed, this event should contain the actions shown in Figure 3-7.

Figure 3-7. The current state of the Draw event for the parent Zool object

Note If you run the game at the moment, it will exit immediately. Why is this? Well, if you think about it, the Create event of obj_zool changes the instance into obj_zool_land, performing the Destroy and Create events of obj_zool and obj_zool_land, respectively. Yet, obj_zool_land inherits its Create event from obj_zool, so its Create event will also change the instance into obj_zool_land. This sets an infinite loop in motion, and Game Maker aborts the program.

The Zool Land State Object

1. Reopen obj_zool_land and add a Create event. Include a Set Variable action (control tab) that sets a variable state to ZSTATE_STAND.

2. Include a Set Gravity action (move tab) that sets Direction to 270 (downward) and Gravity to 0 (off). All the land-based movement will work with gravity turned off, as it makes things simpler that way.

3. Include a Set Friction action that sets Friction to 1. This adds a force to Zool that will gradually slow him down by reducing his speed by 1 pixel every step.

4. Include a Speed Vertical action that sets Vert. Speed to 0. This makes sure that Zool stops any vertical movement if he had any in a previous state.

5. Add a Keyboard, Left event and include a Speed Horizontal action (move tab) that sets Hor. Speed to a Relative value of -2.5. This applies a force on Zool toward the left when the left key is held. The size of this movement force is one greater than the friction force, so he will gradually build up speed by 1.5 pixels per step (the difference).

6. Include a Set Variable action (control tab) that sets the Variable state to ZSTATE_WALK.

7. Include another Set Variable action that sets the Variable facing to FACE_LEFT.

8. Repeat steps 5-7 for a Keyboard, Right event containing the same three actions. These should set Hor. Speed, Relative to 2.5, the Variable state to ZSTATE_WALK and the Variable facing to FACE_RIGHT.

9. Now add a Step, Begin Step event and include a Set Variable action that sets the Variable state to ZSTATE_STAND. You might think that putting this in a Begin Step

event would mean that Zool always ends up being drawn in the standing state, but we will explain why that’s not the case in the next section.

10. Add a Collision event with obj_solid. The parent hierarchy we have set up between collision objects means that obj_solid will include collisions with every other kind of solid object (platforms, walls, ledges, ramps, and slopes).

11. Include a Set Variable action (control tab) that sets the Variable x to xprevious.

12. Include another Set Variable action that sets the Variable y to yprevious. These two actions set Zool back to the position he was in before the collision happened.

13. Include a Move to Contact action (move tab) with Direction set to direction, Maximum set to -1 (no maximum), and Against set to all objects.

14. Finally, include a Speed Horizontal action with Hor. Speed set to 0. This now provides us with the same kind of collision event as used in the Fishpod game. The obj_zool_land object should now look like Figure 3-8.

Figure 3-8. The current state of the land Zool state object

Note If you run the game now, then the previous problem has fixed itself. The obj_zool_land object now has its own Create event that overrides that of its parent and so prevents the infinite loop occurring.

Marching Order

So before moving on, it’s worth looking at the order of events in Game Maker, as it is critical to why this particular combination of events works. If you’ve ever wondered why there are three different kinds of Step events in Game Maker, then this should help to explain things. In every game step, Game Maker executes all the different events for all the different objects in the game. However, it does this by performing each kind of event for every object before moving on to the next kind of event. The order in which it does this is as follows:

Any Begin Step events for all objects

Any Alarm events for all objects

Any Keyboard, Key Press, and Key Release events for all objects (in that order)

Any Mouse Button, Mouse Press, and Mouse Release events for all objects (in order)

Any normal Step events for all objects

[At this point all instances are set to their new positions based on their current speed and direction.]

Any Collision events for all objects with all other objects

Any End Step events for all objects

And finally, any Draw events for all objects

One of the things that seems to be missing from Game Maker when you first start to create games is an event for when a specific key is not being pressed. You start something moving in a Keyboard, Left event and you add a Keyboard, event to stop it moving again. Unfortunately the event only gets called when no keys are being pressed on the keyboard at all. So once you have started an object moving left, then you can keep it moving left by holding any key down on the keyboard  — even the right arrow key! Similarly, solutions using Key Release events can also be tricky to make work in all situations.

So for Zool, we have created a solution to this kind of problem by taking advantage of the event order. We set the state to ZSTATE_STAND in the Begin Step event, and create Keyboard, Left and Keyboard, Right events that reset the state to ZSTATE_WALK. If neither key is being pressed, then the state will still be ZSTATE_STAND in any of the Step, Collision, End Step, or Draw events as they occur after the keyboard events. The left and right keys will set the state appropriately, but any other key will have no effect on the outcome.

Note Actually, in the registered, Pro version of Game Maker 8, you can now add your own Trigger events that can detect the user pressing (or not pressing) any combination of keys you like.

Room with a View

If you try running the game at the moment, then you will discover that your entire room is displayed in one very large window. Fortunately, Game Maker provides views as a means of changing how much of the room you can see at once, and even settings to make that view automatically follow a particular object around the room. We know that the entire test room is 2048 pixels wide by 640 pixels high (see Figure 3-9), but how much of it do we want to see at once? The screen resolution on the original (Amiga) version of Zool was only 320×256 pixels, which didn’t give a wide view of Zool’s surroundings. We’re going to be a bit more generous and adapt our aspect ratio to fit a widescreen monitor by giving players a 512×300 pixel view window into the room. However, modern monitor resolutions are much higher than this, so this would create a pretty small window on your desktop. Therefore, we will also scale up the output of this view by doubling it up to 1024×600 pixels — you can always hit F4 to go full-screen if you have a really high desktop resolution and you want the full effect.

Setting Up the Room View

1. Open room_test from the Rooms resources folder and select the views tab. Check the Enable the use of views option and select View 0 from the pane below it. The settings below this pane now refer to View 0.

2. Check the Visible when room starts option and set View in room to X=0, Y=0, W=512, H=300. You should see a rectangular outline appear in the room panel marking the boundary of the view we will be copying. The X and Y positions of the view are actually irrelevant, as we will make the view follow the position of the Zool object below.

3. Set Port on screen to X=0, Y=0, W=1024, H=600. So if you think of the previous step as a copy operation, then this is the paste. It will paste the view to an X,Y position of (0,0) on the player’s window, but scale up the width and height to double its original size as part of the pasting process.

Figure 3-9. The entire Zool test room, with the size of the player’s view area shown in the top left corner

Figure 3-10. The view panel settings for this view

4. Select obj_zool from the drop-down menu next to Object following and set the Hbor (horizontal border) to 256, Vbor (vertical border ) to 160, and both Hsp and Vsp(horizontal and vertical speed) to -1. Because obj_zool is the parent of all the other Zool objects, the view will know to follow any kind of Zool object. The border values determine the distance Zool needs to be from the edge of the view before the view starts to move. Setting these to half the width and height of the view means that the view will always try and keep Zool in its center. The speed values allow you to set a limit on the speed at which the view can move and a value of -1 means no limit.

5. Your View tab settings should now look like Figure 3-10.

Now when you run your game, you should have a much better view of Zool as he moves around the level (albeit in mid-air and at incredible speeds). He should also stop when he collides with walls (but pass through ramps). If you have problems, then you will find a version of the game at this point in the file zool1.gmk in the Chapter03/Games directory on the CD.

Crash Landing a Ninja

Clearly, walking on thin air is not right, so we’ll start by putting some limits on Zool’s speed and then we’ll get his feet on the ground. We know from the previous chapter that it is necessary to limit the speed of moving objects to prevent them from moving so fast that they pass through other objects (one of the problems of discrete time sampling). We’ll do this in the Step event because this takes place immediately after the keyboard events that change Zool’s speed. We’ll put this Step event in obj_zool (the parent Zool object) because then the event will be inherited by all Zool objects and that will save us repeating the same actions in each object.

Limiting Zool’s Speed

1. Define two new constants using the Define Constants option on the Resources menu (or two new variables in the Create event of obj_zool, if you are using Game Maker Lite). Set both MAX_HSPEED and MAX_FALL_SPEED to 14.

2. Add a Step, Step event to obj_zool and include a Set Variable action. Set the Variable vspeed and type min(MAX_FALL_SPEED, vspeed) into Value. Here, min is a Game Maker function that returns the minimum value of its arguments (the values separated by commas). So while vspeed is smaller than MAX_FALL_SPEED, the min function will return the value of vspeed and theaction will set vspeed to the value of vspeed (which changes nothing). Yet, when vspeed is greater than MAX_FALL_SPEED, the min function will return MAX_FALL_SPEED and the action will set vspeed to MAX_FALL_SPEED. In this way, the min function limits Zool’s falling speed to a maximum of 14 pixels per step.

3. Include another Set Variable action. This time, set the Variable hspeed and type median (-MAX_HSPEED, hspeed, MAX_HSPEED) into Value. Here, median is a different function that computes the middle value of its arguments when they are arranged in order of size. So while hspeed is less than MAX_SPEED and more than -MAX_HSPEED, the middle value will be hspeed. However, if hspeed is greater than MAX_HSPEED, then the middle value becomes MAX_HSPEED and if hspeed is smaller than -MAX_HSPEED, then the middle value becomes -MAX_HSPEED. In this way, we can keep hspeed between -14 and +14 pixels per step just using a single action.

Caution We’ll need to remember that if we create a Step event for any of the other Zool objects (which have obj_zool as a parent), then the new Step event will replace this inherited Step event. That means our speed clamping actions won’t be called for that object and Zool will potentially move too fast again.

If you play the game now, Zool is limited to a sensible running speed — also notice how he slides to a halt when you release a key when he is moving fast. This is a result of the friction gradually bringing him to a halt when you are no longer applying a force to move him. Next, we’ll start to implement behaviors for obj_zool_air and add a bit of gravity into the equation.

For our purposes, we will consider a “normal” gravity setting to be a downwards force of 2 pixels per step. Logically, obj_zool_air should always be subject to this same gravitational pull, so this shouldn’t need to change. However, many platform games of this type include a feature whereby the character can jump higher if you continue to hold down the jump key rather than releasing it immediately. So we will make gravity a little weaker (1.5 pixels per step) when the player initially presses the spacebar so that Zool can jump a little higher, but revert back to normal gravity as soon as the player releases it again. So to get the maximum height out of a jump, the player will need to hold the spacebar down for the duration of the jump.

The Zool Air State Object

1. Reopen obj_zool_air and add a Create event. Include a Set Variable action (control tab) with Variable set to state and Value set to ZSTATE_JUMP.

2. Include a Set Gravity action (move tab) with Direction set to 270 and Gravity set to 1.5. This sets an initial downwards force of 1.5 pixels per step on Zool when he is jumping.

3. Include a Set Friction action with Friction set to 0. We don’t want any friction in the air.

4. Add a Step, End Step event and include a Test Variable action (control tab) with Variable set to vspeed, Value set to 0 and Operation set to larger than.

5. Immediately follow this with a Start Block action to group together the following actions.

6. Include a Set Variable action with Variable set to state and Value set to ZSTATE_FALL.

7. Include a Set Gravity action (move tab) with Direction set to 270 and Gravity set to 2. This sets a downward force of 2 pixels per step to Zool when he is falling.

8. Immediately follow this with an End Block action to end the grouped events.

9. Add a Collision event with obj_solid. The first four actions in this event will be very similar to the obj_solid collision event for obj_zool_land. You’ll need to set Zool back to his previous position, move him to the contact position in his current direction (against all objects), and then stop his movement. Try doing this for yourself and look back at obj_zool_land (or Figure 3-8) to make sure you’ve got it right. Your last action will need to be a Move Fixed action rather than a Speed Horizontal, to stop movement in both directions.

10. After these four actions, include a Check Object action (control tab) to check for the Object, obj_platform at a Relative position of X= 0, Y= 1 (underneath Zool).

11. Immediately follow this with a Change Instance action (main1 tab) that changes into obj_zool_land and performs (Destroy and Create) events as it does so. This will now revert Zool back to the land state after a collision if he is supported by a platform.

12. Add a Key Release, Space event and include a Set Gravity action (move tab) with Direction set to 270 and Gravity set to 2.

This last KeyRelease event may seem a bit odd without the corresponding Key Press event, but the player will already have pressed the spacebar in another state object if they are jumping. This Key Release event sets the gravity back to normal when the player releases the spacebar to create the variable height jumping effect we’re after. There are now a few changes we need to make to obj_zool to accommodate the new object.

Modifying the Parent Zool Object

1. Reopen obj_zool and select the Draw event again. Within the grouped actions for FACE_RIGHT, include a new Test Variable action (control tab) to check if the Variable state is equal to the Value ZSTATE_JUMP.

2. Immediately follow this with a Draw Sprite action (draw tab) to draw spr_zool_jump_right at a Relative position of X=0, Y = 0.

3. Follow this with another Test Variable action (control tab) to check if the Variable state is equal to the Value ZSTATE_FALL.

4. Immediately follow this with a Draw Sprite action (draw tab) to draw spr_zool_fall_right at a Relative position of X =0, Y =0.

5. Now use spr_zool_jump_left and spr_zool_fall_left to create left-facing equivalents of these four new actions within the grouped actions for FACE_LEFT. The actions

of the Draw event should now look like Figure 3-11.

6. Select the Create event of obj_zool and edit the Change Instance action to Change Into obj_zool_air rather than obj_zool_land.

If you try running the game now, then Zool should fall to the ground and run across it as you would expect, but we really need to jump up to see the real effect of our new behaviors. This will require some changes to the land state object.

Figure 3-11. The new state of the Draw event for the parent Zool object

Modifying the Zool Land State Object

1. Reopen obj_zool_land and add a new Key Press, Space event. Include a Speed Vertical action (move tab) with Vert. Speed set to -22.

2. Include a Change Instance action (main1 tab) to Change into obj_zool_air and Performevents (Destroy and Create) as it does so.

3. Add a new Step, End Step event. Remember this event happens after all the movement and collision of the object has been resolved but before the object is drawn, so it is a good place to change the state of an object if its new position requires it.

4. Include a Check Object action (control tab) to check for the Object, obj_platform NOT beingat a Relative position of X= 0, Y= 1 (underneath Zool).

5. Immediately follow this with a Change Instance action (main1 tab) that changes into obj_zool_air and performs (Destroy and Create) events as it does so. This will now revert Zool back to the air state if he is no longer supported by a platform.

Run the game now and you should have your first reasonably mobile version of Zool. You can only interact with the solid, sweet-encrusted parts of the landscape (and still not ramps), but if you’re careful, you can make the jump up to the nearest platform and fall back down again. You will find a version of the game at this point in the file zool2.gmk in the Chapter03/Games directory on the CD.

Congratulations — Phase 1 Complete!

You’re now 25% of the way toward creating the finished Zool game! Already you’ve covered a lot of key concepts and done important groundwork for the remaining chapters. You’ve surveyed the alien landscape and boldly taken on the grand plan for the Zool state objects without losing your nerve. You’ve assimilated a fearsome set of parent hierarchies and helped our inter-dimensional ninja to take his first steps (and jumps) around the hostile sweet planet — closely tracked by your camera view.

Clearly there is still a long way to go, so we’d understand if you’d prefer to save your progress and resume your journey at some later date. Although, before you go, you may as well turn over to see what happens on the next page of the Zool comic first, and then you might as well quickly skim through the next chapter to see what’s coming up next. Actually, there’s an intriguing-looking picture of an athlete and another of a knight fighting a giant skeletal hand, but… they can wait. There are also several diagrams of Zool head-butting a cake, which is quite strange. Nonetheless, there’s bound to be a perfectly good reason why and there’s no need to read on at all. No, it can all wait for another time….

Note:  ZOOL and the ZOOL logo are registered trademarks of Urbanscan Ltd. All rights reserved. The Zool game resources are provided solely for use with The Game Maker’s Companion and should not be distributed without written permission from the copyright holder. (source:gamecareerguid


上一篇:

下一篇: