原文:
zh.annas-archive.org/md5/B91F6649162E9805B55AF1CE820DC361
译者:飞龙
前言
HTML5 的引入彻底改变了 Web 浏览器作为一个合法的游戏平台,具有无限的潜力。制作浏览器游戏从未如此简单,特别是使用 GameMaker Studio。
HTML5 Game Development with GameMaker 将向您展示如何使用实际示例制作和发布基于浏览器的游戏。本书利用 GameMaker 强大的脚本语言,让您能够在短时间内创建您的第一个游戏。通过本指南,您将开发出全面的技能和对开发逐渐复杂的游戏的工具的一致理解,逐渐增强您的编码能力,并将其提升到一个全新的水平。
本书指导您如何轻松有效地使用高级功能,包括数据结构,并演示如何用简单的解释和视觉示例创建刚体物理。通过本书,您将深入了解如何使用 GameMaker 开发和发布在线社交浏览器游戏。
本书内容
第一章,通过你的第一个游戏了解 Studio,将帮助你制作自己的游戏。您将有机会探索 GameMaker: Studio 界面。在本章中,我们将创建和实现所有类型的资源,同时利用各种资源编辑器。
第二章,三 A 游戏:艺术和音频,将帮助您了解艺术和音频在 GameMaker: Studio 中的工作原理。它将涵盖可接受的图像格式以及如何导入精灵表。在本章中,我们将创建一个瓷砖集,它将更好地利用计算机内存,并允许创建大型独特的世界,并了解如何控制声音以及它们被听到的方向。
第三章,射击游戏:创建横向卷轴射击游戏,将帮助您创建您的第一个横向卷轴射击游戏。在本章中,我们将应用所有三种移动方法:手动调整 X 和 Y 坐标,并设置速度和方向。我们将能够动态地向游戏世界添加和删除实例。
第四章,冒险开始,通过将键盘检查和碰撞预测放入单个脚本中,简化了玩家控制。它涵盖了处理精灵动画的几种方法,从旋转图像到设置应显示哪些精灵。我们将通过接近检测和路径查找来处理人工智能。
第五章,平台乐趣,深入探讨系统设计和创建一些非常有用的脚本。我们将构建一个动画系统,游戏中的大多数对象都会使用,并预测碰撞,并将我们自己的自定义重力应用于玩家。最后,我们将利用我们之前的知识和新系统创建一个三阶段的 Boss 战。
第六章,倾覆的塔,涵盖了使用 Box2D 物理系统的基础知识。我们将学习如何为对象分配 Fixture 以及可以修改的不同属性。我们将创建一个利用旋转关节的链条和破坏球,以便每个部分都会随着前面的部分旋转。此外,本章涵盖了绘制 GUI 事件以及精灵在房间中的位置与屏幕上的位置之间的区别。
第七章,动态前端,包括添加整个前端,包括商店和可解锁级别。我们将处理网格,地图和列表数据结构,以保存各种信息。我们将重建 HUD,以便显示更多按钮,仅显示可用设备,并构建基本倒计时器。最后,我们将添加一个保存系统,教会我们如何使用本地存储,并允许我们拥有多个玩家保存。
第八章,玩转粒子,将向您展示如何添加一些细节和修饰,使我们的游戏真正闪耀。我们将深入研究粒子的世界,并创建各种效果,为 TNT 和柱子的破坏增添影响力。游戏现在已经完成,准备发布。
第九章,将您的游戏发布出去,将帮助我们使用 FTP 客户端将游戏上传到 Web 服务器。我们将把 Facebook 整合到游戏中,允许玩家登录他们的账户,并将级别得分发布到他们的墙上。它还涵盖了使用 Flurry 进行分析,以跟踪玩家如何玩游戏。最后,我们将简要了解通过赞助赚钱的方法。
附录,拖放图标到 GameMaker 语言参考,将帮助我们了解每个图标的功能,因为每个图标通常不止一个功能。该附录提供了所有拖放图标的代码等效的彻底参考。
您需要为本书准备什么
这本书需要 GameMaker: Studio 专业版与 HTML5 导出模块,以及一个符合 HTML5 标准的浏览器(Google Chrome 效果最好)。
这本书适合谁
这本书适合任何热衷于使用 GameMaker: Studio 创建有趣和充满动作的网页游戏的人。这本直观的实用指南既适合初学者,也适合想要使用强大的 GameMaker 工具创建和发布在线游戏与世界分享的高级用户。
约定
在本书中,您会发现一些文本样式,用于区分不同类型的信息。以下是一些这些样式的示例,以及它们的含义解释。
文本中的代码单词,数据库表名,文件夹名,文件名,文件扩展名,路径名,虚拟 URL,用户输入和 Twitter 句柄显示如下:“创建一个新的声音并命名为snd_Collect
”。
代码块设置如下:
mySpeed = 4;
myDirection = 0;
isAttacking = false;
isWalking = false;
health = 100;
image_speed = 0.5;
当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:
isWalking = false;
if (keyboard_check(vk_right) && place_free(x + mySpeed, y))
{
x += mySpeed;
myDirection = 0;
sprite_index = spr_Player_WalkRight;
isWalking = true;
新术语和重要单词以粗体显示。例如,屏幕上看到的单词,菜单或对话框中的单词会出现在文本中,如:“点击下一个按钮会将您移至下一个屏幕”。
注意
警告或重要说明会出现在这样的框中。
提示
提示和技巧会出现在这样。
第一章:通过您的第一个游戏了解 Studio
欢迎来到使用 GameMaker 进行 HTML5 游戏开发!您即将进入令人兴奋的网络游戏开发世界。如果您以前从未使用过GameMaker:Studio,本书将向您展示有关使用该软件、制作游戏以及将其上载到互联网的一切。如果您以前有 GameMaker:Studio 的经验,但这是您首次尝试 HTML5,本书将帮助您更好地了解开发独立游戏和基于浏览器的游戏之间的区别。随意浏览本章并转到项目。
现在,如果您仍在阅读本文,我们可以假设您想了解更多关于这个软件的信息。您可能会问自己,“为什么我应该使用 GameMaker:Studio?HTML5 模块给我什么功能?说到底,HTML5 是什么,我为什么要关心?”所有这些都是很好的问题,让我们试着回答它们。
使 HTML 游戏开发变得简单
GameMaker:Studio 是一个非常强大且易于使用的开发工具,用于制作游戏。该软件最初是设计用于课堂环境,作为学生学习基本编程概念、了解游戏架构和创建功能齐全的游戏的方式。因此,由于拖放式编码系统,开发环境对于初次使用者来说非常直观。与许多其他具有类似功能的竞争开发工具不同,GameMaker:Studio 具有非常强大的脚本语言,允许用户创建几乎可以想象的任何东西。再加上您可以轻松导入和管理图形和音频资源,集成了出色的 Box2D 物理库以及内置的源代码控制,为什么不使用它呢?直到现在,制作游戏通常意味着您正在创建一个独立的产品。
互联网并不是真正的考虑,因为它相当静态,并且需要一堆专有插件来显示动态内容,例如游戏、电影和音频。然后,HTML5 出现并改变了一切。HTML5 是一组开放标准的代码语言,允许任何人开发交互式体验,并能够在具有现代浏览器和互联网连接的任何设备上本地运行。开发人员现在能够使用尖端功能,例如 WebGL(一种允许进行 3D 渲染的图形库)、音频 API 和资产管理,来推动在浏览器中所能做的事情的边界。
注意
并非所有浏览器都是相同的!虽然 HTML5 标准由 W3C 制定,但每个供应商的实现方式都不同。此外,目前还没有制定所有标准,这意味着某些功能可能在某些浏览器中无法正常工作。例如,有多个音频 API 竞相成为标准。随着标准的确定和浏览器的更加兼容,这些问题应该会消失。要查看您喜欢的浏览器对 HTML5 的支持程度,可以访问html5test.com
。
通常,为 HTML5 开发游戏需要对三种不同的编码语言有所了解:HTML5(超文本标记语言),用于创建网页结构的代码语言,CSS3(层叠样式表 3),用于确定网站的呈现方式,以及实际实现魔术的JavaScript。GameMaker: Studio HTML5 导出模块通过允许开发人员在集成环境中工作并通过按下按钮导出到这些语言,使所有这些变得简单。除了作为游戏引擎之外,HTML 导出模块还包括用于处理 URL 和浏览器信息的特定功能。它还配备了自己的本地服务器软件,可以让您测试游戏,就好像它实时上网一样。最后,您可以进一步扩展 GameMaker: Studio,因为它允许您导入外部 JavaScript 库,以获取您可能需要或想要的任何功能。听起来很棒,不是吗?现在让我们启动 Studio。
设置软件
为了使用本书,我们需要一些软件。首先,我们需要一个 HTML5 兼容的浏览器,如 Mozilla Firefox,Microsoft Internet Explorer 9.0,或者为了获得最佳效果,Google Chrome。其次,我们需要购买并安装 GameMaker: Studio 专业版和 HTML5 导出模块。一旦我们拥有了所有这些,我们就可以开始制作游戏了!
注意
请注意,GameMaker: Studio 专业版和 HTML5 导出模块是两个单独的项目,您需要拥有两者才能为网络创建游戏。
-
从
www.yoyogames.com/buy/studio/professional
购买并下载 GameMaker: Studio 专业版和 HTML5 导出模块。 -
下载完成后,运行程序
GMStudio-Installer.exe
。 -
按照屏幕上的说明操作,然后启动程序。
-
输入您的许可密钥。这将解锁已购买的软件和模块。
GameMaker: Studio 已经准备就绪,让我们开始一个项目吧!
-
在新项目窗口中,选择新选项卡。它应该看起来像前面的屏幕截图。
-
GameMaker: Studio 通过为每个资源创建文件夹以及项目文件来管理项目。为此,您需要指定游戏文件存储的目录。将项目名称字段设置为
Chapter_01
,然后单击创建。
我们第一次看到 Studio
现在我们已经安装并运行了软件,让我们来看看界面。GameMaker: Studio 的基本布局可以分为四个组件:菜单、工具栏、资源树和工作区。我们将在本书中探索这些组件,所以不要期望对每个项目进行详细分解。这不仅会让阅读变得枯燥无味,还会延迟我们制作游戏。相反,让我们专注于我们现在需要知道的东西。
首先,与大多数复杂软件一样,每个组件都有自己的方式让用户执行最常见的任务。例如,如果要创建一个精灵,可以导航到菜单 | 资源 | 创建精灵,或者单击工具栏中的创建精灵按钮,或者在资源树中右键单击精灵组,或者使用Shift + Ctrl + S在工作区中打开精灵编辑器窗口。实际上,还有更多的方法可以做到这一点,但您明白了。
虽然有很多重叠的功能,但也有许多事情只能在每个特定的组件中完成。以下是我们需要知道的内容。
菜单
菜单是您将找到每个编辑器和工具所需的地方。有一些非常有用的工具,比如在脚本中搜索和定义常量,这些只能在这里找到。为什么不花点时间看看每个菜单选项,以便了解您可以使用的所有内容呢。我们会等一下。
工具栏
工具栏使用简单的图形图标来表示我们将要使用的最常见的编辑器和工具。这些按钮是创建新资产和运行游戏的最简单、最快速的方式,所以预计会经常使用这些按钮。工具栏上有一个非常重要的独特元素:目标下拉菜单。目标确定我们将编译和导出到哪种格式。将其设置为HTML5。
注意
目标菜单的默认设置是Windows,所以确保将其更改为HTML5。
资源树
资源树显示和组织了为游戏创建的所有资产。保持项目有条不紊不会影响软件的性能,但会节省我们的时间,并在长期内减少挫折感。
工作区
工作区是各种编辑器将打开的地方。运行游戏时,编译器信息框将出现在底部,并在运行游戏时显示正在编译的所有内容。还有一个源控制选项卡,如果您有一个 SVN 客户端和用于团队合作的存储库,可以使用它。
注意
如果您想了解更多关于源控制的信息,请查看以下 GameMaker: Studio 维基页面:wiki.yoyogames.com/index.php/Source_Control_and_GameMaker:Studio
探索资源编辑器
为了在 GameMaker: Studio 中创建游戏,您需要至少三种类型的资源资产:精灵(您所看到的)、对象(它的功能)和房间(发生的地方)。除此之外,您还可以拥有脚本、背景、声音、字体、路径和时间轴。
您可以将每个可以引入 GameMaker: Studio 的资源都有自己的属性编辑器。为了熟悉它们中的每一个,我们将构建一个非常简单的猫鼠游戏。我们将创建一个玩家角色(一只老鼠),可以在房间中移动,收集物品(奶酪),并避开敌人(一只猫)。让我们立即开始创建一些精灵。
使用精灵属性编辑器加载您的艺术资产
精灵是用于对象的图形表示的位图图像。这些可以是单个图像或一系列动画图像。GameMaker 有自己的图像编辑器来创建这些,但也允许导入 JPG、GIF、PNG 和 BMP 文件。
在我们的示例中,我们将首先创建两个精灵;一个用于墙,一个用于玩家角色。如果您已经下载了支持文件,我们在Chapter_01
文件夹中提供了这些图像文件。
墙精灵
我们将从一个简单的精灵开始,它将代表我们游戏的墙。
-
通过导航到资源 | 创建精灵来创建一个新精灵。这将在资源树中创建一个精灵,并打开精灵属性编辑器。
-
将精灵命名为
spr_Wall
。 -
单击加载精灵以打开精灵图像。在窗口的一侧有一个图像信息部分,我们可以在那里预览所选图像并选择激活几个选项。使不透明将从所选精灵中删除所有透明度。删除背景将删除图像左下角像素中找到的颜色的所有像素。平滑边缘将平滑图像的透明边缘,在导入动画 GIF 文件时非常有用,可以去除硬边缘。
-
在没有选中任何选项的情况下,打开
Chapter 1/Sprites/Wall.png
,然后单击确定。 -
如下截图所示,它的宽度和高度为 32 像素,有一个子图像。没有其他需要改变的地方,所以点击确定:
玩家精灵
这个游戏中的玩家将是一个鼠标,精灵由两帧动画组成。
-
创建一个新的精灵。
-
将精灵命名为
spr_Player
。 -
点击加载精灵,选择
Chapter 1/Sprites/Player.gif
。勾选去除背景和平滑边缘。点击确定。 -
再次,它的宽度和高度为 32 像素,但是有两个子图像,如下一截图所示。这意味着它有动画!让我们点击显示旁边的箭头来看看每一帧的样子。加载动画图像时这样做是有用的,以确保所有帧都按适当的顺序排列并且对齐正确。
-
在原点中将X设置为
16
,Y设置为16
,或者你可以直接点击中心按钮。 -
点击确定按钮。
恭喜!你已经创建了你的第一个精灵。在下一章中,我们将更深入地探讨艺术资源的创建,所以让我们继续到对象。
使用对象属性编辑器创建游戏对象
这就是 GameMaker: Studio 真正展示其实力的地方。对象可以被看作是容器,其中包含了我们希望游戏中的每个项目执行的属性、事件和功能。当我们将一个对象放入游戏世界时,它被称为实例,它将独立于该对象的所有其他实例运行。
在我们继续之前,理解对象和对象的实例之间的区别是很重要的。对象是描述某物的一组规则,而实例是该某物的独特表示。一个现实世界的例子是你是人对象的一个实例。人是有手臂、腿、说话、睡觉等特征的东西。你是这些元素的独特解释。这个概念的一个例子可以在前面的图表中看到。
这很重要的原因是,根据所使用的功能,效果将被应用于该类型的所有项目或个别项目。一般来说,你不会希望射击一个敌人然后世界上所有的敌人都死掉,对吧?
继续我们的例子,我们将创建一个墙对象和一个玩家对象。墙将是一个固定的障碍物,而玩家将有控制,使其能够在世界中移动并与墙碰撞。
墙对象
我们将从实体墙对象开始,我们可以用它来创建迷宫供玩家使用。
-
通过导航到资源 | 创建对象来创建一个新对象。这将在资源树中创建一个新对象,并打开对象属性编辑器。
-
将此对象命名为
obj_Wall
。 -
点击精灵中的输入框,选择
spr_Wall
。
GameMaker 处理与实体对象的碰撞与非实体对象的碰撞方式不同。如果实体对象和非实体对象发生碰撞,GameMaker 会尝试通过将非实体对象移回其先前的位置来防止它们重叠。当然,为了正确地做到这一点,实体对象必须是静止的。因此,我们应该将实体属性添加到墙上。
- 点击实体复选框,然后点击确定。
注意
实体属性应该只用于不移动的对象。
玩家对象
玩家对象将向我们介绍使用事件和动作来进行移动和碰撞等操作。
-
创建一个新对象并命名为
obj_Player
。 -
选择
spr_Player
作为精灵。
GameMaker 的强大之处在于其事件驱动系统。事件是游戏运行过程中发生的时刻和动作。当您向对象添加事件时,您要求该项在发生该动作时做出响应,然后应用指定的指令。
听起来相当简单,不是吗?但是当涉及到事件顺序时可能会有点混乱。GameMaker 将游戏分解为步骤(有限的时间段),每秒运行多次事件。一些事件按照预设顺序发生,比如开始步骤,它总是从步骤的最开始开始。其他事件在被调用时发生,比如创建,它会立即在对象的实例创建时运行,以检查该代码是在步骤的开始还是结束时发生。
注意
访问wiki.yoyogames.com/index.php/Order_of_events
了解更多关于 GameMaker: Studio 事件顺序的信息。
- 在事件:区域,单击添加事件,然后导航到键盘 | 左。这个事件将在按住左箭头键的每一步中运行代码。
事件需要动作来应用它们才能发挥作用。GameMaker: Studio 使用拖放(DnD)系统,其中代表常见行为的图标可以很容易地实现。这些行为根据功能分为七个不同的选项卡。在本书的绝大部分内容中,我们将只使用在常见选项卡中找到的执行脚本图标,因为我们将编写放置在脚本中的代码。然而,在本章中,我们将使用 DnD 动作,以便您了解它们的作用。
- 从移动选项卡中,选择并将移动固定图标拖放到左键事件的动作区域。
移动固定图标
-
在移动固定选项框中,有一个选项,用于指定要应用此动作的对象。我们希望将其设置为自身,以便将其应用于玩家的实例。
-
单击左箭头以指示我们希望移动的方向。
-
将速度字段设置为
8
。这将每步应用 8 像素的速度。 -
确保相对未被选中。相对会将该值添加到当前值。
-
单击确定。
-
对于其他键盘箭头(右,上,下),重复步骤 4 到 9,使用相同的速度和适当的方向。
现在我们有一个对象,当按下箭头键时会在世界中移动。但是,如果我们运行这个程序,一旦开始移动,我们将无法停止。这是因为我们正在给对象应用速度。为了停止对象,我们需要给它一个速度为零。
-
在事件:区域,单击添加事件,然后导航到键盘 | 无键。这是一个特殊的键盘事件,只有在没有按键时才会发生。
-
选择并将移动固定图标拖放到动作区域。
-
将方向设置为中心,将速度字段设置为
0
。
我们需要做的最后一件事是添加碰撞检测。在 GameMaker: Studio 中,碰撞是由两个实例组成的单个事件。每个实例都能在这个单一碰撞上执行一个事件调用,尽管通常将代码放在其中一个上更有效。在我们的情况下,将碰撞事件放在玩家身上,当它与墙碰撞时,这是有意义的,因为玩家将是执行动作的实例。墙将保持原样,什么也不做。
-
单击添加事件,然后导航到碰撞 | obj_Wall。
-
将移动固定图标拖放到**动作:**区域。
-
将方向设置为中心,速度字段设置为
0
。单击确定。
演员已经准备好了;我们有一些可以看到并且可以做一些事情的对象。现在我们需要做的就是把它们放到一个房间里。
使用房间属性编辑器创建世界
房间代表我们对象实例所在的世界。您创建的大多数房间可能会被用作各种级别,但房间也可以用于:
-
前端菜单屏幕
-
非交互式场景
-
您需要的任何自包含环境!使用房间属性编辑器创建世界
我们想要布置一个包含玩家并呈现一些障碍物的世界。为此,我们将在房间的外缘放置墙对象,并在中心放置几条线。
-
通过导航到资源 | 创建房间来创建一个新房间。这将在资源树中创建一个新房间,并打开房间属性编辑器。
-
为了使放置变得更容易,将Snap X和Snap Y字段设置为
32
。这将创建一个每 32 像素一个捕捉点的放置网格。 -
选择设置选项卡。在这里,我们可以更改基本的房间属性,大小,每秒步数和房间的名称。
-
将房间命名为
rm_GameArea
。 -
我们将保留房间宽度,高度和速度字段的默认值,如下面的截图所示:
-
选择对象选项卡,在用鼠标左键添加的对象下,选择
obj_Wall
。 -
在房间的左上角,用鼠标左键单击放置一个墙的实例。
现在你可能会认为这将需要非常长的时间来逐个点击构建房间。别担心,有一个更简单的方法。如果你按住Shift + Ctrl,你就可以用实例来绘制世界。如果你犯了一个错误并想要删除一个实例,只需右键单击以删除一个实例,或者按住Shift键来取消绘制实例。如果你只想移动实例一点点,而不是整个网格单元,按住Alt键。
- 按住Shift + Ctrl键和鼠标左键,绘制周边墙壁。还要放下两个凸出的部分,如下面的示例截图所示:
不要忘记添加玩家!
-
在对象选项卡中,选择
obj_Player
。 -
在房间的右下角放置一个
obj_Player
的单个实例。 -
通过单击房间属性编辑器左上角的复选标记来关闭房间。
-
在这一点上,我们已经拥有了在 GameMaker: Studio 中运行游戏所需的所有必要元素。在我们测试游戏之前,我们应该通过导航到文件 | 保存来保存我们的工作。
运行游戏
在创建游戏时,有三种不同类型的编译可以进行。如果游戏已经完成了 100%,您可以选择创建应用程序以用于目标平台。如果游戏仍在开发中,有正常运行,它将编译并运行游戏,就像它是一个应用程序一样,还有调试模式运行,它运行调试工具。
让我们不再等待。通过导航到运行 | 运行游戏,或者按下F5来运行游戏。
如果一切正常,玩家对象应该能够使用箭头键在世界中移动,但不能通过任何墙对象。然而,有一些地方不太对。玩家对象似乎在闪烁,因为它是动画的。让我们在查看脚本属性编辑器时修复这个问题。
使用脚本属性编辑器引入代码
GameMaker: Studio 利用自己的专有脚本语言称为GameMaker Language,又称为GML。这种语言被开发成非常适合初学者使用,并利用了一些在其他脚本语言中可能找不到的功能。例如,GML 将接受标准表达式&&
来组合两个比较,或者替代地使用单词and
。GameMaker: Studio 通过提供一组出色的函数、变量和常量,在创建游戏时做了大量的工作。
如前所述,我们希望停止玩家对象的动画。使用脚本非常容易实现这一点。
-
通过导航到资源 | 创建脚本来创建一个新脚本。这将在资源树中创建一个新脚本,并打开脚本属性编辑器。
-
将其命名为
scr_Player_Create
。在本书中,我们将大部分脚本命名为事件名称的结尾。在这种情况下,我们将把这段代码放入一个创建
事件中。 -
要停止精灵动画,我们只需要将精灵的播放速度设置为零。在第1行,输入以下内容:
image_speed = 0;
- 通过点击编辑器左上角的复选标记来关闭脚本。
为了使脚本运行,我们需要将其附加到一个对象上。
-
重新打开
obj_Player
的对象属性编辑器。 -
添加一个创建事件。
-
导航到操作 | 控制,并选择并拖动执行脚本图标到**操作:**区域。
执行脚本图标
- 选择
scr_Player_Create
作为要执行的脚本,然后点击确定。
现在我们可以运行游戏,我们会发现玩家对象不再动画。
使用背景属性编辑器填充场景
背景是一种特殊的艺术资源,有两种不同的类型:背景图片和瓷砖集。与精灵不同,背景从不作为艺术资源的一部分进行任何动画。背景图片主要用作房间的大背景,并且在需要背景移动时非常有用。瓷砖集是可以用来绘制背景的小艺术片段,非常适合创建大型、独特的世界,并且可以保持图形成本的计算低。
注意
如果需要,可以使用背景图片:
-
背景中的一个大图像
-
背景移动
注意
如果需要,可以使用瓷砖集:
-
只需少量的艺术资源就可以创建大型世界
-
为背景添加独特的细节
对于这个简单的例子,我们将只创建一个静态背景。我们将在下一章更深入地了解瓷砖集:
-
通过导航到资源 | 创建背景来创建一个新背景。这将在资源树中创建一个新背景,并打开背景属性编辑器。
-
将其命名为
bg_Ground
。 -
点击加载背景,打开
Chapter 1/Backgrounds/Ground.png
。 -
然后点击确定。
现在我们已经准备好艺术资源,只需要将其放置到房间中。
-
重新打开
rm_GameArea
。 -
点击
背景
选项卡。
每个房间最多可以同时显示八个背景。这些背景也可以用作前景元素。如果没有激活背景,它将显示为纯色。
-
选择背景 0,然后勾选游戏开始时可见的复选框。这必须激活才能在游戏过程中看到背景。
-
选择
bg_Ground
作为要显示的背景。 -
其他所有内容都可以保持默认。水平平铺和垂直平铺应该被选中,所有其他值应该设置为
0
。 -
通过点击编辑器左上角的复选标记来关闭房间。
让我们再次运行游戏,现在我们可以看到我们有了一个背景。事情看起来确实更好了,但是缺少了一些东西。让我们给游戏加点声音。
用声音属性编辑器带来噪音
声音属性编辑器是您可以引入用于游戏的声音的地方。GameMaker 只允许引入 MP3 和 WAV 文件。您可以使用两种类型的声音:
-
正常声音
-
背景音乐
正常声音都是你听到的小声音效,比如枪声和脚步声。这些通常应该是 WAV 文件。背景音乐是指较长的声音,比如游戏音乐,还有一些像口语对话之类的东西。这些应该是 MP3 格式。
当 GameMaker: Studio 为 HTML5 导出游戏音频时,所有声音都将转换为 MP3 和 OGG 格式。这是因为不同的浏览器在实现 HTML5 音频标签时使用不同的音频文件格式。幸运的是,GameMaker: Studio 会自动将浏览器识别代码添加到游戏中,所以游戏知道正在使用哪些文件。
我们将为游戏创建两种声音,一些背景音乐和一个可收集物品的音效。
一点背景音乐
让我们为我们的游戏引入一些音乐,以帮助营造一些氛围。
-
通过导航到资源 | 创建声音来创建一个新声音。这将在资源树中创建一个新声音,并打开声音属性编辑器。
-
将其命名为
snd_bgMusic
。 -
加载
Chapter 1/Sounds/bgMusic.mp3
文件。如果你想听音乐,只需点击播放按钮。听完后,点击停止按钮。 -
在种类下选择背景音乐作为类型,然后点击确定。
我们希望音乐在游戏开始时立即开始。为此,我们将创建一个名为霸主的数据对象。数据对象通常不会在游戏中显示,所以我们不需要为它分配一个精灵。
用霸主控制游戏
我们将使用一个霸主对象来监视游戏并控制一些东西,比如音乐和胜利/失败条件。
-
创建一个新对象,命名为
obj_Overlord
。 -
添加一个事件,然后导航到其他 | 游戏开始。这是一个特殊的函数,只有在游戏开始时才会运行。
-
导航到操作 | 主 1,并选择并拖动播放声音图标到**操作:**区域。
播放声音图标
- 将声音:字段设置为
snd_bgMusic
,将循环:设置为true
,然后点击确定。
在我们测试之前,我们需要确保霸主在世界中。当你把它放在一个房间里时,它将被一个小蓝色圆圈图标代表,如下面的截图所示:
-
重新打开
rm_GameArea
。 -
从对象选项卡中选择
obj_Overlord
,并将一个实例放在房间里。
让我们运行游戏并听一下。音乐应该立即开始播放并无限循环。让我们继续创建一个可收集的物品。
可收集的物品
我们将创建一个玩家在游戏中可以收集的物品。当玩家与其碰撞时,声音将被播放一次。
-
创建一个新声音,命名为
snd_Collect
。 -
加载
Chapter 1/Sounds/Collect.wav
文件,并将其设置为正常声音,然后点击确定。
我们还没有为此创建一个对象,也没有引入一个精灵。现在是你测试记忆的机会。我们只会快速地复习一下我们需要的东西。
-
创建一个新精灵,命名为
spr_Collect
。 -
选择删除背景和平滑边缘,加载文件
Chapter 1/Sprites/Collect.png
并将其中心设置为原点。 -
创建一个新对象,命名为
obj_Collect
。 -
将
spr_Collect
分配为其精灵。 -
添加一个与
obj_Player
的碰撞事件。 -
导航到操作 | Main1,并将播放声音图标拖放到**操作:**区域。
-
将
声音:
字段设置为snd_Collect
,并将**循环:**设置为false
。
现在,当玩家与对象发生碰撞时,它将播放一次声音。这是一个良好的开始,但为什么我们不给玩家更多的奖励呢?
- 导航到操作 | 分数,并将设置分数图标拖放到**操作:**区域。
设置分数图标
- 如下截图所示,将新的分数:字段设置为
50
,勾选相对框,然后点击确定。这将在每次收集对象时为我们的分数增加 50 分。相对使得分数增加到先前的分数。
现在我们有值得收集的东西。只剩下一个问题,那就是我们只是碰到对象就得到了分数和声音。我们不能让这种情况永远持续下去!
- 导航到操作 | Main1,并将销毁实例图标拖放到操作:区域。此操作将从世界中移除实例。保持值不变,然后点击确定。
销毁实例图标
- 我们已经完成了这个对象,如果构建正确,它应该看起来像下面的截图。点击确定。
让我们在房间里放置一些这些可收集物,并运行游戏。我们应该能够在世界中移动玩家并与可收集物发生碰撞。我们应该听到声音播放并且对象消失。但是,我们的分数在哪里呢?嗯,在显示它之前,我们需要引入一些文本。
编写文本和字体属性编辑器
您可以导入字体以在游戏中使用它们作为文本。这些字体需要安装在您的机器上,以便在开发过程中使用。每个字体资源都设置为特定的字体类型、大小,以及是否为粗体/斜体。如果您想要稍微变化,比如一个字体大两个点,那么必须创建一个单独的字体资源。这是因为在导出时,GameMaker 将把字体转换为图像,这样就可以在用户的机器上使用而不需要预先安装字体。
我们将创建一个用于显示游戏分数的字体。
-
通过导航到资源 | 创建字体,创建一个新的字体。这将在资源树中创建一个新的字体,并打开字体属性编辑器。
-
将其命名为
fnt_Impact
。 -
从字体下拉菜单中选择Impact。这是一个默认的 Windows 字体。
-
将大小设置为
16
。然后点击确定。
现在我们有一个可以在游戏中使用的字体。为此,我们将让 Overlord 对象在屏幕顶部绘制游戏分数。我们还将使文本为白色,并将其居中对齐。
-
重新打开
obj_Overlord
。 -
通过导航到绘制 | 绘制 GUI,添加一个绘制 GUI 事件。
注意
绘制事件发生在每个步骤的最后,在所有计算完成并需要在屏幕上显示之后。绘制 GUI 事件用于显示游戏中的悬浮显示,并始终呈现在所有其他游戏图形的顶部。
- 导航到操作 | 绘制,并将设置颜色图标拖放到**操作:**区域。这将打开一个对话框,您可以在其中设置颜色。
设置颜色图标
-
我们想要将颜色设置为青色。在弹出的颜色调色板中,选择左起第五列底部的青色。点击确定。
-
导航到动作 | 绘制,并将设置字体图标拖放到**动作:**区域。这将打开一个带有两个参数的对话框:要使用的字体以及它应该如何对齐。
设置字体图标
-
将字体:字段设置为
fnt_Impact
并将其对齐到中心
。点击确定。 -
最后,导航到动作 | 得分,并将绘制得分图标拖放到**动作:**区域。这将打开一个带有三个参数的对话框:x 和 y 坐标,以及一个可选的标题,可以放在实际得分前面。
-
将**x:**字段设置为
320
,y:字段可以保持为0
,并从标题:字段中删除得分:
,使其为空,如下面的屏幕截图所示。点击确定。
现在我们可以运行游戏,得分现在将显示在屏幕顶部的中心位置。现在,当您与可收集物品碰撞时,您应该看到得分增加。
使用路径属性编辑器创建复杂移动
路径是为对象创建复杂移动模式的最佳方式。路径由一系列点组成,对象可以沿着这些点移动。点之间的过渡可以是直线的,这意味着对象将精确地到达每个点,也可以是曲线的,是三个点之间的插值。路径可以是开放线或闭合循环。以下屏幕截图将在本节中用作参考图像。
我们将创建一个简单的敌人,它将沿着房间周围的路径移动。如果玩家与敌人碰撞,玩家将被销毁。让我们从创建路径开始。
-
通过导航到资源 | 创建路径来创建一个新路径。这将在资源树中创建一个新路径,并打开路径属性编辑器。
-
将其命名为
pth_Enemy
。 -
在编辑器工具栏的末尾,我们可以设置显示哪个房间。这对于按房间基础创建准确路径非常有用。将其设置为
rm_GameArea
。
要为路径添加一个点,您可以在地图的任何位置单击左键。第一个点将由绿色方块表示,其后的所有点将是圆圈。
-
将第一个点放在地图的
64
,64
处。如果出现错误,您可以随时将点拖动到正确的位置,或者您可以手动设置 X 和 Y 值。 -
在这条路径上,我们将添加另外五个点,如参考图像所示。
-
我们将保留所有其他设置为默认值,所以点击确定。
路径已准备就绪,现在我们只需要创建一个敌人并将路径附加到它。这个敌人将简单地沿着路径移动,如果它与玩家碰撞,它将重新开始游戏。
-
创建一个新精灵并命名为
spr_Enemy
。 -
选择删除背景和平滑边缘,加载
Chapter 1/Sprites/Enemy.png
并将原点居中。 -
创建一个新对象并命名为
obj_Enemy
。 -
添加一个创建事件,导航到动作 | 移动,并将设置路径图标拖放到**动作:**区域。这将打开设置路径选项对话框。
设置路径图标
-
将**路径:**设置为
pth_Enemy
。 -
将**速度:**字段设置为
4
。 -
下一个选项确定实例到达路径末端时应该发生什么。有选项可以停止,从头继续(对于开放路径),从这里继续(对于闭合路径),和反向方向。将在末端:设置为从这里继续。
-
这里的相对:选项确定路径是从实例开始(相对)还是实例从路径的第一个点开始(绝对)。由于我们建立它来适应房间,所以将相对:设置为绝对。然后点击确定。
我们现在有一个准备好跟随路径的敌人,但它对玩家并不构成威胁。让我们在敌人上添加一个碰撞事件,并使其在接触时重新开始游戏。
- 添加一个与
obj_Player
的碰撞事件,导航到动作 | 主要 2,并将重新开始游戏图标拖放到**动作:**区域。
重新开始游戏图标
-
敌人现在已经完成,所以点击确定关闭它。
-
在房间中的任何位置放置一个敌人的单个实例。确切的位置并不重要,因为游戏运行时它会重新定位到正确的位置。
-
保存游戏并运行。我们应该看到敌人沿着房间周围的路径移动。如果玩家对象与其发生碰撞,游戏将重新开始。
现在游戏中有一些风险,但奖励还不够。让我们来解决这个问题,好吗?
使用时间轴属性编辑器生成可收集物品
时间轴是一个高级的时间跟踪系统,允许对游戏过程中发生的事情进行有限控制。时间轴由一系列时刻组成。每个时刻代表时间轴开始后的步数。
时间轴几乎可以用于任何事情,其中最常见的用途之一是生成实例。在这个游戏中,我们将使用它来生成我们的可收集物品,以便玩家有东西可以追逐。
-
通过导航到资源 | 创建时间轴来创建一个新的时间轴。这将在资源树中创建一个新的时间轴,并打开时间轴属性编辑器。
-
将其命名为
tm_Spawn_Collectibles
。 -
点击添加按钮,并将步数设置为
60
。 -
我们将通过给它们施加速度来使这些可收集物品移动。导航到动作 | 主要 1,并将创建移动图标拖放到**动作:**区域。
创建移动图标
-
将对象设置为
obj_Collect
。 -
我们希望生成发生在屏幕外,这样玩家就看不到它突然出现。我们将使这个可收集物品水平移动,所以我们将从游戏区域的左侧开始。将**x:**字段设置为
-64
。 -
我们不希望可收集物品总是在完全相同的位置生成,所以我们要为其添加一个随机元素。我们将在屏幕顶部和底部之间的随机垂直位置创建实例。将**y:**字段设置为
random(394) + 48
。 -
给它一个速度为
4
,并将方向:字段设置为0
。它应该看起来像下面的截图。点击确定。 -
在
120
处添加另一个时刻,并重复上述步骤,不过这次是垂直的。为此,**x:**字段应设置为random(546) + 48
,**y:**字段应为-64
,**速度:**字段应为4
,**方向:**字段应为270
。
我们现在有一个时间轴,每两秒钟就会生成一个新的可移动的可收集物品。但是,我们需要将其附加到一个对象上,所以让我们将其应用到obj_Overlord
上。
-
重新打开
obj_Overlord
。 -
在已经存在的游戏开始事件中,通过导航到动作 | 主要 2,将设置时间轴图标拖放到**动作:**区域。
设置时间轴图标
-
将**时间轴:**字段设置为
tm_Spawn_Collectibles
。 -
将**位置:**保留为
0
;这将从开始位置开始。 -
将开始:设置为立即开始。
-
我们希望它无限重复,所以将**循环:**设置为
Loop
。
就是这样!运行游戏,您应该看到可收集物品在两秒后开始生成,并将继续无限地生成。正如您从下一张截图中看到的,我们的游戏已经完成,但还有一个组件我们需要看一看。
调试游戏的工具
无论您在脚本编写和制作游戏方面有多么经验,错误总是会发生。有时可能是拼写错误或缺少变量,在这种情况下,GameMaker: Studio 会捕捉到并显示代码错误对话框。其他时候,游戏可能不会按照您的期望进行,比如在不应该通过墙壁时却通过了。在这种情况下,代码在技术上没有问题,只是构造不当。追踪这些错误可能非常乏味,如果没有调试工具,可能会是不可能的。为了使用这些工具,游戏必须在调试模式下运行,您可以通过单击工具栏中的运行调试模式按钮或转到菜单并导航到运行 | 运行调试模式来访问。
在调试模式下,我们可以利用调试消息来帮助我们理解游戏中发生的情况。这些消息只能通过脚本编写时使用show_debug_message()
函数实现(没有拖放选项),并且每当执行该函数时,它们将出现在控制台窗口中。您可以使用这个来传递一个字符串或显示一个变量,以便您可以将结果与您期望的结果进行比较。这是您在尝试解决问题时的第一道防线。
使用 HTML5 调试控制台
我们应该使用的第一个控制台是 GameMaker: Studio 的 HTML5 调试控制台。当游戏以 HTML5 为目标并在调试模式下运行时,将会创建一个弹出窗口,其中包含调试输出,所有调试消息都将显示在其中,以及实例列表和它们的基本数据信息。让我们测试一下这个控制台!
- 我们将从在玩家创建时添加传统的
Hello World
调试消息开始。重新打开scr_Player_Create
并在脚本的末尾添加以下代码:
myText = "Hello World";
show_debug_message(myText);
提示
下载示例代码
您可以从您在www.packtpub.com
购买的所有 Packt 图书的帐户中下载示例代码文件。如果您在其他地方购买了本书,您可以访问www.packtpub.com/support
并注册以直接通过电子邮件接收文件。
我们首先创建一个变量来保存字符串。虽然我们可以直接通过show_debug_message
函数传递字符串而不使用变量,但我们将在以后的调试测试中使用这个变量。
-
由于此脚本已经附加到我们的玩家创建事件,我们可以直接运行游戏。单击运行调试模式图标。
-
当游戏在浏览器中启动时,将会弹出一个带有调试控制台的第二个窗口,如下一张截图所示。如果您没有看到此窗口,请检查浏览器是否允许弹出窗口。滚动到调试输出列的底部。在这里,您应该看到调试消息
Hello World
。这表明我们的代码已成功执行。如果我们没有如预期看到它,那么我们就会知道游戏出了问题的地方。 -
我们还可以看到游戏中每个实例的所有属性,包括它们的实例编号,当前在房间中的位置,它正在显示的精灵等。单击实例列中的任何一个数字,然后查看实例数据列中的属性。
-
点击暂停/恢复按钮。这样我们就可以暂停游戏,如果你有很多调试消息涌入控制台,并且想要花时间看看发生了什么,这是很有用的。
-
最后,我们可以点击清除控制台按钮,从调试输出列中删除所有内容。
恭喜!现在你可以开始调试你的脚本了。虽然在游戏开发过程中你会经常使用show_debug_message
,但是保持活跃消息的数量最少是很重要的。有太多调试消息发生,以至于你看不到发生了什么是没有意义的!
使用 Windows 版本调试器
虽然你可以通过调试消息解决大部分问题,但有时你需要更详细的了解游戏中发生了什么。GameMaker: Studio 有一个更高级的调试器,只有在游戏被定位为 Windows 版本时才会运行。如果我们不至少粗略地看一下这个精彩的工具,那就不够意思了。
- 将目标更改为
Windows
,并以调试模式运行游戏。游戏打开时,GameMaker 调试器将显示在一个单独的窗口中,如下图所示:
一些基本信息会立即显示出来,比如它的表现如何,通过查看房间速度:(每秒步数)和每秒帧数(FPS:)。如果你把鼠标光标移到游戏中的实例上,你会注意到**鼠标 id:**会改变。这个 ID 是该特定实例的唯一标识符,非常方便。
GameMaker 调试器窗口有更多选项可用于调试游戏。运行菜单不仅允许我们暂停游戏,还可以一步一步地向前走。监视菜单允许您跟踪特定表达式,比如函数调用或属性。工具菜单不仅可以访问调试消息,还可以显示所有全局变量、每个实例的变量,以及当前存在的所有实例的列表。让我们看看这个控制台中实例有什么信息。
-
导航到工具 | 显示实例。这将打开一个窗口,显示游戏中的所有实例。
-
滚动列表,直到找到
obj_Player
。双击它,这样我们就可以看到它的所有属性。就像 HTML5 调试控制台一样,我们可以看到它在世界上的位置以及它有哪个精灵(通过精灵索引号)。然而,如果你滚动列表,还有许多其他属性。事实上,如果我们看列表底部,我们可以看到myText
变量。太棒了!
查看 JavaScript 代码
我们要看的最后一件事是编译后的 JavaScript 代码。所有现代浏览器,比如 Mozilla Firefox、Microsoft Internet Explorer 9.0 和 Google Chrome 都带有内置的调试控制台,允许任何人查看任何网站的源代码,甚至影响本地屏幕上显示的内容。没错。每个人都可以看到游戏的代码。虽然这可能吓到你,但不用担心!当 GameMaker: Studio 导出游戏或正常运行时,它会对代码进行混淆,使其非常难以解读。另一方面,在调试模式下运行时,除了引擎本身,它不会进行任何混淆。
让我们快速看一下这段代码是什么样子的。我们将从调试版本开始,这样我们就可以看到没有混淆时它是什么样子的。在这个例子中,我们将使用 Chrome,因为它有最强大的调试控制台。
-
将目标平台设置为
HTML5
,以调试模式运行游戏。 -
在游戏下方的浏览器窗口中,右键单击并选择检查元素。这将为 Chrome 打开开发者工具。
-
选择源代码选项卡,在左上角点击名为显示导航器的小图标。
-
在导航器中有一个目录树。打开文件夹,直到找到
html5
文件夹。在这个文件夹里是游戏。点击游戏,我们应该看到所有的代码,就像下一个屏幕截图中所看到的那样。如果我们浏览代码,我们可以清楚地看到我们创建的脚本,对象的属性等。 -
现在让我们看看混淆版本。关闭浏览器标签,然后以正常模式运行游戏。重复相同的过程并查看代码。它应该看起来像下一个屏幕截图。我们仍然可以读取一些片段,但其中没有任何意义。您可以相当确信,很少有人会想要干预这个。
摘要
嗯,就是这样。在本书的第一章中,您已经制作了自己的第一个 HTML5 游戏。这样做,您有机会探索 GameMaker: Studio 界面并熟悉它。您还创建并实现了所有类型的资源,同时利用了各种资源编辑器。希望您已经意识到,这款软件让您轻松地为网络制作游戏。凭借您已经获得的知识,您可以开始制作更高级的游戏。例如,为什么不添加射击,因为您知道如何使用按键事件,使对象移动,并在碰撞时执行操作?
在下一章中,我们将深入研究资产创建。游戏的好坏取决于它的外观和声音。我们将学习如何创建动画角色,构建一个瓷砖集来装饰一个房间,并使用音频来增加氛围。让我们继续前进,因为事情即将变得更加令人兴奋!
第二章:AAA 游戏:艺术和音频
现在我们已经熟悉了界面导航,并建立了一个简单的游戏,我们可以开始创建更复杂的项目。在本章中,我们将专注于创建艺术作品,添加动画,并实现音频音景。这三个元素对于游戏的创建非常重要,因为它们每个都有助于玩家理解发生了什么,并使体验更加沉浸。我们构建游戏的方式可能会受到我们使用的资产类型以及它们的实施方式的极大影响。我们将首先看看如何导入外部图像,然后进行一些实际示例,如如何创建一个瓷砖集并制作一个动画角色。然后我们将转向音频文件,以及如何为游戏添加环境氛围。最后,我们将简要讨论如何使游戏看起来更专业。让我们开始吧!
制造艺术资源
在创建游戏时,大多数艺术资源都将在外部程序中创建,并且需要导入。GameMaker: Studio 确实有一个内置的图像编辑器,我们稍后会进行调查,但其功能相当有限。它非常适合创建简单的艺术作品,但还有许多其他工具可以为我们提供更高级的复杂艺术创作技术。
有许多受欢迎的软件选项供您考虑。最全面的选择和最昂贵的选择是 Adobe Photoshop,这是大多数专业艺术家的首选,可以在www.photoshop.com/
购买。一个具有许多类似功能的免费替代品是 GIMP,可在www.gimp.org/
下载。这两个软件包都提供了一套高级工具,用于创建图像。还有许多其他更简单的工具可供选择,例如 Pickle www.pickleeditor.com/
,Spriter www.brashmonkey.com/
和 PyxelEdit pyxeledit.com/
,所有这些工具都是免费的,值得一试。
如果您只想跳过艺术创作,而更喜欢一些预制的作品,有很多地方可以下载精灵。最受欢迎的精灵网站之一是 Spriters Resource spriters-resource.com/
。他们拥有您能想象到的各种类型游戏的资源。您还可以查看 GameMaker 论坛gmc.yoyogames.com/
。在这里,您会找到许多愿意制作或分享他们的艺术资源的活跃人士。
了解图像文件格式
GameMaker: Studio 能够导入四种图像类型:BMP、GIF、JPG 和 PNG。每种格式都有其独特的功能和缺点,这将决定它们应该如何使用。BMP 格式是如今最不常用的格式,因为数据未经压缩。未经压缩的图像通常被认为效率低下,因为它们的文件大小很大。GIF 是唯一可以制作动画的格式,但限于 256 种颜色和单一透明级别。这非常适合经典的 8 位风格艺术,其中所有内容都有硬边缘。JPG 图像由于没有任何透明度和其有损压缩格式,具有最小的文件大小。这是背景和不透明精灵的不错选择。PNG 图像格式最有用,因为它们比 BMP 更有效,具有 1600 万种颜色和完全透明度,并且这是 GameMaker: Studio 在编译游戏时输出为纹理页的格式。
在本书中,我们将只使用两种图像格式,GIF 和 PNG。我们将使用 GIF 图像来制作所有动画,因为这是导入动画的最简单方式。与上一章一样,如果我们加载一个动画 GIF 图像,每一帧动画都将在精灵属性编辑器中分开。不幸的是,这意味着我们在角色的艺术风格上受到了限制,由于单一的透明度水平,我们的角色边缘会有硬边。如果我们想要更平滑、更清晰的外观,我们需要使用 PNG 图像来进行反锯齿处理。试图在 GIF 图像中获得平滑的边缘是艺术家可能犯的最常见的错误之一。正如我们将在下面的截图中看到的,左侧是一个具有清晰硬边的 8 位艺术风格的 GIF 图像,右侧是一个具有平滑、反锯齿边缘的 PNG 图像。
在中间,我们有相同的平滑精灵,使用 PNG 保存,但保存为 GIF。注意曾经略微透明的边缘像素现在是一个实心的白色轮廓。
导入精灵表
尽管本书中的所有动画都将使用 GIF 图像出于便利性的考虑,但如果我们不介绍如何导入精灵表,那就有失职了。精灵表通常是一个 PNG 文件,其中包含一个对象(如角色)的所有动画帧,均匀地放置在一个网格中。然后我们可以快速地在 GameMaker 中剪切出每一帧动画,以构建我们需要的单个精灵。让我们试一试!
-
让我们从打开一个名为
Chapter_02
的新项目开始。 -
创建一个新的精灵,并命名为
spr_PlayerSpriteSheet
。 -
点击编辑精灵按钮打开精灵编辑器。
-
在文件下,选择从条带创建,然后在图像信息部分中不选择任何内容,打开
Chapter 2/Sprites/PlayerSpriteSheet.png
。这将打开加载条带图像编辑器。 -
我们刚刚加载的精灵表包含了一个六帧的奔跑循环。由于我们需要所有的帧,所以我们需要将图像数量设置为
6
。 -
精灵表的布局有两行三个图像。将每行图像数设置为
3
。 -
由于每个图像的大小为 64 x 64 像素,我们需要将图像宽度和图像高度设置为
64
。对于如此小的精灵表来说,偏移和分离的其他选项并不是必要的,但如果我们有这个角色的完整动画集,它们将会很有用。设置应该如下图所示: -
点击确定。我们现在有一个带有平滑边缘的动画精灵!
-
我们已经完成了这个精灵。现在点击精灵编辑器和精灵属性编辑器的复选标记,然后点击确定按钮关闭它。
介绍图像编辑器
使用 GameMaker: Studio 开发的一个重要好处是它内置了一个用于创建精灵和背景的图像编辑器。这个编辑器可能看起来非常基础,但有很多优秀的可用工具。有各种不同的绘图工具,包括标准工具,如铅笔、橡皮擦和填充。编辑器中一个非常有用且独特的功能是能够用鼠标的两个按钮进行绘画。颜色 | 左和颜色 | 右颜色选项,如下图所示,表示根据使用左键或右键,将使用的颜色。我们还可以通过变换和图像菜单调整各种东西。变换菜单包含影响图像中像素大小和位置的能力。图像菜单包含图像修改工具,如改变颜色、模糊图像和添加发光效果。
与其谈论图像编辑器,不如在其中构建一些艺术资源。我们将首先创建一个图块集,然后转移到一个动画角色,这两者都可以在第四章中稍后使用,冒险开始。如果您更愿意在外部编辑器中工作,也可以跟着做,因为创建这些资源的一般理论是普遍适用的。
使用图块集创建背景
图块集是一种特殊类型的背景资源,允许游戏在不使用大量计算机内存的情况下在环境中拥有巨大的变化。保持文件大小和内存使用量小是非常重要的,特别是对于 HTML5 游戏。浏览器需要下载所有这些资源,因为我们不知道用户有多强大的计算机。
创建自然外观的图块集主要是为了欺骗眼睛。我们的眼睛非常擅长发现模式;当有重复时,它们会识别形状、对比和颜色的差异。知道我们的大脑是这样硬编码的,让我们能够利用这一点。我们可以通过使用奇怪的形状、最小化对比和在艺术作品中使用类似的颜色来打破模式。
我们将为游戏中最常见的表面之一创建一个图块集:石头地板。现在这可能看起来很容易,但惊人的是这经常被错误地完成。
-
创建一个新的背景资源,并命名为
bg_StoneFloor
。 -
由于我们希望这是一个图块集,请确保勾选用作图块集的复选框。这将显示图块属性,允许您设置图块的宽度和高度、偏移和间隔。
-
将图块宽度和图块高度设置为
32
,如前面的图像所示。我们现在准备开始构建图块。 -
点击编辑背景按钮。这将打开图像编辑器。
-
我们将从创建所有其他图块将基于的主图块开始。在图像编辑器中,选择文件 | 新建,并将宽度和高度设置为
32
。 -
选择填充区域工具,并将浅灰色应用到整个精灵上。这是基础,我们稍后会更改颜色。
在开始绘制一堆石头之前,我们需要首先考虑潜在的问题和解决方案。人们在创建平铺图块时最常见的问题是他们试图直接创建最终产品,而不是逐步构建。这包括在确保可以正确平铺之前选择颜色和添加细节。
在查看平铺纹理时,我们需要确保尽量打破网格。整个世界将基于小的 32 x 32 像素图块,但我们不希望观察者注意到这一点。因此,我们的目标是使用不规则的形状,并尽量避免水平和垂直对齐。
-
选择在图像上绘制工具和深灰色。
-
为了让生活变得更容易,我们可以放大图像。这可以通过放大镜或中间鼠标滚动按钮来完成。
-
绘制小石头的轮廓,但记得保持一定的大小和形状的变化。另外,不要忘记将对角线线条保持在一个像素的宽度上!一旦你做到了这一点,它应该看起来类似于前面的示例截图。
-
从图像编辑器菜单中,选择转换 | 移动。这将打开移动图像对话框,允许您水平或垂直移动像素。
-
将水平和垂直值设置为
16
,并勾选水平包裹和垂直包裹框。这将使图像向下和向右移动 16 像素(瓷砖大小的一半),并将剩余的像素包裹起来,如前面的屏幕截图所示。 -
点击确定。
通过移动像素,我们现在可以看到边缘是如何铺砌的。你可能会注意到它并不完美。在下面的示例截图中,你可以看到有几条线只是结束了,没有形成完整的石头。你可能也不喜欢某些石头的大小,或者看到一些线条太粗。目标是修复这些问题,并重复这个过程,直到一切都符合你的要求。
-
在必要的地方画线并覆盖旧线,以修复任何看起来不正确的石头。
-
使用相同的设置重新应用变换 | 移动工具。如果看到错误,修复它们并重复,直到你满意。
一旦我们对瓷砖图案和沿边缘正确重复感到满意,我们就可以开始添加颜色了。一般来说,最好不要使用完全脱饱和的灰色调来代表石头,因为大多数石头都有一些颜色。在选择颜色时,目标是避免只使用单一颜色和明暗变化,而是选择一系列相似的颜色。为此,首先选择一个中性的基础颜色,比如米色。然后,每种额外的颜色都应该在色调、饱和度和亮度上略有变化。例如,第二种颜色可以比第一个米色略微偏红,略微不那么鲜艳,比第一个米色暗一些。
-
选择浅褐色,并使用填充区域工具填充一些石头。
-
使用其他褐色变种重复这个过程,直到没有灰色的石头剩下。
-
填满所有的石头后,我们需要确保它仍然可以铺砌。使用变换 | 移动 来查看颜色是否正确对齐。如果有任何问题(如前面的截图所示),只需调整颜色,直到你再次满意。
基础瓷砖的最后一步是将深灰色线条改为深褐色。现在你可能会认为这将是非常乏味的,但幸运的是,图像编辑器有一个工具可以让这变得容易。
-
用鼠标左键选择深褐色。这种颜色应该出现在颜色 | 左下方。
-
选择更改所有相同颜色的像素工具,如前所示,然后在一个深灰色像素上单击左键。现在石头的轮廓应该都是深褐色,就像我们将在下面的截图中看到的那样:
干得好!现在我们有了一个基础瓷砖,可以用来制作其他所有瓷砖。下一步是添加边框瓷砖,以便有一个用于分隔不同材料的边缘。如果我们要有一个正方形房间,我们将需要总共九块瓷砖:基础瓷砖和代表边缘和角落的八块瓷砖。让我们给我们的画布增加一些空间,并用我们的瓷砖填满它。
-
选择变换 | 调整画布。
-
将新尺寸 | 宽度和新尺寸 | 高度增加
300
%或96
像素。然后在位置下点击中心方块,使画布在我们创建的瓷砖周围扩展。设置如下屏幕截图所示。 -
你需要确保一切都正确对齐,所以打开网格。选择视图 | 切换网格 或点击切换网格图标。
-
此时您可能看不到任何网格。这是因为默认网格设置为 1 x 1 像素。选择视图 | 网格选项打开网格设置。将水平大小和垂直大小更改为
32
,并选中对齐到网格。如果需要,可以随意更改颜色,就像我们在之前的屏幕截图中所做的那样。然后点击确定。 -
使用选择区域工具,拖动以选择整个基础瓷砖。
-
复制(Ctrl + C)和粘贴(Ctrl + V)瓷砖,然后将其拖放到一个可用的空间中。重复此步骤,直到所有九个位置都有一个基础瓷砖,就像以下的屏幕截图中所示:
-
返回到视图 | 网格选项,关闭对齐到网格。否则,您将在尝试绘制边框时感到非常沮丧!使用瓷砖集创建背景
-
我们希望边框厚度为八个像素。使用与石头相同的颜色,使用绘制线条工具在瓷砖集的外围创建一个边框,就像之前看到的那样。
太棒了!我们现在有了一个基本的瓷砖集,让我们来测试一下。
-
如果您还没有一个,创建一个新房间。
-
在房间属性编辑器中,选择瓷砖选项卡。
-
如果尚未选择,请将背景图像设置为
bg_StoneFloor
。 -
要选择一个瓷砖,只需在预览区域左键单击要使用的瓷砖,如前面的屏幕截图所示。
可以有多个层的瓷砖,这在您想要放置奇形怪状的瓷砖(树木、路标)时非常有用,而无需为每种表面类型(石地板、草地)创建新的瓷砖。它还可用于编译多个瓷砖以创建更自然的表面,例如在石地板上放置一个泥土瓷砖组。
-
我们将保持简单,所以让我们将当前瓷砖图层保留在
1000000
。 -
在房间中,使用左键单击放置单个瓷砖,或按住Shift在房间中绘制瓷砖。尝试布置瓷砖,就好像有多个带走廊的房间,就像以下屏幕截图一样。
看起来相当不错,但有一些明显的问题,特别是内角没有边框。您可能还会觉得在这么大的区域里,瓷砖重复得有点太多了。由于我们将为第一个问题创建更多的瓷砖,我们也可以为第二个问题添加一些!
-
如果尚未打开,请重新打开
bg_StoneFloor
并选择变换 | 调整画布大小。 -
将大小增加
133
%,或者到128
像素。在位置下点击左上角箭头,然后点击确定。现在它应该看起来像以下的屏幕截图。 -
选择视图 | 切换网格,这样我们就可以看到网格。我们需要复制原始的基础瓷砖,可以在第二行和第二列找到。
-
使用选择区域工具,选择原始基础瓷砖的像素。
-
将此瓷砖复制并粘贴到图像外边缘的每个空单元格中。
-
我们需要创建四个角瓷砖来修复我们的房间布局。为此,我们将使用刚刚放置的右侧边缘的四个瓷砖。使用在图像上绘制工具绘制角落的修饰,并对所有四个角瓷砖重复此操作。
我们还有三个沿底部的瓷砖,我们将用作基础瓷砖的替代品。只要不影响外边缘周围的一个像素边框,我们可以随意更改内部,它仍然可以正确平铺。
-
更改内部每个剩余瓷砖的一些石头的形状并交替颜色。如下截图所示,平铺集现在完成了!使用平铺集创建背景
-
最后,回到房间,根据需要放置角落瓷砖,并铺设备选瓷砖的随机变化。
正如您所看到的,使用一个小小的 128 x 128 纹理,我们可以轻松填满一个大区域,同时提供随机性的错觉。为了增加更多变化,我们可以轻松地创建调色板交换版本,从而可以调整色调和饱和度。因此,我们可以有一个蓝灰色的平铺集。通过更多的练习,我们可以开始添加诸如阴影之类的细节,以增加世界的透视。对于您未来的平铺集,只需记住使用非均匀形状,最小化对比度,并仅轻微变化颜色。更重要的是,始终确保基本平铺正确重复,然后再构建边缘和备选!
动画和创建精灵
动画精灵是一系列静态图像,播放时看起来有动作。它让玩家知道他们正在奔跑,当他们用剑攻击时,以及按钮是可点击的。好的游戏在所有互动元素上都有动画,通常还有许多背景元素上也有动画,以至于您可能甚至都没有注意到。正是诸如动画之类的微小细节真正为游戏注入了生命。
行动的错觉
创建动画需要时间和敏锐的眼光,但基本的动画,甚至是角色的动画,每个人都可以做到。有一些重要的规则可以让动画变得更容易。首先,它关乎动作的外观,而不是动作的准确性。如下截图所示,第一个挥剑动画在技术上是准确的;剑会在每个位置。然而,第二个集会看起来更自然,因为它包括了人们在挥剑时所期望看到的模糊效果。
最大化精灵空间
第二条规则是最大化精灵空间。大多数游戏使用基于框的碰撞而不是像素完美的碰撞。因此,您希望尽可能多地利用精灵可用于所需动画的空间。通常开发人员会浪费很多空间,因为他们在考虑现实世界而不是游戏世界。例如,一个常见的问题可以在跳跃动画中看到。在下面的截图中,第一个跳跃动画中的角色从地面起跳,跳到空中,落下并着陆。第二个跳跃动画是一样的,但所有空白空间都被移除了。这不仅更有效,而且还可以帮助防止碰撞错误,因为我们始终知道碰撞框的位置。
循环动画
最后一个重要规则,可能也是最重要的规则是可重复性。大多数游戏动画在某个时候都会循环,而有一个明显重复的序列对玩家来说会非常刺眼。这种可重复性问题的一个常见原因是动画太多。动画帧数越多,出现问题的可能性就越大。关键在于简单化并删除不需要的帧。在下面的截图中,您可以看到两个奔跑动画,第一个有五帧,第二个只有三帧。顶部的看起来会更流畅一些,但由于步幅的轻微差异,重复性会稍微差一些。第二个最终看起来会更好,因为它的帧数更少,步幅的差异也更小。
牢记这三条规则,让我们来制作一个简单的角色奔跑循环:
-
创建一个新的精灵,并命名为
spr_WalkCycle
。 -
点击编辑精灵;这将打开精灵编辑器。这个编辑器用于处理组成动画精灵的所有单个图像。
-
在精灵编辑器中,选择文件 | 新建,这将打开一个新图像尺寸的对话框。保持为
32
x32
,然后点击确定。 -
现在你应该看到,就像之前的截图一样,在精灵编辑器中有一个名为图像 0的空图像。双击图像打开图像编辑器。
现在我们需要一个角色设计。在设计角色时,你需要考虑角色要做什么,他们存在的世界以及碰撞区域。在我们的情况下,角色只会行走,世界将是一个户外冒险游戏,并且会有一个大的方形碰撞框。
注意
如果你不想自己设计角色,我们提供了一个精灵,Chapter_02/Sprites/WalkCycle.gif
,其中包含了动画的第一帧。
- 我们将创建的第一帧动画应该是角色在行走循环的最大伸展,腿之间距离很远,触及精灵的底部。角色在这一帧上将处于迈步的最低点,所以确保头部距离精灵顶部至少一个像素,最好是两个像素。
在前面的截图中设计的角色是一种穿着夹克的猿类生物。穿夹克的原因是在摆动时使手臂更易读。我们可以看到这个角色相当厚,这使得大碰撞区域更加真实。最后,后腿稍微更暗,好像有一个阴影。再次强调,这是为了帮助可读性。
一旦我们对第一帧满意,我们需要继续下一个关键帧。关键帧是动画中发生最大变化的点。在这种情况下,当角色处于最高点时,手臂和腿交叉时就是关键帧。
-
在精灵编辑器中,选择动画 | 设置长度,将帧数设置为
3
,如前面的截图所示。这将复制第一帧两次,给我们增加两帧动画。 -
打开图像 1并使用选择区域工具将身体的上半部分提高到精灵的顶部,如下截图所示。这一帧将代表迈步的最高点,角色站在一只脚上,另一只脚越过。我们还可以选择并移动手和脚,快速将它们放到正确的位置。
-
使用铅笔和橡皮擦工具,将手臂和腿画到适当的位置,前腿着地,后腿抬起,只有一只手臂显示。一旦你对外观满意,关闭图像。
-
打开图像 2。这是第一帧的相反运动,这样改变起来相当容易。手和脚已经在正确的位置,所以我们只需要相应地重新绘制手臂和腿,如左侧截图所示。完成后关闭图像。
-
现在我们需要复制图像 1并将其放在末尾,以便行走循环。选择图像 1并复制并粘贴帧。这将复制帧,并标记为图像 2。
-
选择图像 2并点击精灵编辑器工具栏中的右箭头。这将把帧移到动画的末尾。选择并打开图像 3,这样我们就可以重新绘制腿,使后腿着地,前腿在空中越过。完成后关闭编辑器。
-
要查看动画的播放情况,请在Sprite Editor中选中Show Preview复选框,并将Speed设置为
5
。请参阅以下截图。
就是这样!一个不错的循环行走动画,虽然有点生硬。如果我们想要稍微平滑这个动画,只需在关键帧之间添加一帧动画,然后按照刚才进行的相同步骤进行。最终应该看起来类似于以下截图:
制作音频
音频对于创建专业质量的游戏非常重要。不幸的是,它通常是最被忽视的元素,也是最后实施的。其中一个原因是我们可以在没有音频的情况下玩游戏,仍然享受体验。然而,游戏中良好的声音景观将使其更具沉浸感,并有助于改善用户反馈。
为了创建音频,我们需要使用外部软件,因为 GameMaker: Studio 没有内置的音频创建工具。有各种软件选择可供选择。用于创建音效和音乐的流行程序包括非常全面的Reason,www.propellerheads.se/
,它模拟了一台合成器、混音台和其他组件的机架。在免费方面,还有BFXR,www.bfxr.net/
,可以让您在线创建游戏音效,还有Sonant,sonantlive.bitsnbites.eu/
,用于制作音乐。所有这些软件包都很有趣且易于使用。需要记住的一点是,音频的创建非常具有挑战性。有时最好只是下载一些免费音乐或音效,有很多网站提供免费和可购买的音频。Freesound,http://www.freesound.org
,有成千上万的音频剪辑可供下载和使用。对于更经典的 8 位风格音乐和音效,还有8-bit Collective,http://8bc.org/
,这是一个专门用于游戏音频的网站。
了解音频文件格式
如果添加音频还不够具有挑战性,HTML5 会使它变得更加困难。我们将遇到的第一个困难是 HTML5 音频标签尚未标准化。有两种文件格式竞相成为官方 HTML5 标准:MP3 和 OGG。MP3文件格式是最常用的格式之一,但缺点是需要许可和专利,这可能导致支付大额费用。OGG文件格式既是开源又不受专利保护,因此是一个可行的替代方案。除此之外,各种浏览器对文件类型有自己的偏好。例如,Internet Explorer 接受 MP3 但不接受 OGG,而 Opera 接受 OGG 但不接受 MP3。Google Chrome 和 Mozilla Firefox 则支持两种格式。GameMaker: Studio 通过在游戏导出时将所有音频转换为 MP3 和 OGG 文件格式来解决这个问题。
使用 GM:S 音频引擎
GameMaker: Studio 配备了两种不同的声音引擎来控制游戏中的各种音频:GM:S 音频和传统声音。这些系统彼此完全独立,您可以在游戏中使用其中一个系统。
GM:S 音频引擎是新的、更强大的声音系统,旨在通过发射器和听者实现完整的 3D 声音景观。发射器允许在游戏空间中定位声音发生的位置。有添加声音衰减、模拟移动的速度等功能。听者通过根据玩家在游戏中的位置、包括他们的方向和速度来播放声音,提供更多的控制。如果您不声明一个听者,那么声音将变得普遍。这将最终成为 GameMaker: Studio 中的主要音频引擎,但由于 HTML5 音频问题,它在所有浏览器中都无法正常工作。
Legacy Sound 引擎是 GameMaker 使用的原始声音系统,正如其名称所示,这个引擎已不再得到积极开发,并且许多功能已经过时。这是一个更简单的系统,没有 3D 功能,尽管对于大多数游戏来说这将是足够的。这个引擎的一个很大的好处是音频应该在所有浏览器中都能工作。
在本书中,我们将一直使用 Legacy Sound 引擎以确保最大的功能,但我们需要知道如何使用 GM:S 音频引擎以备将来使用。让我们通过创建一个非常简单的定位声音演示来测试这些功能。我们将在房间中创建一个对象,并使其播放一个只有当鼠标接近位置时才能听到的声音。
-
要选择使用哪个系统,请单击资源 | 更改全局游戏设置。在常规选项卡中,有一个使用新音频引擎复选框;确保您选中它。如果选中,它将使用 GM:S 音频引擎;如果没有,则使用 Legacy Sound。
-
创建一个新声音并命名为
snd_Effect
。 -
加载
Chapter 2/Sounds/Effect.wav
。确保类型设置为普通声音。 -
创建一个新对象并命名为
obj_Sound
。 -
创建一个新脚本并命名为
scr_Sound_Create
。首先,我们需要创建一个发射器并将其捕获在一个变量中:
sem = audio_emitter_create();
- 接下来,我们将发射器定位到我们对象的位置。此函数的参数是:要应用此函数的发射器和 X/Y/Z 坐标。我们将使用对象的 X 和 Y,但由于这是一个 2D 示例,我们将 Z 设置为 0:
audio_emitter_position(sem, x, y, 0);
- 我们还希望在发射器上有一个衰减,使得随着听者的接近声音变得更大。我们拥有的参数是:发射器、声音在一半音量时的距离、总的衰减距离和衰减因子:
audio_emitter_falloff(sem, 96, 320, 1);
- 发射器已经设置好了;现在让我们在发射器上播放声音。此函数的参数是:发射器、要播放的声音、是否应该循环以及其优先级。我们希望这个循环,这样我们就可以听到声音:
audio_play_sound_on(sem, snd_Effect, true, 1);
- 当所有内容放在一起时,此代码已完成并应如下所示:
sem = audio_emitter_create();
audio_emitter_position(sem, x, y, 0);
audio_emitter_falloff(sem, 96, 320, 1);
audio_play_sound_on(sem, snd_Effect, true, 1);
-
添加一个创建事件,并将一个控制 | 执行脚本图标拖放到附有此脚本的操作中。
-
现在声音将播放,但在我们有听者之前,它将没有方向。我们将根据鼠标的位置在每一步上移动听者的位置。创建一个新脚本并命名为
scr_Sound_Step
。 -
我们只需要一行代码来定位听者的 X/Y/Z 坐标。X 和 Y 将设置为鼠标的 X 和 Y,再次 Z 设置为
0
。
audio_listener_position(mouse_x, mouse_y, 0);
-
在
obj_Sound
对象上,添加一个Step | Step事件,并将一个Execute Script图标拖放到附有步骤脚本的操作中。 -
打开房间,并将
obj_Sound
对象的实例放在房间的中心。 -
运行游戏。
你应该能够听到声音很轻,并且当你把鼠标移到屏幕中心附近时,声音应该变得更大声。如果你有环绕声或耳机,你还会注意到声音从左到右的声道移动。这只是 GM:S 音频引擎可以做的一些示例,一旦它在所有浏览器中都能正常工作,就会变得令人兴奋。
提高质量标准
当我们看着成千上万的游戏时,很容易辨认出顶尖游戏和底层游戏。然而,当我们在整个光谱上看所有最好的游戏时,它们之间存在着明显的差异。有些游戏非常简约,有些是逼真的,而有些是奇幻的。这些游戏可能是由少数人制作的,也可能是由一大队专家团队制作的。是什么让根本上如此不同的游戏仍然能够达到相同的质量定义呢?答案非常简单,可以用三个一般原则来概括:一致性、可读性和抛光。虽然创作高水准的艺术和音频确实需要通过多年的学习和实践获得的技能,但遵循这些几条规则将有助于使任何游戏看起来更加专业。
一致性
一致性听起来很明显,但实际上比人们预期的要具有挑战性得多。每个精灵、背景或其他艺术资源都需要按照相同的规则集构建。在下面的截图中,你可以看到飞机在城市背景下飞行的三种变化。第一张图片完全不一致,因为它有一个平面阴影和像素块风格的飞机,以及一个逼真的背景。下一张图片比第一张图片更一致,因为城市是平面阴影的,但缺乏像素块风格的清晰度。这是大多数人可能会停下来的地方,因为它已经足够接近了,但仍然有改进的空间。最后一张图片是最一致的,因为所有东西都有平面阴影和像素块风格。
这个过程同样可以轻松地朝相反方向进行,让飞机变得更加逼真。所需的只是选择一组选项,并将其均匀应用到所有内容上。
可读性
可读性就是确保向用户传达正确的信息。这可能意味着很多事情,比如确保背景与前景分离,或者确保可收集的物品不像危险物品。在下面的图片中,有两组药水;一种是毒药,另一种是治疗药水。仅仅改变颜色对玩家来说并不那么可读,而用骷髅头表示毒药,用心脏表示治疗药水则更容易让玩家理解。重要的是让玩家能够轻松理解发生了什么,以便他们能够做出反应而不是思考。
抛光
最后,尽管通常不太显眼,但最重要的因素是抛光。抛光关乎细节。它涵盖了很多方面,从收集物品时产生粒子效果到确保记分牌正确居中。在下面的图片中,我们有两个带有统计条的头像图标。左边的那个在功能上是正确的,看起来还不错。然而,右边的那个似乎更加抛光。统计条被移到左边,这样它们和头像图标之间就没有间隙了,头像图标也被正确地居中了。希望你能看到一些微小的调整如何能够大大提高抛光的质量。
总结
为游戏创建艺术和音频是一项巨大的任务,无论是在所需的时间还是要制作的资源方面。作为游戏开发者,您有责任确保一切都是连贯的和美观的,无论是创建资源还是与艺术家和音效设计师合作。在本章中,您已经开始了解在 GameMaker: Studio 中艺术和音频是如何工作的,以及好和足够好之间的区别。您了解了可接受的图像格式以及如何导入精灵表。您创建了一个将更好地利用计算机内存并允许创建大型独特世界的平铺集。您对精灵进行了动画处理,并使其正确循环。您还学会了如何控制声音以及它们的听觉方向。现在,您已经准备好开始制作真正的游戏了!
在下一章中,我们将构建我们的第二个游戏,一个横向卷轴射击游戏。我们将创建一个在屏幕上移动的玩家,建立几个射击武器的敌人,创建移动背景,并实现胜利/失败的条件。最令人兴奋的是,我们将在学习GameMaker 语言(GML)的同时完成所有这些工作。
第三章:射击游戏:创建横向卷轴射击游戏
在本章中,我们将创建一个非常简单的横向卷轴射击游戏,这将使我们了解使用 GML 代码制作完整游戏的基础知识。我们将有一个玩家角色,可以在游戏区域内移动并发射武器。如果他们与敌人或敌人的子弹相撞,他们将被摧毁,并且如果他们还有剩余生命,可以重新生成。
我们将创建三种不同类型的飞越屏幕的敌人:
-
FloatBot:它没有武器,但很难击中,因为它在移动时上下浮动。
-
SpaceMine:它是最慢的敌人,如果玩家靠得太近,它会发射一圈子弹。
-
Strafer:它是飞行速度最快的敌人,直线飞行并直接朝玩家位置发射子弹。
我们将通过显示得分和玩家生命,滚动背景以营造移动的错觉,播放音乐并添加爆炸效果来完善游戏。最后,我们将通过实现胜利/失败条件来重新开始游戏。游戏将如下截图所示:
编码约定
为了编写有效的代码,无论编程语言如何,遵循推荐的编码约定是很重要的。这将有助于确保其他人可以阅读和理解代码尝试做什么并对其进行调试。虽然许多语言遵循类似的指南,但编程实践没有通用标准。GameMaker 语言(GML)没有官方推荐的约定集,部分原因是它被开发为学习工具,因此非常宽容。
对于本书,我们将根据常见做法和学习的便利性来定义自己的约定。
-
除了房间外,所有资产都将以简单的类型标识符和下划线开头。例如:
-
精灵:
spr_
-
对象:
obj_
-
脚本:
scr_
-
即使可以使用执行代码 DnD 直接在事件上编写代码,所有代码都将放置在脚本中,并且命名约定将指示其附加到的对象和应用的事件。这将使以后更容易找到以进行调试。例如,放置在玩家对象的创建事件上的代码将具有名为
scr_Player_Create
的脚本。 -
如果脚本打算由多个对象使用,则名称应使用清晰描述其功能的名称。例如:要在物体离开屏幕后移除物体,脚本将被命名为
scr_OffScreenRemoval
。 -
变量将使用驼峰命名法编写,其中使用多个单词;第一个单词以小写字母开头,每个后续单词以大写字母开头,例如:
variableWithManyWords
。 -
布尔变量应该以问题的形式提出,例如:
canShoot
,isPlaying
。 -
常量使用全大写字母和下划线来分隔单词,例如:
LEFT
,MAX_GRAVITY
。 -
if
语句中的表达式始终用括号括起来。GameMaker 不要求这样做,但这样做可以更容易阅读代码;例如:if (x > 320)
。
建造玩家
我们将从构建我们的玩家对象开始。我们已经简要描述了设计,但我们还没有将设计分解为可以开始创建的内容。首先,我们应该列出每个功能及其包含的内容,以确保我们拥有所有我们需要的变量和事件。
-
箭头键将使玩家在游戏区域内移动
-
必须保持在游戏区域内
-
空格键将发射武器
-
每次按下按钮都会发射一颗子弹
-
与子弹或敌人碰撞会造成伤害
-
应该根据类型有不同的值
设置玩家精灵
让我们创建玩家精灵并为游戏做好准备:
-
创建一个新项目并将其命名为
Chapter_03
。 -
创建一个新的精灵并命名为
spr_Player
。 -
点击加载精灵,加载
Chapter 3/Sprites/Player.gif
,勾选移除背景。这个.art
文件有一个带有透明度和几帧动画的太空飞船。
接下来,我们要调整太空飞船的碰撞区域。默认的碰撞是一个覆盖具有像素数据的精灵整个区域的矩形。这意味着即使外观上没有接触任何东西,飞船也会受到伤害。我们希望的是有一个非常小的碰撞区域。
- 点击修改遮罩。这将打开遮罩属性编辑器,如下截图所示:
在遮罩属性编辑器中,我们可以控制碰撞遮罩的大小、形状和位置,即精灵中进行碰撞检测的区域。一些游戏需要像素级的碰撞检测,即根据单个像素确定碰撞。这是最精确的碰撞检测,但也需要大量计算。然而,大多数游戏可以使用简单得多的形状,比如矩形。这种方法更有效,但限制了碰撞的视觉准确性。选择哪种方法取决于游戏的设计以及为实现期望的结果需要多少控制。
-
我们希望完全控制碰撞区域,所以将边界框设置为手动,并将形状保留为矩形。
-
调整边界框参数有两种方法。我们可以输入框的角落的确切位置,或者直接在精灵图像上绘制框。用鼠标左键拖动一个小框,大致位于太空飞船的中心,如前一个截图所示。
-
点击确定。
现在我们回到精灵属性编辑器,可以看到碰撞检测现在显示为已修改。我们要做的最后一件事是将原点移动到太空飞船枪的尖端。这样做,我们就不必担心通过代码在创建时偏移子弹。
- 将原点设置为X:
28
,Y:24
,然后点击确定。
控制玩家对象
让我们创建玩家对象,并让它在世界中移动。
-
创建一个新对象,命名为
obj_Player
。 -
将
spr_Player
指定为其精灵。 -
我们需要初始化一个变量,用于控制玩家移动的速度。这样以后更改数值会更容易,并且
obj_Player
中的所有脚本都可以引用它。创建一个新的脚本,命名为scr_Player_Create
。
mySpeed = 8;
-
在
obj_Player
中,添加一个创建事件。 -
从控制中拖动一个执行脚本图标到操作:区域,并将
scr_Player_Create
应用到脚本选项。点击确定。 -
创建一个新的脚本,命名为
scr_Player_Key_Left
。这个脚本将包含左箭头键的代码。 -
虽然我们希望玩家能向左移动,但我们也希望防止玩家离开屏幕。将以下代码写入脚本:
if ( x >= sprite_width )
{
x -= mySpeed;
}
我们首先使用条件if
语句查询玩家当前的x
位置是否大于或等于精灵的宽度。在这种情况下,这意味着玩家的原点大于 48 像素的图像宽度。如果大于,我们将对象放在当前位置的左侧八个像素处。
我们在这里使用的移动方法不是传统意义上的移动。对象没有施加速度,而是我们将对象从一个位置瞬间传送到另一个位置。使用这种方法的好处是,如果没有按键,对象就不会移动。这在这个游戏中是必要的,因为我们不能使用无按键事件来射击武器。
-
在
obj_Player
中,在键盘下添加一个左事件。 -
从控制中拖动一个执行脚本图标到操作:区域,并将 scr
_Player_Key_Left
应用到脚本选项中。点击确定。
在继续处理所有其他键及其脚本之前,最好检查对象是否按预期工作。
-
创建一个新的房间。
-
在设置选项卡中,将名称更改为
TheGame
,宽度更改为800
。使房间变宽将为玩家提供更多的操纵空间,并更容易识别敌人。 -
在对象选项卡中,选择
obj_Player
并在房间中心附近放置一个单个实例,如下截屏所示: -
运行游戏。
如果一切设置正确,玩家应该只在按下左箭头时向左移动,并且应该保持在游戏区域内。现在我们可以继续处理其他控制。
-
创建一个新的脚本,并将其命名为
scr_Player_Key_Right
。这将用于右箭头键。 -
脚本将类似于左侧,只是我们还需要考虑房间的宽度。编写以下代码:
if (x <= room_width - sprite_width)
{
x += mySpeed;
}
在这里,我们正在测试玩家当前的x
位置是否小于房间宽度减去精灵的宽度。如果小于这个值,我们将mySpeed
添加到当前位置。这将确保玩家在向右移动时保持在屏幕上。
-
在
obj_Player
中,在键盘下添加一个右事件。 -
从控制中拖动一个执行脚本图标到操作:区域,并应用
scr_Player_Key_Right
。点击确定。
我们现在有了水平控制,并且需要添加垂直移动。我们将介绍上键和下键脚本的代码,但现在您应该能够将它们实现到对象中。
- 对于上箭头键,创建一个新的脚本,并将其命名为
scr_Player_Key_Up
,并编写以下代码:
if (y >= sprite_height)
{
y -= mySpeed;
}
这与水平代码类似,只是现在我们要考虑y
位置和精灵的高度。
- 对于下箭头键,创建一个新的脚本,并将其命名为
scr_Player_Key_Down
,并编写以下代码:
if (y <= room_height - sprite_height)
{
y += mySpeed;
}
同样,在这里,我们要考虑的是房间的高度减去精灵的高度作为我们可以向下移动的最远点。移动控制现在已经完成,对象属性应该如下截屏所示:
- 运行游戏。
玩家应该能够在整个屏幕上移动,但永远不会离开屏幕。我们剩下的唯一控制是开枪的按钮。然而,在实现这一点之前,我们需要一颗子弹!
创建子弹
制作子弹很容易,因为它们通常一旦被发射就沿着直线移动。
-
创建一个新的精灵,并将其命名为
spr_Bullet_Player
。 -
点击加载精灵,加载
Chapter 3/Sprites /Bullet_Player.gif
。 -
由于我们当前将玩家对象的原点设置为枪口,我们希望子弹的原点在前面。这将有助于使子弹看起来是从枪口射出,而无需直接编码。将值设置为X:
17
,Y:4
。 -
其他所有内容保持不变,然后点击确定。
-
子弹发射时也应该发出声音,所以让我们加入一个声音。我们需要切换回传统声音引擎,以确保在所有浏览器中都能听到音频。导航到资源 | 更改全局游戏设置,在常规选项卡下,取消使用新音频引擎的复选框。
-
创建一个新的声音,并将其命名为
snd_Bullet_Player
。 -
点击加载声音,加载
Chapter 3/Sounds/Bullet_Player.wav
。 -
确保类型设置为普通声音。然后点击确定。
-
现在是时候让子弹自行移动了。创建一个新的脚本,并将其命名为
scr_Bullet_Player_Create
。 -
我们希望子弹向右水平移动。使用以下代码很容易实现:
hspeed = 16;
sound_play(snd_Bullet_01);
Hspeed是 GameMaker: Studio 中表示对象水平速度的属性。我们需要在子弹实例化到世界中的那一刻应用这段代码。我们还会播放子弹的声音一次。
-
创建一个新对象,命名为
obj_Bullet_Player
,并将精灵设置为spr_Bullet_Player
。 -
添加一个Create事件。Create事件只在创建时执行一次。
-
应用
scr_Bullet_Player_Create
并点击OK。
如前面的截图所示,子弹现在已经完成,准备好发射。让我们回到太空船!
发射子弹
子弹只有在被发射后才对敌人构成威胁。玩家飞船将处理这段代码。
-
创建一个新的脚本,命名为
scr_Player_KeyPress_Space
。 -
写下以下代码:
instance_create(x, y, obj_Bullet_Player);
通过这段代码,我们只是在玩家飞船当前位置,或者更具体地说,玩家飞船精灵的原点处创建一个子弹实例。这将使子弹看起来是从飞船的枪中射出的。
-
在
obj_Player
中,添加一个Space事件从Key Press并应用scr_Player_KeyPress_Space
。Key Press事件检查指定的键是否被按下。这将运行一次,并需要释放键才能再次运行。 -
运行游戏。
如果一切正常,我们应该能够在屏幕上四处移动并尽可能快地射击子弹,如下图所示。我们几乎可以开始添加游戏玩法了,但在这之前,我们还有一点清理工作要做。
注意
如果一切看起来正确,但仍然无法看到预期的结果,请尝试刷新您的浏览器。偶尔,浏览器会将游戏保存在内存中,并不会立即加载更新的版本。
从世界中移除子弹
每次创建一个对象实例,都需要将其放入内存,并且计算机需要跟踪它。我们有所有这些子弹离开屏幕再也看不到了,但计算机看到了。这意味着随着时间的推移,计算机可能会试图观察数百万个浪费的子弹,这反过来意味着游戏会开始变慢。由于我们不希望发生这种情况,我们需要摆脱所有这些离开屏幕的子弹。
-
创建一个新的脚本,命名为
scr_OffScreenRemoval
。这个脚本可以应用于游戏中任何离开屏幕并且我们想要摆脱的对象。 -
要从世界中移除一个实例,写下以下代码:
instance_destroy();
- 在
obj_Bullet_Player
中,添加一个Outside Room事件从Other并应用脚本。Outside Room事件是一个特殊事件,检查实例化对象的整个精灵是否完全在房间外。
好了!现在我们有一个在屏幕上移动、射击子弹并且内存使用率低的太空船。让我们制作一些敌人!
构建三个小敌人
在这个游戏中,我们将有三种独特类型的敌人供玩家对抗:FloatBot,SpaceMine 和 Strafer。这些敌人每个都会以不同的方式移动并具有独特的攻击。然而,它们也有一些共同的元素,比如它们都会与子弹和玩家发生碰撞,但彼此之间不会发生碰撞。
考虑各种对象的共同点总是有用的,因为可能有简化和减少所需工作量的方法。在这种情况下,由于我们正在处理碰撞,我们可以使用一个父对象。
制作敌人父对象
父对象是 GameMaker: Studio 中非常有用的功能。它允许一个对象,父对象,将其属性传递给其他对象,称为子对象,通常被称为继承。最好的理解这种关系的方式是,父对象是一个群体,子对象是个体。这意味着我们可以告诉一个群体做某事,每个个体都会去做。
我们将创建一个父对象,并将其用于所有常见的碰撞事件。这样我们就不必为每个不同的敌人应用新的碰撞事件。
-
创建一个新对象,命名为
obj_Enemy_Parent
。我们不需要为这个对象添加精灵,因为它在游戏中永远不会被看到。 -
创建一个新脚本,命名为
scr_Enemy_Collision_Player
。 -
编写以下代码:
with (other)
{
instance_destroy();
}
instance_destroy();
在这里,我们使用了一个with
语句,它允许我们对另一个对象应用代码。在这种情况下,我们还可以使用一个特殊的变量叫做other
,它只在碰撞事件中可用。这是因为总是涉及两个实例,两者之间只有一个碰撞。谁拥有代码被标识为self
,然后是另一个。当obj_Enemy_Parent
或其任何子对象与obj_Player
发生碰撞时,我们将移除玩家,然后移除它碰撞的实例。
- 在
obj_Enemy_Parent
中,从碰撞中添加一个obj_Player
事件,并应用此碰撞脚本。
玩家碰撞现在可以工作了,但是当子弹碰撞时目前什么也不会发生。如果所有实例都将被移除,我们可以使用相同的脚本。在这种情况下,如果敌人被玩家子弹击中,我们希望做一些不同的事情。我们想要奖励分数。
-
与其创建一个新脚本,不如直接复制我们刚刚创建的碰撞脚本。在资源树中,右键单击
scr_Enemy_Collision_Player
,然后选择复制。 -
将此脚本命名为
scr_Enemy_Collision_Bullet
,并在脚本顶部添加以下代码行:
score += 20;
这将为游戏的总分数增加 20 分。为了确保一切设置正确,此脚本的整个代码应该如下所示:
score += 20;
with (other)
{
instance_destroy();
}
instance_destroy();
- 在
obj_Enemy_Parent
中,从碰撞中添加一个obj_Bullet
事件,并应用scr_Enemy_Collision_Bullet
。当敌人与子弹碰撞时,敌人现在将被摧毁并奖励分数!
我们需要父对象监视的最后一个事件是,如果敌人离开屏幕,将其移除。我们不能使用与我们的子弹清理脚本相同的脚本,因为我们将在屏幕右侧生成敌人。因此,我们需要确保它们只在离开左侧时被移除。
-
创建一个新脚本,命名为
scr_Enemy_Removal
。 -
编写以下代码:
if (x < 0)
{
instance_destroy();
}
首先,我们检查实例的x
位置是否小于0
,或者在屏幕左侧。如果是,我们将其从游戏中移除。
- 在
obj_Enemy_Parent
中,从其他中添加一个外部房间事件,并应用此脚本。我们已经完成了父对象,它应该看起来像下面的截图:
现在我们有了一个父对象,它将处理子弹碰撞并在敌人离开屏幕时移除它们。让我们通过创建一些子对象来测试它。
构建 FloatBot
FloatBot 是游戏中最基本的敌人。它不会发射武器,这使它更像是要避开的障碍物。FloatBot 将横穿屏幕向左移动,同时上下浮动。
-
创建一个新精灵,命名为
spr_FloatBot
。 -
加载精灵
Chapter 3/Sprites/FloatBot.gif
,勾选删除背景。 -
这是一个动画精灵,每一帧形状都会改变。因此,我们希望确保碰撞相应地改变。在碰撞检查中,勾选精确碰撞检查。
-
我们希望将原点设置在此精灵的中心,这样当我们添加摆动运动时,它就会正确移动。将原点设置为X:
16
,Y:16
。然后单击确定。
我们需要两个脚本来使 FloatBot 以我们想要的方式飞行。在创建时,我们将应用水平移动,然后在每一步之后我们将调整垂直摆动运动。
-
创建一个新的脚本,并将其命名为
scr_FloatBot_Create
。 -
编写以下代码:
hspeed = -4;
angle = 0;
水平速度的负值意味着它将向左移动。angle
是我们将在下一个脚本中使用的变量,用于摆动运动。
-
创建一个新脚本,并将其命名为
scr_FloatBot_Step
。 -
为了获得我们想要的垂直运动,我们将使用一些简单的三角学。编写以下代码:
vspeed = sin(angle) * 8;
angle += 0.1
在这里,我们根据变量角的正弦值(以弧度为单位)乘以基本速度8
来改变垂直速度。我们还每一步增加angle
的值,这是必要的,以便它遵循正弦波。
-
创建一个新对象,命名为
obj_FloatBot
,并将spr_FloatBot
设置为精灵。 -
我们希望将此对象设置为子对象,因此在父对象下拉框中,选择
obj_Enemy Parent
。 -
添加一个创建事件并应用
scr_FloatBot_Create
脚本。 -
添加一个步骤事件并应用
scr_FloatBot_Step
脚本。FloatBot 现在已经准备好测试,应该看起来像下面的截图: -
重新打开房间
TheGame
,并在屏幕右侧的某个地方放置一个obj_FloatBot
的实例。 -
运行游戏。
如果一切正常,FloatBot 应该沿着屏幕向左移动,并在大约 240 像素的高度上上下摆动,模式与下一个截图中显示的类似。如果我们用子弹击中 FloatBot,子弹和 FloatBot 都将消失。我们还成功创建了父子关系。让我们再创建一个!
创建 SpaceMine
SpaceMine 将是一个缓慢移动的对象,如果玩家靠近,它将发射一圈子弹。由于这将需要两个对象,我们应该始终从最简单的对象开始,即子弹。
-
创建一个新精灵,命名为
spr_Bullet_SpaceMine
。加载Chapter 3/Sprites/Bullet_SpaceMine.gif
,勾选删除背景。 -
将原点居中。我们不需要改变碰撞检查,因为正方形对于这个对象来说效果很好。
-
创建一个新对象,命名为
obj_Bullet_SpaceMine
,并将精灵设置为spr_Bullet_SpaceMine
。 -
创建一个新脚本,并将其命名为
scr_Bullet_SpaceMine_Create
。 -
这次我们希望使用
speed
和direction
的实例属性,因为我们稍后需要设置方向。编写以下代码:
speed = 16;
direction = 180;
-
在
obj_Bullet_SpaceMine
中,添加一个创建事件并应用此脚本。 -
我们需要为子弹添加碰撞,为了快速完成这个过程,我们可以重用
scr_Enemy_Collision_Player
脚本。从碰撞中添加一个obj_Player
事件并应用脚本。目前我们已经完成了子弹,如下截图所示: -
是时候建立 SpaceMine 本身了。创建一个新精灵,命名为
spr_SpaceMine
,并加载Chapter 3/Sprites/SpaceMine.gif
,勾选删除背景。正如你所看到的,SpaceMine 有动画闪烁的灯光。 -
将原点居中并检查精确碰撞检查。
-
当 SpaceMine 发射时,我们希望有射击声音,因此创建一个新声音,
snd_Bullet_SpaceMine
,并加载Chapter 3/Sounds/Bullet_SpaceMine.wav
。我们不会将其附加到子弹本身,因为我们将创建八颗子弹,但我们只需要播放一次声音。 -
如果尚未设置,将类型设置为普通声音,然后单击确定。创建一个新对象,命名为
obj_SpaceMine
。 -
将精灵设置为
spr_SpaceMine
,父对象设置为obj_Enemy_Parent
。 -
创建一个新脚本,并将其命名为
scr_SpaceMine_Create
。
我们需要 SpaceMine 做一些事情。它将发射子弹,所以我们需要一个变量来控制何时射击。它需要在屏幕上移动,所以我们需要应用速度。最后,我们希望减慢动画的速度,以免闪烁太快。
- 写下以下代码:
hspeed = -2;
canFire = false;
image_speed = 0.2;
首先,我们将水平速度设置为向左缓慢移动。canFire
是一个布尔变量,将决定是否射击。最后,image_speed
设置了动画的速度。以0.2
的速度,它以正常速度的 20%进行动画,换句话说,每一帧动画将保持五个步骤。
-
在
obj_SpaceMine
中,添加一个Create事件并应用这个脚本。 -
创建另一个新的脚本,命名为
scr_SpaceMine_Step
。
每一步,我们都希望查看玩家是否在 SpaceMine 的附近。如果玩家离得太近,SpaceMine 将开始发射子弹环。我们不希望有一串子弹,所以我们需要在每次射击之间添加延迟。
- 写下以下代码:
if ( distance_to_object( obj_Player ) <= 200 && canFire == false )
{
alarm[0] = 60;
sound_play(snd_Bullet_SpaceMine)
for (i = 0; i < 8; i += 1)
{
bullet = instance_create(x,y,obj_Bullet_SpaceMine);
bullet.direction = 45 * i;
bullet.hspeed -= 2;
}
canFire = true;
}
我们首先检查两个语句;SpaceMine 和obj_Player
之间的距离,以及我们是否能够射击。我们选择的距离是200
像素,这应该足够让玩家偶尔避免触发它。如果玩家在范围内并且我们能够射击,我们将alarm
设置为60
步(2 秒),并播放一次子弹声音。
注意
警报是一个事件,当触发时,将执行一次代码。
为了创建子弹环,我们将使用一个for
循环。当我们创建一个对象的实例时,它会返回该实例的唯一 ID。我们需要将这个 ID 捕获在一个变量中,这样我们才能与对象交互并影响它。在这里,我们使用一个名为bullet
的变量,它是obj_Bullet_SpaceMine
的一个实例。然后我们可以改变子弹的属性,比如方向。在这种情况下,每颗子弹的偏移角度为 45 度。我们还给子弹添加了一些额外的hspeed
,这样它们就可以跟随 SpaceMine 移动。最后,我们将canFire
变量设置为true
,表示我们已经发射了子弹。
-
在
obj_SpaceMine
中,添加一个Step事件并应用这个脚本。 -
我们几乎完成了 SpaceMine,我们只需要在一个可以触发的警报中添加一些代码,这样它就可以多次射击。创建一个新的脚本,命名为
scr_SpaceMine_Alarm0
。 -
将
canFire
变量设置回false
:
canFire = false;
-
在
obj_SpaceMine
中,添加一个Alarm 0事件并应用这个脚本。现在我们已经完成了 SpaceMine,它应该看起来像下面的截图: -
打开
TheGame
,在屏幕的右侧添加一个obj_SpaceMine
的实例,然后运行游戏。
如果一切设置正确,SpaceMine 将缓慢地向左移动并闪烁。当玩家靠近 SpaceMine 时,应该会有八颗子弹从中射出,就像下一个截图中所示。每两秒,这个实例将发射另一个子弹环,只要玩家仍然在范围内。如果 SpaceMine 被玩家的子弹击中,它将被摧毁。最后,如果玩家与敌人的子弹相撞,玩家就会消失。让我们继续我们的最终敌人!
制作 Strafer
Strafer 是游戏中最危险的敌人。它以直线非常快速移动,并且会瞄准玩家无论他们在哪里。再次,我们需要两个对象,所以让我们从子弹开始。
-
创建一个新的精灵,命名为
spr_Bullet_Strafer
。加载Chapter 3/Sprites/Bullet_Strafer.gif
,并勾选Remove Background。 -
将原点居中。
-
创建一个新的对象,命名为
obj_Bullet_Strafer
,并将精灵设置为spr_Bullet_Strafer
。 -
我们想要一个独特的射击声音,所以创建一个新的声音,
snd_Bullet_Strafer
,并加载Chapter 3/Sounds/Bullet_Strafer.wav
。 -
如果尚未将种类设置为普通声音,请点击确定。
-
创建一个新的脚本,并将其命名为
scr_Bullet_Strafer_Create
。 -
这个脚本与
scr_Bullet_SpaceMine_Create
类似,只是这颗子弹速度更快,并播放子弹声音。编写以下代码:
speed = 20;
direction = 180;
sound_play(snd_Bullet_Strafer);
-
在
obj_Bullet_Strafer
中,添加一个创建事件,并应用此脚本。 -
与其他敌人子弹一样,让我们通过重用
scr_Enemy_Collision_Player
脚本为子弹添加碰撞。从碰撞中添加一个obj_Player
事件,并应用该脚本。子弹部分完成后,让我们构建敌人。 -
创建一个新的精灵,并将其命名为
spr_Strafer
,并加载Chapter 3/Sprites/Strafer.gif
,勾选删除背景。 -
我们希望子弹从飞船的前方发射,因此我们需要手动将原点移动到正确的位置。将原点设置为X:
0
,Y:19
。 -
创建一个新的对象,并将其命名为
obj_Strafer
。 -
将精灵设置为
spr_Strafer
,父对象设置为obj_Enemy_Parent
。 -
创建一个新的脚本,并将其命名为
scr_Strafer_Create
。 -
Strafer 将快速在屏幕上移动并不断向玩家发射子弹。编写以下代码:
hspeed = -10;
alarm[0] = 10;
与 SpaceMine 类似,我们将hspeed
设置为向左移动,并设置一个警报,以便 Strafer 立即开始射击。
-
在
obj_Strafer
中,添加一个创建事件,并应用此脚本。 -
我们只需要再创建一个脚本,那就是用于警报的脚本。创建一个新的脚本,并将其命名为
scr_Strafer_Alarm0
。 -
当警报响起时,我们需要创建一个子弹,将其发射到玩家,并重置警报,以便它可以再次发射。编写以下代码:
bullet = instance_create(x, y, obj_Bullet_Strafer);
if (instance_exists(obj_Player))
{
bullet.direction = point_direction(x,y, obj_Player.x,obj_Player.x, obj_Player.y);
}
alarm[0] = irandom(30) + 15;
我们首先创建obj_Bullet_Strafer
的一个实例。当创建一个实例时,该函数会返回该实例的唯一 ID;然后我们将其捕获在一个变量中,比如bullet
。接下来,我们查询玩家是否存在。这是一个非常重要的步骤,因为如果没有这个检查,如果玩家死亡并且 Strafer 试图瞄准它,游戏将出错并崩溃。
如果玩家存在,我们将设置子弹的方向指向玩家。这是通过point_direction
函数完成的,该函数接受空间中的任意两点(x1,y1)和(x2,y2),并返回角度(以度为单位)。
最后,我们重置警报。在这种情况下,为了增加趣味性,我们添加了一些随机性。irandom
函数将返回一个介于零和传递给它的数字之间的整数。我们这里的代码将给我们一个介于0
和30
之间的随机值,然后我们将其加上15
。这意味着每隔半秒到一秒半之间将创建一个新的子弹。
-
在
obj_Strafer
中,添加一个Alarm 0事件,并应用此脚本。 -
Strafer 现在已经完成,让我们测试一下,并将其放置在
TheGame
的左侧。
如果一切正常,Strafer 将快速横穿屏幕,并直接朝向玩家位置发射子弹。确保您将玩家移动到房间的各个方向,以确保它可以朝各个方向射击!玩家应该能够射击并摧毁 Strafer。如果被 Strafer 的子弹击中,玩家应该消失。
游戏的敌人都已经完成;现在我们只需要一种方法来填充游戏世界。让我们引入一个 Overlord!
通过 Overlord 控制游戏
在这个游戏中,我们将使用 Overlord,游戏的主控制器,来控制敌人的生成,监视玩家的生命,并处理胜利/失败条件。胜利条件很简单,就是在两分钟内生存下来,抵御敌人的波浪。失败条件是玩家耗尽生命。
生成敌人的波浪
我们需要首先创建敌人的波动,以便游戏可玩。为此,我们将利用循环时间线来生成各种敌人。我们将有三个不同的波动,每两秒生成一个不同的敌人。
-
创建三个新脚本,并命名为:
scr_Wave_Strafer
,scr_Wave_SpaceMine
和scr_Wave_FloatBot
。 -
我们将从 Strafer 的波动开始,因为它将是最简单的波动。在
scr_Wave_Strafer
中编写以下代码:
instance_create(room_width - 64, room_height/2 - 64, obj_Strafer);
instance_create(room_width - 64, room_height/2 + 64, obj_Strafer);
在这里,我们生成两个 Strafer 的实例,位于屏幕右侧64
像素处。这将确保我们看不到它们突然出现。我们还将它们偏移了64
像素,使其与房间的垂直中心相差64
像素。
- 对于 SpaceMine,我们希望它们在随机位置垂直放置,以保持事情的趣味性。在
scr_Wave_SpaceMine
中编写以下代码:
placeY = irandom_range(64, room_height - 64);
instance_create(room_width - 64, placeY, obj_SpaceMine);
我们创建一个名为placeY
的变量来保存垂直位置的值。GameMaker: Studio 有一个特殊的函数irandom_range
,它将返回传递给它的两个数字之间的整数。我们使用的数字将确保 SpaceMine 距离屏幕顶部和底部至少 64 像素。然后我们在创建实例时使用placeY
变量。
- FloatBot 将使用类似的垂直轴放置设置,但我们希望有三个实例以“V”形式飞行。在
scr_Wave_FloatBot
中编写以下代码:
placeY = irandom_range(80, room_height - 80);
instance_create(room_width - 32, placeY, obj_FloatBot);
instance_create(room_width - 64,placeY - 32, obj_FloatBot);
instance_create(room_width - 64, placeY + 32, obj_FloatBot);
在这里,我们再次使用placeY
变量,但数字范围更窄。我们需要一些额外的填充,以便所有三个飞机都保持在屏幕上。创建的第一个实例是编队的前部单位。接下来的两个实例在第一个实例的后面生成,偏移了 32 像素,并分别在第一个实例的上方和下方偏移了 32 像素。
-
所有波动都已编写脚本,因此我们现在可以在时间线中实现它们。首次实现时间线时,保持数字简单是有用的,例如相隔两秒。适当平衡时间是在游戏开发的打磨阶段进行的,花费太多时间试图在所有内容都在位之前就把这个问题解决好,很可能是浪费时间。创建一个新的时间线,命名为
tm_Wave_Spawning
。 -
点击Add,将Indicate the Moment设置为
60
,并应用scr_Wave_FloatBot
脚本。这将在游戏中生成第一个敌人,持续两秒。 -
两秒后我们将添加 SpaceMines。点击Add,将Indicate the Moment设置为
120
,并应用scr_Wave_SpaceMine
脚本。 -
最后,六秒后我们将带入 Strafer。点击Add,将Indicate the Moment设置为
180
,并应用scr_Wave_Strafer
脚本。时间线现在已准备好使用,并且应该如下截图所示:
构建 Overlord
我们现在准备开始构建 Overlord 并应用我们的生成系统。
-
创建一个新对象,命名为
obj_Overlord
。 -
不需要精灵,所以将Sprite设置为no sprite。
-
我们将在创建 Overlord 时立即开始时间线。创建一个新脚本,命名为
scr_Overlord_Create
,并编写以下代码:
timeline_index = tm_Wave_Spawning;
timeline_running = true;
timeline_loop = true;
代码的第一行定义了我们要运行的时间线,我们只有一个:tm_Wave_Spawning
。接下来,我们启动时间线,然后告诉它循环。这最后两个是布尔变量,这意味着它们只能打开和关闭。
-
在 Overlord 中,添加一个Create事件并应用此脚本。
-
打开TheGame,并在房间中放置一个 Overlord 的实例。位置无关紧要,但左上角是一个常见的放置位置。
-
删除房间中剩余的敌人实例。如下截图所示,房间中应该只有一个 Player 的实例和一个 Overlord 的实例:
-
运行游戏。
游戏现在有敌人!第一个敌人 FloatBots 出现需要几秒钟,但之后敌人将会不断生成。到目前为止,我们已经实现了大部分核心游戏玩法,如下所示:
-
我们可以在屏幕上移动玩家,但不能移出屏幕
-
我们可以射击和摧毁敌人
-
敌人可以射击和摧毁玩家
-
敌人将不断生成
在这个阶段玩游戏时,唯一剩下的元素非常明显;玩家可以死亡,但游戏不会停止。我们需要实现胜利/失败条件。
处理玩家的生死
由于这是一个关于生存的游戏,我们希望胜利/失败条件相对简单。对于胜利条件,我们将使玩家生存一段时间。失败条件是玩家死亡,但我们不希望游戏太难玩,所以我们给玩家三条生命。这意味着我们需要重新生成玩家。最后,为了使这个功能正常工作,我们需要给 Overlord 一些额外的职责。
设置胜利条件
这个游戏的胜利条件是生存一段时间。我们可以通过使用警报和一个变量来实现这一点,向 Overlord 发出信号,玩家已经生存下来。
- 我们需要为生命、胜利和失败条件设置一些变量。重新打开
scr_Overlord_Create
,在底部添加以下代码:
lives = 3;
isVictory = false;
isDefeat = false;
GameMaker: Studio 有一些内置的全局变量,包括lives
。这个变量可以被游戏中的每个实例访问,永远不会消失。在这里,我们将其设置为3
,并将其用作我们的起点。我们还创建了另外两个变量,isVictory
和isDefeat
,我们将其设置为false
。我们之所以使用两个变量来表示游戏的胜利和失败,而不是一个,是因为我们希望在游戏过程中检查它们,当他们既没有赢也没有输时。
- 我们还可以通过在这个脚本中设置一个 90 秒的警报来设置我们的胜利条件。为此,在步骤 1 的代码之后添加以下代码行:
alarm[0] = 2700;
scr_Overlord_Create
脚本现在应该总共如下所示:
timeline_index = tm_Wave_Spawning;
timeline_running = true;
timeline_loop = true;
lives = 3;
isVictory = false;
isDefeat = false;
alarm[0] = 2700;
- 接下来,我们需要为胜利条件的警报事件设置一个脚本。创建一个新脚本,命名为
scr_Overlord_Victory
,并编写以下代码:
timeline_running = false;
with ( obj_Enemy_Parent )
{
instance_destroy();
}
alarm[1] = 90;
isVictory = true;
我们要做的第一件事是停止时间线,因为我们不希望再生成更多的敌人。下一步是移除游戏中仍然存活的所有敌人。我们通过使用with
语句来执行obj_Enemy_Parent
的所有实例的代码来实现这一点。因为所有的敌人都是这个对象的子对象,它们也会被销毁。最后,我们为三秒钟设置另一个警报。最后,我们将isVictory
变量设置为 true。
-
在
obj_Overlord
中,添加一个Alarm 0事件并应用胜利脚本。 -
让我们通过创建重新启动脚本来结束这一切。创建一个新脚本,命名为
scr_Overlord_GameRestart
,并编写以下代码:
game_restart();
- 添加一个Alarm 1事件并应用重新启动脚本。现在胜利条件已经生效,随时可以尝试。
使用 Ghost 对象重新生成
现在我们可以转向失败条件和重新生成。当玩家死亡时,我们不希望玩家立即重新生成,而是有一个较短的无敌时间。为此,我们需要创建一个 Ghost 对象,暂时代替玩家。
-
创建一个新精灵,命名为
spr_Ghost
,并加载Chapter 3/Sprites/Ghost.gif
,勾选删除背景。它看起来就像飞机,但是略微透明,在动画时会闪烁。 -
我们需要将原点设置为与
spr_Player
的原点完全相同。将原点设置为X:43
,Y:22
,然后点击确定。 -
创建一个新对象,命名为
obj_Ghost
,并将spr_Ghost
应用为精灵。 -
当玩家死亡时,我们将让 Ghost 出现在屏幕左侧并移入游戏区域。创建一个新的脚本,命名为
scr_Ghost_Create
,并编写以下代码:
x = -64;
y = room_height * 0.5;
hspeed = 4;
我们首先将x
坐标设置为屏幕外64
像素。然后通过将y
坐标设置为房间高度的一半来垂直居中 Ghost。最后,我们对 Ghost 施加正向速度,使其开始自行移动。
-
为
obj_Ghost
添加一个Create事件并应用此脚本。 -
Ghost 将在屏幕上移动,我们需要在某个时候将其转换为玩家。在我们的情况下,一旦 Ghost 通过了游戏区域的四分之一,我们将进行切换。创建一个新的脚本,命名为
scr_Ghost_Step
,并编写以下代码:
if ( x >= 200 )
{
hspeed = 0;
instance_change(obj_Player, true);
}
在这里,我们检查 Ghost 的x
坐标是否已经越过了200
像素。如果是,我们停止向前的速度,然后转换为玩家。instance_change
函数需要两个参数:要转换为的对象以及是否要运行此新对象的Create事件。
-
为
obj_Ghost
添加一个Step事件并应用此脚本。 -
我们将在这种设置中遇到一个问题,那就是玩家无法控制 Ghost,并且在变换时可能会出现在靠近敌人的危险位置。我们不希望出现这种情况,所以让我们给玩家一些有限的控制权。我们可以重用现有的
scr_Player_Key_Up
和scr_Player_Key_Down
脚本,以便玩家具有垂直移动。添加适当的键盘事件并附加这些脚本。
Ghost 对象的属性应该如下截图所示,现在已经准备好成为游戏的一部分。我们只需要改变玩家被击中时发生的事情。
-
重新打开
scr_Enemy_Collision_Player
。 -
目前,我们正在销毁子弹和玩家。我们需要更改
with
语句以允许重新生成。删除第3行:
instance_destroy();
并替换为:
if ( lives > 0 )
{
instance_change(obj_Ghost, true);
}
else
{
instance_destroy();
}
lives -= 1;
我们只想在有生命可用时变成 Ghost,因此我们首先要检查这一点。如果我们至少有一条生命,我们就将玩家变成 Ghost。否则,我们只是销毁玩家,玩家将永远死亡。最后,无论我们是否有生命,每次都要减少一条生命。最终的代码应该如下所示:
with ( other )
{
if ( lives > 0 )
{
instance_change(obj_Ghost, true);
}
else
{
instance_destroy();
}
lives -= 1;
}
instance_destroy();
此时我们可以玩游戏。请注意,当玩家死亡时:
-
玩家消失
-
创建一个 Ghost 并移入游戏区域
-
Ghost 可以上下移动
-
Ghost 变回 Player
当然,这会发生三次,然后玩家永远消失。然而,游戏的其余部分正在继续,就好像什么都没有发生。我们需要添加失败条件。
- 创建一个新的脚本,
scr_Overlord_Step
,并编写以下代码:
if ( lives < 0 && isDefeat == false ) {
alarm[1] = 90;
isDefeat = true;
}
这段代码中的每一步都会检查玩家是否还有生命。如果玩家没有生命了,而变量isDefeat
仍然为false
,它将为重新开始游戏警报设置三秒。最后,我们将isDefeat
变量设置为true
,这样我们就不会再运行这段代码了。
- 在
obj_Overlord
中,添加一个Step事件并应用此脚本。玩家死亡三次后游戏将重新开始。
游戏的核心机制现在已经完成,但对于玩家来说,发生了什么并不是很清楚。玩家可以死亡并重新生成几次,但没有显示剩余生命的指示。也没有显示玩家是赢了还是输了。让我们来解决这个问题!
绘制用户界面
创建一个伟大游戏的最重要元素之一是确保玩家拥有玩游戏所需的所有信息。其中很多通常显示在HUD中,也就是heads-up display。每个游戏都有不同的组件可以成为 HUD 的一部分,包括我们需要的记分牌和生命计数器等。
-
首先,我们需要一个用于显示文本的字体。我们提供了一个名为Retroheavyfuture的字体供本游戏使用,需要在您的计算机上安装。要在 Windows 7 计算机上安装此字体,请右键单击
Chapter 3/Fonts/RETRRG__.ttf
,然后单击安装。然后按照提示进行操作。 -
回到 GameMaker: Studio,创建一个新的字体,命名为
fnt_Scoreboard
。 -
选择Retroheavyfuture作为字体。
-
在样式下将大小设置为
16
。 -
我们需要一个适当大小的字体来显示游戏中的得分和生命。它应该看起来像下面的截图,所以点击确定:
-
当我们显示胜利/失败条件时,我们将需要字体的第二个版本。创建一个新的字体,命名为
fnt_WinLose
。 -
再次选择Retroheavyfuture作为字体,但这次将大小设置为
32
。现在我们已经拥有了所有游戏中需要的字体,所以点击确定。 -
让我们继续进行新的脚本
scr_Overlord_Draw
。我们将从以下代码开始设置记分牌文本的颜色和字体:
draw_set_color(c_white);
draw_set_font(fnt_Scoreboard);
第一行代码设置了一个 GameMaker: Studio 预设颜色c_white
。接下来的一行将记分牌设置为字体。
注意
设置颜色是全局应用于draw
事件的。这意味着如果您不设置颜色,它将使用上次设置的颜色,而不管对象如何。
- 设置字体后,我们可以开始应用 HUD。我们将从玩家生命开始。将以下代码添加到脚本中:
draw_set_halign(fa_left);
if ( lives >= 0 )
{
draw_text(8, 0, "Lives: " + string(lives));
} else {
draw_text(8, 0, "Lives: " );
}
为了确保文本格式正确,我们将文本的水平对齐设置为左对齐。文本本身需要是一个字符串,可以通过两种方式完成。首先,任何用引号括起来的内容都被视为字符串,比如"生命:"
。如果我们想传递一个数字,比如我们拥有的生命数量,我们需要通过字符串函数进行转换。如下所示,如果我们还有剩余的生命,我们可以将这两个东西连接起来创建一个句子“生命:3”,并将其绘制在屏幕的左上角。如果我们没有生命了,我们就绘制不带连接值的文本。
- 我们想要的另一个 HUD 元素是得分,我们将其放在屏幕的对面,即右上角。添加以下代码:
draw_set_halign(fa_right);
draw_text(room_width-8, 0, "SCORE: " + string(score));
与之前的文本一样,我们设置了水平对齐,这次是右对齐。然后使用相同的连接方法将文本放在正确的位置。
-
现在让我们通过向
obj_Overlord
添加绘制 GUI事件并应用此脚本来测试一下。 -
运行游戏。如下截图所示,游戏现在应该在左上角显示生命,并在玩家死亡时更新。它还应该在右上角显示得分,并随着每个敌人被杀而增加。
-
现在我们需要添加玩家赢或输时的显示。在
scr_Overlord_Draw
的末尾添加以下代码:
draw_set_font(fnt_WinLose);
draw_set_halign(fa_center);
if ( isVictory == true )
{
draw_text(room_width / 2, room_height/2, "VICTORY");
}
if ( isDefeat == true )
{
draw_text(room_width / 2, room_height/2, "DEFEAT");
}
我们将字体更改为fnt_WinLose
,并将水平对齐设置为居中。我们不希望文本一直显示,而是应该在适当时只显示VICTORY或DEFEAT。我们已经在 Overlord 中实现了游戏条件的代码,所以我们只需要在每一步检查isVictory
是否为true
或isDefeat
是否为true
。一旦游戏赢了或输了,我们就在房间的中心绘制适当的文本。
完整的scr_Overlord_Draw
脚本应该如下所示:
draw_set_color(c_white);
draw_set_font(fnt_Scoreboard);
draw_set_halign(fa_left);
draw_text(8, 0, "LIVES: " + string(lives));
draw_set_halign(fa_right);
draw_text(room_width-8, 0, "SCORE: " + string(score));
draw_set_font(fnt_WinLose);
draw_set_halign(fa_center);
if ( isVictory == true )
{
draw_text(room_width / 2, room_height/2, "VICTORY");
}
if ( isDefeat == true )
{
draw_text(room_width / 2, room_height/2, "DEFEAT");
}
为游戏添加完成细节
游戏现在在功能上已经完成,但它没有任何光泽或我们期望的完整游戏的完成细节。没有音乐,没有背景艺术,也没有爆炸!让我们立即解决这个问题。
添加游戏音乐
我们希望音乐从头开始播放,并在游戏持续时间内播放。当发生胜利/失败条件时,我们希望音乐渐渐消失,以让玩家知道游戏已经结束。
-
创建一个新的声音并命名为
snd_Music
。 -
加载
Chapter 3/Sounds/Music.mp3
。种类应设置为背景音乐。 -
重新打开
scr_Overlord_Create
。由于霸主控制整个游戏,我们将使用它来控制音乐。在最后一行代码之后,添加以下内容:
sound_play(snd_Music);
sound_loop(snd_Music);
volume = 1;
sound_global_volume(volume);
我们首先播放音乐并设置为循环。然后创建一个名为volume
的变量,我们将用它来控制音量和淡出。我们已将音量设置为1
,即最大音量。最后,我们将全局音量,或主增益级别,设置为变量volume
。
- 重新打开
scr_Overlord_Step
。为了淡出音乐,我们需要在几个步骤内降低全局音量,但只有在游戏结束时才这样做。在最后一行代码之后,添加以下内容:
if ( isDefeat == true || isVictory == true )
{
volume -= 0.02;
sound_global_volume(volume);
}
在这里,我们检查是否已将胜利或失败条件设置为true
。如果是,我们将通过0.02
减少音量变量并将其应用于主增益级别。声音级别从最大音量降至静音需要 50 步,大约是游戏重新开始之前的一半时间。
- 运行游戏。现在你应该听到背景音乐正在播放。如果玩家快速死亡三次并触发了失败条件,你应该听到声音渐渐消失。
使背景移动
这个游戏发生在外太空,所以我们需要添加一个太空背景。为了让游戏宇宙感觉玩家在移动,我们需要使背景不断向左移动。
-
创建一个新的背景并命名为
bg_Starscape
。 -
加载
Chapter 3/Backgrounds/Starscape.gif
,不勾选删除背景。这就是我们需要做的一切,所以点击确定。 -
打开
TheGame
并选择背景选项卡。 -
将
bg_Starscape
设置为背景图像。这应该会自动发生,但确保背景 0被突出显示,并且在房间开始时可见已被选中。 -
星空只会水平移动,因此我们只需要勾选水平平铺,以便图像环绕。
-
要移动背景,将水平速度设置为
-2
。这将使其向左移动,从而使玩家看起来向右移动。设置应如下截图所示: -
运行游戏。现在你应该看到一个移动的星空!查看以下截图:
创建爆炸
让敌人突然消失不仅看起来很糟糕,而且对玩家来说也不是很有意义。让我们通过添加一些爆炸效果来使游戏更加令人兴奋!
-
创建一个新的精灵,
spr_Explosion
,并加载Chapter 3/Sprites/Explosion.gif
,勾选删除背景。 -
将原点设置为中心,然后点击确定。
-
创建一个新的声音,
snd_Explosion
,并加载Chapter 3/Sounds/Explosion.wav
。 -
如果尚未设置种类为普通声音,请设置为普通声音,然后点击确定。
-
创建一个新的对象,
obj_Explosion
,并将精灵设置为spr_Explosion
。
我们希望爆炸发出声音,播放其动画,然后从游戏中移除自身。
- 创建一个新的脚本,
scr_Explosion_Create
,并编写以下代码以播放爆炸声音一次:
sound_play(snd_Explosion);
-
添加一个创建事件并应用此脚本。
-
要使爆炸自行消失,最好在动画完成时执行。幸运的是,GameMaker: Studio 有一个事件可以做到这一点。从其他中添加一个动画结束事件,然后创建一个名为
scr_Explosion_AnimEnd
的新脚本,并添加以下代码以删除实例:
instance_destroy();
- 爆炸现在已经准备好了,我们所要做的就是在摧毁敌人时生成它。打开
scr_Enemy_Collision_Bullet
,并在脚本的第一行添加以下代码:
instance_create(x,y, obj_Explosion);
这将在敌人所在的位置创建一个爆炸。这需要在我们将敌人从游戏中移除之前发生。
-
使用
scr_Enemy_Collision_Player
重复这段代码添加。 -
运行游戏。现在,每当有东西被摧毁时,你应该看到爆炸,就像下面的截图所示:
总结
恭喜!你刚刚完成了创建你的第一个横向卷轴射击游戏。在本章中,我们涵盖了相当多的内容。我们应用了移动的三种方法:手动调整 X 和 Y 坐标,使用hspeed
和vspeed
,以及设置speed
和direction
变量。我们现在能够动态地向游戏世界添加和移除实例。通过子弹,我们学会了将信息从一个实例传输到另一个实例,比如移动的方向,通过捕获实例的 ID 并通过点运算符访问它。
我们发现了美妙的with
语句,它使我们能够影响单个实例、对象的所有实例,甚至是碰撞中涉及的other
实例。我们研究了全局变量,比如lives
和score
,并使用Draw事件来显示它。敌人的波浪是使用时间轴生成的。通过滚动背景图像创建了移动的错觉。声音被应用,并调整音量以创建淡出效果。我们甚至使用了一点三角学!
有了本章中所学的技能和知识,现在轮到你来接管这个游戏,并进一步扩展它。尝试添加你自己的敌人、可收集物品和武器升级。玩得开心吧!
在下一章中,我们将通过制作一个恐怖冒险游戏,更多地了解碰撞和玩家控制。我们还将研究人工智能,并使用路径使敌人看起来像在自己思考和行动。