HTML5-游戏开发跨越手册-全-
HTML5 游戏开发跨越手册(全)
一、简介
“如果你有一个花园和一个图书馆,你就拥有了你需要的一切。”
马库斯·图利乌斯·西塞罗(公元前 106—公元前 43 年)
var replaceWord1 = str.replace("garden", "computer");
var replaceWord2 = str.replace("a library", "time");
我从 1996 年开始开发软件,至今已经为大大小小的公司开发游戏十多年了。像现实世界中任何形式的开发一样,在考虑编码策略和构建流程之前,您需要知道为什么要构建游戏。在游戏世界中,这以游戏故事的形式出现。这包括背景,玩的理由,游戏的目标。
介绍我们的游戏:太空僵尸
这是我们的故事,我们将把它发展成一个游戏。
你好。我的名字是 Ace Star。时间是 2107 年。在过去的三个月里,我一直在仙女座星系唯一的卫星 ZC636 上担任安全警卫。除了我和一群来自地球的政要,还有一群大约 500 名顶尖科学家驻扎在这里进行秘密实验。
我需要你帮我。
昨晚,一个实验室发生了爆炸。一种气体释放出来,把实验室里所有的科学家都变成了僵尸。
我已经在殖民地主楼唯一的门外。其他幸存者都在里面。在地球救援到来之前,我是最后一道防线。
我发现我们的武器对僵尸毫无用处。然而,在跑出实验室时,我发现了一种新的实验性武器。
它似乎有效果。
我能听到他们来了。你准备好了吗?
让我们来看看我们将用于开发的一些图形。
| ![A459093_1_En_1_Figb_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Figb_HTML.jpg) | 这是我们游戏的背景图片。它将通过水平和垂直拉伸来填充屏幕。我们的僵尸将从地面和天空的交汇处诞生。一旦产卵,它们就会向我们靠近,变得越来越大。 | | ![A459093_1_En_1_Figc_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Figc_HTML.jpg) | 向 Z 教授问好,我们的普通僵尸。就速度而言,他不是很快,也不会突然冲到前面。他只是以自己悠闲的速度向你走来!在我们的武器射击方面,他不会太难或太容易“中和”需要两次电击才能抓到他。 | | ![A459093_1_En_1_Figd_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Figd_HTML.jpg) | 向颠茄说再见,我们最快的僵尸。盯紧她因为她会出现一分钟然后突然冲刺到前面。然而,她不会太难被压制。一次电击就能干掉她。 | | ![A459093_1_En_1_Fige_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Fige_HTML.jpg) | 最后,这是布拉德,我们的重量级僵尸。不幸的是,由于长时间举重,他比一般的僵尸要慢。一旦产卵,它会慢慢加速。然而,他将更难被压制。需要三次电击才能抓到他。 | | ![A459093_1_En_1_Figf_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Figf_HTML.jpg) | 这是我们的英雄在一个实验室找到的实验武器。当发射时,它会喷出一种特殊的液体,如果使用成功,这种液体会将僵尸包裹在一个气泡中。它需要经常重装。 | | ![A459093_1_En_1_Figg_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Figg_HTML.jpg) | 这是我们的重新加载按钮。从游戏设计的角度来看,它为游戏增加了另一个维度。 | | ![A459093_1_En_1_Figh_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Figh_HTML.jpg) | 这是我们游戏的标志。我们将不会看到它,直到本书的最后一章,当我们嵌入我们的游戏。 | | ![A459093_1_En_1_Figi_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Figi_HTML.jpg) | 这是我们将嵌入到游戏中的盒子。最初,你会看到游戏横跨整个屏幕。然而,在接近尾声时,我们看到了将代码嵌入到这个盒子中。 | | ![A459093_1_En_1_Figj_HTML.jpg](https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/crossover-h5-game-dev/img/A459093_1_En_1_Figj_HTML.jpg) | 这是我们在最后一章使用的背景图片。背景是当我们建立一个专门的网页来嵌入我们的游戏时使用的主要图像。 |以下是游戏完成后的截图。
设置您的工作环境
本节讨论如何设置工作环境。
第一部分:设置我们的文件夹
您需要创建一个工作文件夹来存储您的所有工作文件。这使得将您的工作文件与计算机上的所有其他文件区分开来变得更加容易。所以首先,在 c 盘中创建一个名为My_Work_Files
的根(或主)文件夹。
一旦你有了根文件夹,下一步就是创建游戏需要的子文件夹。在My_Work_Files
中创建四个文件夹。将文件夹命名如下:
CSS
Images
Raw Images
js
你的文件夹应该看起来像下面的截图。
文件夹将保存特殊的代码文件,帮助构建游戏的设计。该文件夹中的所有文件将以.css
结尾。
文件夹将保存我们所有的 JavaScript 文件,这些文件将构成我们游戏的引擎。它们将包含控制我们游戏中发生的事情的命令和指令。该文件夹中的所有文件将以.js
结尾。
顾名思义,Images
文件夹将包含游戏所需的所有图像或媒体文件。
从技术上讲,Raw Images
文件夹不会用于原始图像。在我们的例子中,我们将使用这个文件夹作为我们所有媒体的临时专用空间。当我们需要它们时,我们会将它们移动到Images
文件夹中。
第二部分:设置我们的文件
出于本书的目的,我将使用记事本(如果你使用苹果电脑,那么我将使用文本编辑)。我发现记事本更简单,更容易使用;但是,几乎任何 IDE(集成开发环境)都可以用于这个项目。因此,请继续使用您最熟悉的 IDE。
如果您确实想使用 IDE,这里列出了一些免费使用的 IDE:
- 月食。这是一个开源编辑器,通常用于 C 和 C++(以及其他高级语言)项目。
- NetBeans。像 Eclipse 一样,这是一个开源编辑器;然而,它与过多的开发框架捆绑在一起。
- 阿普塔纳。这是一个在 web 开发人员中非常流行的 IDE,它可以插入 Eclipse。通常用于 HTML 项目。
- 代码运行。这是一个有点不寻常的选择,因为它运行在浏览器上(即,它是一个基于 web 的 IDE)。就我个人而言,我发现它非常适合在远程位置进行最后的修复。
- Visual Studio 社区。这对个人程序员来说是免费的,它包含了 Visual Studio Professional 系列中所有令人惊叹的特性。
虽然使用 IDE 有它的好处,但我认为值得记住这句关于使用 IDE 处理多种语言的名言:“尽管许多 IDE 可以处理多种语言,但很少有人做得很好。此外,如果你才刚刚开始,这可能有些过头了。”
现在文件夹已经设置好了,让我们来创建您将用来开发游戏的文件。
首先,您需要创建一个default.html
文件。如果使用的是 IDE,请单击“文件”“➤”“新建”,然后选择“HTML”。如果您正在使用记事本,请打开一个新文件,并将其另存为default.html
。
您的文件夹现在应该看起来像这样:
现在,我们需要在我们创建的一些文件夹中创建文件。双击js
文件夹。重复前面的步骤(即创建一个新文件,然后另存为)。以下是要输入的文件名:
> SZ_main.js
> SZ_movement.js
> SZ_setupContent.js
> SZ_SS.js
> SZ_touch.js
> SZ_zombie_movement.js
您的js
文件夹现在应该是这样的:
最后,我们需要在CSS
文件夹中创建一个文件。重复前面的步骤(即创建一个新文件,然后另存为)。要输入的文件名是
> SZ_master.css.
您的CSS
文件夹现在应该是这样的:
我们需要我们的文件在全球网络上成功地工作,所以我们应该尽量遵守标准化的命名约定。
最好避免在文件名中使用字符空格。从技术上讲,这在本地环境(Apple 和 Windows OS)中是可以接受的,但是,字符空间不被其他系统识别。理想情况下,使用下划线或连字符来分隔文件名中的单词。
不要使用任何特殊字符,例如!, ?、%、#或/。最好将文件名限制为下划线、数字和字母。
对于这个项目,你会注意到,我尝试一致地以SZ_
开始我所有的文件名。这是因为它们是游戏名字的首字母——太空僵尸。在命名和组织文件时保持一致性和描述性是很重要的,这样就可以清楚地找到特定的数据和文件包含的内容。
通过以有意义的方式命名文件,您可以增加将来找到这些文件并知道它们包含什么信息的机会。当你来开发新游戏时,你将很容易通过搜索所有以SZ_
开头的文件来定位空间僵尸文件。
最后,保持文件名尽可能短是一个好习惯。除了增加文件的大小,这也让他们更容易记住六个月后的事情。
托管和媒体文件
只要文件还在你的硬盘上(之前创建的文件和文件夹),你就可以在你的电脑上轻松地测试游戏。这对刚起步的开发人员来说当然没问题。
尽管如此,当你开发了几个游戏时,你可能希望展示给所有人看和玩。
为此,您需要将文件上传到服务器计算机上。服务器本质上是一台连接到互联网的计算机。
第一部分:您的计算机与托管服务器
你需要在服务器上开一个账户。如果你在谷歌上搜索“服务器托管免费试用”,你有几个选择。如果你仍然不确定,请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
以下是可用主机选项的简短列表。
专用服务器
这是最昂贵的选择。本质上,你拥有连接到互联网的计算机。如果你是一家大公司或经销商,这是唯一的选择。
共享服务器
这通常是托管的最经济的选择。这对像你这样租用服务器的人来说意义重大。当然,主要优势是低得离谱的成本。然而,随着您的游戏开发专业知识的增加,您可能会发现这个选项有局限性,不适合您的特定需求。
云托管
前两个选项依赖于一台物理计算机,而云托管允许无限数量的计算机作为一个系统。
第二部分:下载项目的媒体
项目中使用的媒体文件(图像和声音文件)可供您下载。
打开您的互联网浏览器并转到以下 URL:
http://zarrarchishtiauthor.com/downloads/
单击下载按钮。这将启动下载。完成后,浏览器会通知您。导航到您的下载文件夹并找到下载的文件。
应该是一个叫raw_media_1.rar
的文件。现在您需要将这个压缩文件解压到一个名为Raw Media
的新文件夹中。双击该文件夹,您将看到以下四个文件夹:
> Images
> JS
> sounds
> html_web
首先,将所有四个文件夹复制到您的Raw Images
文件夹,它位于My_Work_Files
文件夹中。
在这个阶段,我们只对JS
文件夹中的文件感兴趣。随着游戏的进行,我们将回到其他文件夹,根据需要复制文件。双击JS
文件夹(在Raw Images
文件夹中)。使用与之前相同的技术复制文件,继续复制所有文件,然后将它们粘贴到您自己的js
文件夹(在My_Work_Files
文件夹中)。
您的js
文件夹(在My_Work_Files
文件夹中)现在应该是这样的:
暂时就这样吧!我们已经成功地建立了我们的游戏开发环境。我们现在准备开始编码我们的游戏!
我们从JS
文件夹中复制的文件是特殊的 JavaScript 程序,我们可以在游戏中使用。想象一下由谷歌等公司维护的代码库,其中包含的功能让我们的生活变得更加轻松。
这些文件——例如 jQuery——是快速、小型且功能丰富的 JavaScript 库。它们共同使 HTML 文档遍历和操作、事件处理、动画和 AJAX 等事情变得更加简单,并提供了一个可在多种浏览器上工作的易于使用的 API。
当使用这样的库时,我们不需要担心它们是如何工作的。我们只需要知道他们做了什么,这样我们就可以决定是否要在我们的游戏中使用他们。
使用 jQuery 等库的另一个优点是,它在所有主流浏览器中运行完全相同,包括 Internet Explorer 6!所以不用担心跨浏览器的问题。
通常,我们直接从源服务器链接到这些文件。这样做的好处是,当我们运行游戏时,我们总是得到代码的最新副本。然而,因为我们希望能够离线玩游戏,所以让我们选择将它们下载到本地文件夹中。
- 在
窗口中,是否出现了在此提取的选项?如果没有,需要从下面下载 WinRAR:
http://www.win-rar.com/download.html
- 你在用笔记本电脑吗?要右击,需要先点击
,然后点击鼠标垫。
- 下载媒体文件时,您是否收到来自浏览器的消息,警告您下载不常用,可能有危险?如果是,这是因为我选择了 WINRAR 而不是 WINZIP 文件。这些文件并不危险。您可以单击“保持”;但是,在打开文件夹之前,请随意对其进行病毒检查。
二、HTML 入门
"Nine people can't have a baby in a month."
弗雷德·布鲁克斯
HTML 是一种用于开发网站的标记语言。那么为什么我们的游戏需要这个呢?最好把 HTML 想象成我们游戏的骨架或骨骼结构。
顺便说一下,一旦你完成了这一章,你不仅开始了你的游戏开发之旅,也开始了你的网页开发之旅!
你好世界
在我 20 年的编程生涯中,我学习了许多编程语言。我一直在做的第一个项目是学习如何输出单词“Hello World”对着屏幕。我打赌你也遵循这个传统,所以让我们用 HTML 开发一个“Hello World”页面。
使用在第一章的“第二部分:设置我们的文件”一节中使用的相同程序或 IDE,在记事本或文本编辑中打开My_Work_Files
文件夹中的default.html
文件。
当文件打开时,它应该是完全空白的。键入以下几行:
<html>
<head>
</head>
<body>
<div id="SZ_maincontent">
Hello World.
</div>
</body>
</html>
导航到菜单,单击文件,然后单击保存。您现在可以关闭该文件了。导航回菜单,单击文件,然后单击退出/关闭。
你准备好测试你的第一个程序了吗?
回到My_Work_Files
文件夹,双击default.html
文件。这将在您的默认 Internet 浏览器中打开;比如微软 Edge,谷歌 Chrome,或者 Safari。
在浏览器上打开的页面应该是一个完全空白的页面,上面写着“Hello World”显示在左上角。太好了。我们的程序成功了,我们已经编写了第一段代码!
显然,这还远不是一场游戏。尽管如此,从现在到那时要坚持工作。请放心,到本书结束时,我们将已经开发了整个游戏。这肯定是值得的。你将会学到很多不同的技术来开始你开发一套游戏的旅程!
HTML 代表超文本标记语言。超文本是你在互联网上导航的方法。Hyper 只是意味着它不是线性的,或者你可以通过点击链接去网络上的任何地方。标记是 HTML 标签对其中的文本所做的事情。他们将其标记为某种类型的文本(例如粗体文本)。
以下是您刚刚编码的每个标签的描述:
- 每个新网页的开头和结尾都需要这样做。这两个标签中的所有内容构成了页面的内容。
- head 标签的内容包括页面标题、脚本、样式和元信息。
- 我们网页的所有视觉内容,如文本、超链接和图像,都包含在这个标签中。
- 这定义了我们页面的一个特定部分。最好将 div 标签视为容器。在更大的 div 标签中包含 div 标签并不罕见。
你会注意到结束标签和开始标签基本上是一样的,前面有一个正斜杠;例如,</div>
表示您正在关闭那个特定的标签。
请记住,每个标签都必须关闭。
背景图像
游戏的背景图像不会改变、移动或与游戏互动。它为所有将被实际游戏控制的各种元素提供了背景。
首先,转到My_Work_Files
文件夹的Raw Images
文件夹中的images
文件夹。找到名为SZ_background_image.jpg
的文件。你需要将这个文件复制到你的Images
文件夹中,看起来应该是这样的:
让我们重新打开default.html
文件。通过选择“Hello World”行并单击删除/退格删除该行。现在键入以下新行(所有新文本都以粗体显示):
<html>
<head>
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" />
</div>
</body>
</html>
保存文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。
通过使用<img>
标签,我们为页面定义了一个背景图片。需要注意的是,从技术上讲,图像并没有插入到我们的 HTML 页面中;相反,背景图片已经链接到我们的 HTML 页面。标签为背景图像创建了一个保存空间。
“Hello World”文本应该已经消失了,背景图像现在在它的位置上。它看起来不像是覆盖了屏幕。不要担心那个。我们将在下一章调整图片的大小。
在这一节中,您遇到了<img>
标签,当您想要在 web 页面中放置图像时会用到它。
在<img>
标签中,你会注意到
id="SZ0_0"
顾名思义,这是图像标签的 ID。当我们在第四章开始用 JavaScript 编码时,会用到这个 ID。
此外,您可能已经注意到了src
标签:
src="img/SZ_background_image.jpg"
src
,代表“源”,允许您指定图像的位置。在本节的前面,我们将SZ_background_image.jpg
放在了images
文件夹中。如您所见,src
是图像文件的确切位置和名称。
现在,让我们回想一下上一节,我说过你总是需要包含结束标签。在本节的最后,我声明所有的标签都必须关闭。然而,我们刚刚编写的代码没有包含</img>
。我忘了吗?
我所做的是在开始标签中关闭我们的标签。注意,在我们的img
标签的末尾,>
前面有一个正斜杠。如果您不需要在开始标签本身的内容之外添加元素,这是结束标签的另一种方式。
让我们分析一下我们的代码行:
<img id="SZ0_0" src="img/SZ_background_image.jpg" />
我们已经设法将所有关于我们的图像的信息放在开始标记中。不需要额外信息;因此,我们可以通过写/>
来结束我们的标签。
如果你想知道,下面是同样有效的:
<img id="SZ0_0" src="img/SZ_background_image.jpg" ></img>
添加剩余的图像
以下图片也需要添加到我们的 HTML 页面:
SZ_gun.png
SZ_reload.png
SZ_score.png
当我们完成游戏时,将会有更多的图像;然而,这就是我们在现阶段所需要的一切。
像以前一样,进入My_Work_Files
文件夹的Raw Images
文件夹中的images
文件夹。找到三个新的.png
文件,并将它们复制到你的Images
文件夹中,看起来应该是这样的:
现在,重新打开default.html
文件,键入以下新行(所有新文本都以粗体显示):
<html>
<head>
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" />
<img id="SZ0_1" src="img/SZ_gun.png" />
<img id="SZ0_2" src="img/SZ_reload.png" />
<img id="SZ0_3" src="img/SZ_score.png" />
</div>
</body>
</html>
保存文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。
您现在应该会看到三个新图像。您可能需要向下滚动网页。同样,不要担心图像如何出现在页面上。请确保您可以看到之前的背景图像和我们刚刚添加的三个新图像。
在这个阶段,相当多的人问我 HTML5 游戏开发是否和 web 开发人员一样。是的,就像 Xbox 游戏机开发者只是一个 C#/C++表单开发者一样。然而 HTML5 游戏看起来和感觉上都不像一个普通的网站,不是吗?当你开发这个游戏的时候,你会发现一个 HTML5 游戏开发者必须学习作为一个网页开发者的所有知识,甚至更多。你需要弄清楚一个网络开发者的边界在哪里,然后学习为了你的游戏引擎你能把它们推进多远。
在这一章中,我们已经设法将四个初始的图形元素编码到我们的屏幕上。他们可能看起来不太好看,因为他们似乎不在正确的地方,也没有正确的大小。不过不用担心,在下一章中我们将对四张图片应用 CSS,这将使它们准确地对齐我们想要的位置。
三、是时候应用一点 CSS 了
#tower-of-pisa
{
font-style: italic;
}
CSS 代表级联样式表,是一种用于帮助设计网站样式的语言。它可以用来描述页面在颜色、布局和字体方面的外观。
那么为什么我们的游戏需要这个呢?以前,我们把 HTML 想象成我们游戏的骨架或骨骼结构。CSS 代码将成为我们游戏的外观。然而,如果你熟悉构建网站,你可能会想知道 CSS 在 HTML5 游戏开发中实际上扮演了多大的角色。
随着 CSS3 的到来,CSS 中的动画允许浏览器确定哪些元素应该获得 GPU 层,这导致了硬件加速。但是,不要一开始就把所有的动画都转移到 CSS 中。一般来说,让每个元素都有自己的层并不是一个好主意。如果你这样做,那么你的 GPU 将会很快耗尽内存。我相信您会同意,作为开发人员,没有比收到可怕的“内存不足”错误更糟糕的感觉了。
从一个快速测试开始
在我们对齐和调整我们的图像之前,让我们从一个简单的测试开始我们的 CSS 文件。这个测试是为了看看我们是否能把整个页面的背景变成红色。通过这样做,我们将确保default.html
页面与我们的 CSS 页面成功通信。
让我们打开SZ_master.css
文件。当文件打开时,它应该是完全空白的。键入以下几行:
html {
height: 100%;
}
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
background-colour: red;
}
您现在可以保存并关闭该文件。
从这段代码中可以看出,CSS 文件的语法由三部分组成。
selector
这通常是你想要定义的 HTML<tag>
。在前面的代码中,我们将<html>
和<body>
标记定义为选择器。- 顾名思义,我们在这里定义我们希望对标签的什么属性应用样式。在我们的
<html>
示例中,我们将 height 属性定义为 style。 value
您希望为属性定义的实际样式。在我们的例子中,我们决定标签的高度是屏幕尺寸的 100%。
有趣的是,您可以通过简单的分组为多个标签指定相同的参数。
在测试之前,我们需要将这个文件链接到我们的default.html
文件中。重新打开default.html
文件,键入以下新行(粗体):
<html>
<head>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" />
<img id="SZ0_1" src="img/SZ_gun.png" />
<img id="SZ0_2" src="img/SZ_reload.png" />
<img id="SZ0_3" src="img/SZ_score.png" />
</div>
</body>
</html>
保存文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。
您应该会看到与上次相同的屏幕(即四幅图像),但背景是红色而不是白色。这很好,因为这意味着您成功地将 CSS 文件链接到了主 HTML 页面。
我们已经在SZ_master.css
文件中介绍了相当多的 CSS 技术。让我们过一遍。
- 这将我们的 HTML 页面的高度设置为 100%。意味着我们的内容应该能够从上到下覆盖可见屏幕。
值得注意的是,我们可以使用属性 min-height 和 max-height 来覆盖 height 属性。
padding
: 0 0 0 0;
这将清除页面内容周围的一个区域。考虑做一个四边空白,在这里你指定你想要它有多厚。在我们的例子中,我们希望内容覆盖整个页面,所以我们将所有四个边的填充设置为 0。四个 0 分别对应top
、right
、bottom
、left
。position
: fixed;
顾名思义,这是根据浏览器窗口将图像定位到一个固定的位置。所以top: 0;
意味着距离浏览器顶部 0 像素(像素是一种度量单位)(也就是说,你希望它固定在顶部)。类似地,bottom: 0;
表示您希望图像放在浏览器窗口的底部。最后,left: 0;
和right: 0;
是指放置在浏览器窗口左侧或右侧的图像。margin
:0;
边距设置元素周围空白的大小。在我们的例子中,我们不希望屏幕边缘有任何空白。- 通过使用
user-select
属性,我们可以控制玩家如何与屏幕上的文本元素交互。在这种情况下,它被设置为none
,这意味着我们不希望用户选择或单击任何文本元素。这样做的原因是,它可能会分散玩实际游戏的注意力(例如,允许用户选择高分的文本)。 background-colour
: red;
顾名思义,这个设置屏幕的背景颜色。如果你愿意,试着用yellow
或者你选择的任何颜色来代替red
这个词。保存文件并刷新浏览器。
我们还在 HTML 文件中添加了另一行代码:
<link href="css/SZ_master.css" rel="stylesheet" />
标签是在页面上包含 CSS 文件的标准方式。href
指定了我们希望包含的 CSS 文件的位置。rel
标签指定了 HTML 文件和 CSS 文件之间的关系。在这种情况下,CSS 文件充当 HTML 文件的样式表。
我们的背景图像
让我们开始修复图像。我们将从背景图像开始。理想情况下,我们希望这个图像填充我们的页面(就像红色背景颜色一样)。
打开SZ_master.css
文件,输入以下新行(所有新文本以粗体显示):
html {
height: 100%;
}
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
}
img {
max-width: 100%;
height: auto;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#SZ0_0 {
position: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
}
保存文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。
请注意,我们已经从body
标签中移除了background-colour: red;
。请确保从代码中删除该行。您的文件看起来应该与显示的完全一样。
您可能想知道为什么我们用四种不同的方式对user-select
属性进行编码。第一种方法是 CSS 中的标准属性(即user-select
)。然后,我们继续定义由各种渲染引擎提供的厂商前缀属性。这允许针对每个单独的浏览器引擎设置特定的属性,以安全地解决实现之间的不一致。
以下是我们使用的供应商前缀属性:
webkit
对于 Chrome 和 Safarimoz
对于火狐来说ms
对于 Internet Explorer
历史上,在 W3 最终澄清之前,我们使用这些前缀来实现新的 CSS 特性。因此,随着时间的推移,属性的最终版本将移除前缀。
保存文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。
你的屏幕应该看起来像下面的截图。
您应该首先注意到背景图像现在覆盖了整个屏幕。另外,其他三个图像完全从屏幕上消失了。别担心。它们仍然在那里——在背景图像后面。
添加到 CSS 中的第一个样式是用于标签的。这意味着定义的样式适用于我们添加到页面中的每一幅图像。我相信你会同意这是一个节省大量时间的技术,因为另一种选择是为我们添加的每张图片重复费力的样式。
无论如何,不是每个图像都需要相同的风格。您可以看到第二种样式是专门为一个图像标签编写的,该标签被标识为#SZ0_0
。
我们放在<img>
标签中的样式是更通用的样式,应该适用于所有图像。然后,我们可以为特定的图像添加单独的样式,并添加更多的样式。我们甚至可以覆盖写在<img>
标签中的样式。
在我们离开这之前,我们为什么把标签叫做#SZ0_0
?如果回到第 2.3 节,请注意以下几点:
<img id="SZ0_0" src="img/SZ_background_image.jpg" />
该图像被标识为SZ0_0
。在 CSS 中,您可以通过在 ID 前放置散列符号(#
)来识别图像。
让我们来看看我们使用的新 CSS 技术。
max-width
: 100%; height: auto;
我们希望图像伸展到其容器的整个宽度。此外,我们希望代码在应用新宽度时自动确定高度。这确保我们在调整大小时保持图像的纵横比。user-drag
: none;
我们不希望用户能够拖动屏幕上的图像。- 这些是 CSS 扩展,是网络浏览器支持的属性,但还不是官方 CSS 规范的一部分。
top: 0; left: 0;
设置图像的上边缘和左边缘。在这种情况下,我们希望图像始终位于其容器的左上角。- 顾名思义,我们希望图像的最小宽度和高度是其容器的最大尺寸。
我们的其他图像
我们可以开始修复其他三幅图像。首先,这里有一个关于图像和它们应该放在哪里的提示:
SZ_gun
枪的图像应该在屏幕的右下角。SZ_reload
重新加载按钮应该出现在屏幕的左上角。SZ_score
得分图像应该出现在屏幕的右上角。
现在打开SZ_master.css
文件,键入以下新行(所有新文本都以粗体显示):
html {
height: 100%;
}
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
}
img {
max-width: 100%;
height: auto;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#SZ0_0 {
posi
tion: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
}
#SZ0_1 {
position: fixed;
bottom: 0;
right: 0;
}
#SZ0_2 {
position: fixed;
top: 0;
left: 0;
}
#SZ0_3 {
position: fixed;
top: 0;
right: 0;
}
保存文件,然后关闭它。
在这段代码中,我们为三幅图像中的每一幅定义了三个属性。但是,请注意,这些属性及其后续值是完全相同的。前面,我提到了这样一个事实,即我们可以通过简单地对多个标签进行分组来为它们指定相同的参数。因此,如果您愿意,可以尝试使用前面的代码,将粗体代码替换为以下代码:
#SZ0_1, #SZ0_2, #SZ0_3 {
position: fixed;
top: 0;
right: 0;
}
回到My_Work_Files
文件夹,双击default.html
文件。
您的屏幕应该看起来像下面的截图。
虽然你现在可以看到所有四个图像的对齐位置,但它们的大小不太合适;但是,不要担心这个。在下一章,我们将使用 JavaScript 来调整图像的大小。
position
属性指定用于元素的定位方法的类型(静态、相对、固定或绝对)。然后使用 top、right、bottom 和 left 属性定位元素。但是,除非首先设置了position
属性,否则这些属性将不起作用。根据位置值的不同,它们的工作方式也不同。
让我们简单看一下四个位置值。
static
元素不受上、右、下、左属性的影响。relative
表示设置一个相对定位的元素的顶部、右侧、底部和左侧属性,导致它被调整远离其正常位置。fixed
表示相对于视窗定位,这意味着即使页面滚动,它也总是停留在同一位置。top、right、bottom 和 left 属性用于定位元素。absolute
表示相对于最近的祖先定位(而不是相对于视口定位,如 fixed)。
在我们的例子中,我们使用了fixed
和bottom: 0; right: 0;
。在上一节中,我们设置了图像的左上角;而在这里,我们可以从图像容器的右下角设置图像。
因为我们需要枪总是位于屏幕的右下角,所以在这种情况下使用 bottom-right 属性比使用 top-left 属性更有意义。
四、使用 JavaScript 应用智能
“总是把维护你代码的人想象成一个知道你住在哪里的暴力精神病患者。”
里克·奥斯本
如你所知,HTML 是我们游戏的骨架,CSS 是我们游戏的外观。那么 JavaScript 带来了什么呢?JavaScript 是一种用于在网站中创建交互性的编程语言。所以我们可以说我们使用 JavaScript 作为游戏的主控制器。
那么为什么我们的游戏需要它呢?显而易见的答案是,游戏需要能够创造僵尸,开枪,并响应用户的命令。这是事实,但是游戏需要 JavaScript 来执行大量的其他工作。例如,在前一章中,我们发现需要根据浏览器的大小来调整图像的大小。现在让我们用 JavaScript 来做这件事。
为什么我们需要调整大小?
我们的游戏可以在多种设备上玩;电脑,笔记本电脑,移动设备,平板电脑,甚至连接到大电视的控制台。在这些设备中,有许多不同的屏幕尺寸。手机和笔记本电脑有各种各样的屏幕尺寸。
让我们更进一步。如果有人调整了互联网浏览器窗口的大小,该怎么办?现在我们正在谈论无限多的组合。
为一切可以想象的事物创建图形将是极其耗时的。事实上,这是不可能的,因为似乎总是有新型号的手机(因此,新的屏幕尺寸)或新的电脑显示器问世。因此,我们需要找到一种通用的方法来调整任何屏幕尺寸的图像。
我们如何普遍调整大小?
如果你手边有一把 30 厘米的尺子,那就看看吧。想象我们为 15 厘米标记设计游戏。我们可以使用 JavaScript 来告诉我们标尺上的实际尺寸。所以我们假设它返回为 10 厘米。然后,我们可以计算出一个比率(即 10 除以 15),该比率可用于所有测量;十除以十五是 0.67。这意味着当我们将它应用到我们的图像时,它们会变得更小,这正是我们想要的。同样,如果尺寸变大了,比如说 20 厘米,比例会反映这一点,并使所有图像比我们设计的要大。
让我们写一个函数来计算这个比率。打开SZ_main.js
。这个文件应该是完全空白的。键入以下几行:
//global vars
//need to store the ratio
var ratio;
//need easy access to the width
var newWidth;
//function that gets called when game starts
$(window).load(function () {
//need to grab an instance of our screen
var div = $(window);
//we can now work out the ratio
ratio = (div.width() / 1024);
//while we are here we can grab the width for future use
newWidth = div.width();
});
您现在可以保存并关闭该文件。
各种 JavaScript 术语的定义可以在下面的进一步信息部分找到。另一件要注意的事情是,我试图输入尽可能多的注释来解释我们的代码行(正如您在自己的编程语言中所习惯的那样)。
在测试之前,我们需要将这个文件链接到我们的default.html
文件。因此,让我们重新打开default.html
并键入以下三个新行(所有新文本都以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" />
<img id="SZ0_1" src="img/SZ_gun.png" />
<img id="SZ0_2" src="img/SZ_reload.png" />
<img id="SZ0_3" src="img/SZ_score.png" />
</div>
</body>
</html>
保存文件,然后关闭它。
这三个新行包含了<script>
标签。在我们的例子中,我们选择通过src
属性将这个标签链接到一个外部脚本文件。或者,我们可以使用相同的标记来定义包含脚本语句的客户端脚本。
现在双击default.html
文件。
什么都没有改变,是吗?这是因为 JavaScript 代码在后台执行任务。此外,我们还没有告诉它对我们的图像做任何事情。我们所做的只是在代码中存储一个值(即比率)。
尽管如此,看看我们的第一部分代码是否正常工作还是不错的。让我们添加一行代码,在屏幕上显示一个消息框。在这个框中,我们将输入代码刚刚计算出的比率值。这并不十分令人兴奋,但至少我们从代码中获得了某种形式的反馈。这太令人兴奋了。
现在打开SZ_main.js
文件,键入以下新行(粗体部分):
//global vars
//need to store the ratio
var ratio;
//need easy access to the width
var newWidth;
//function that gets called when game starts
$(window).load(function () {
//need to grab an instance of our screen
var div = $(window);
//we can now work out the ratio
ratio = (div.width() / 1024);
//while we are here we can grab the width for future use
newWidth = div.width();
//We are adding in a temporary bit of code here
window.alert("Hi this is your code and I have just worked out that the ratio will be "+ratio);
});
我们现在可以保存并关闭该文件。
我们刚刚使用了window.alert()
方法,它通常显示一个带有指定消息和 OK 按钮的警告框。通常,警告框用于确保重要信息显示给用户。在我们的例子中,我们使用警告框来通知我们一个变量的值,在我们的代码中,这个值是我们无法访问的。
我应该注意到,警告框通过迫使浏览器阅读消息,将焦点从当前窗口移开。我不建议过度使用这种方法,因为它会转移用户玩游戏的注意力,直到盒子关闭。
回到我们的My_Work_Files
文件夹,双击default.html
文件。您应该会看到我们的网站有一个消息框:
我们这里有我们的代码与我们交谈。它告诉我们,它计算出的比值。
在继续之前,我们需要删除刚刚添加的两行。打开SZ_main.js
文件。删除这两行代码后,代码应该再次显示如下:
//global vars
//need to store the ratio
var ratio;
//need easy access to the width
var newWidth;
//function that gets called when game starts
$(window).load(function () {
//need to grab an instance of our screen
var div = $(window);
//we can now work out the ratio
ratio = (div.width() / 1024);
//while we are here we can grab the width for future use
newWidth = div.width();
});
在这本书里,你会创造出许多奇妙的功能,毫无疑问,在你继续开发的游戏里也是如此。然而,我希望你们珍惜这一刻,就像我在 1994 年学习 Pascal 编码时一样。
窗口框没有出现吗?别担心。以下建议之一应该会有所帮助:
- 返回并重新检查每一行代码是否相同
- 你是否漏掉了行尾的分号(
;
)? - 您是否确保在 HTML 文件中添加了三行代码?
js
文件夹中的九个文件是否都在它们应该在的位置?
如果你的代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
接下来,我们将让这个比例发挥作用。
什么是函数?
我们会用 JavaScript 写很多函数。函数只是一组指令,当函数本身被调用运行时执行这些指令。所以当我们的函数被调用时,它决定了比例并存储了屏幕的宽度。
为什么//开头的台词写的像会话英语?
当你用双正斜杠(//
)开始一行时,你是在告诉计算机忽略这一行。你为什么要这么做?它是为我们准备的,叫做评论热线。它的目的是给我们自己(或者其他程序员)留言。通过留下信息,我们打破了代码,使整个程序更容易阅读。你可以写任何你喜欢的东西。我喜欢用它来解释为什么注释后的代码是原来写的。
您不必注释每一行;然而,我总是被教导要注释尽可能多的代码行。对于一些程序员来说,这可能显得有些过火;然而,我发现当我几年后回到我的代码时,我写的注释帮助我理解了代码背后的推理。
为什么我们将另外两个文件添加到我们的 HTML 文件中?
说到添加SZ_main.js
文件,我们还添加了 jQuery 和 jQuery-UI 文件。这些基本上都是高级函数(就像你写的那个)。只要我们使用它们的函数,我们所要做的就是将它们添加到我们的 HTML 中。
这些功能快速、可靠且功能丰富。世界上一些最大的公司使用它们,像我们这样的小游戏开发者也使用它们。
现在让我们看看我们写的一些 JavaScript 代码。
var ratio
;
我们在这里声明一个叫做ratio
的变量。变量是可以存储数据的容器。我们可以将数据转换成比率并从中读取。- 一旦整个页面加载完毕,这个函数就会被调用。这使得它非常有用,因为这个函数内部的指令要求元素(例如,图像)出现并加载到屏幕上。
var div
= $(window);
正如我们之前发现的,var
创建了一个容器来存储数据。然而,在这种情况下,我们使用它来传递整个窗口的实例。这个名为div
的变量现在包含了所有关于我们窗口的重要信息。例如,我们继续使用下面的语句。newWidth
= div.width();
这意味着我们可以将窗口的宽度存储在名为newWidth
的变量中。
让我们调整图像的大小
提醒一下,这些是理想尺寸的图片:
SZ_gunWidth 133px
和Height 150px
SZ_reloadWidth 200px
和Height 90px
SZ_scoreWidth 235px
和Height 100px
打开js
文件夹中的SZ_setupContent.js
文件。当文件打开时,它应该是完全空白的。键入以下几行:
//main function
function main_call_setupContent() {
//need to resize all elements
//first we set their normal sizes in CSS
//Gun
$('#SZ0_1').css('width', 150 * ratio);
$('#SZ0_1').css('height', 150 * ratio);
//Reload Button
$('#SZ0_2').css('width', 200 * ratio);
$('#SZ0_2').css('height', 90 * ratio);
//Score
$('#SZ0_3').css('width', 235 * ratio);
$('#SZ0_3').css('height', 100 * ratio);
}
保存并关闭该文件。
在某些时候,您可能希望重新访问这个函数并重新编码这个流。我建议您将图像 id 的值放入一个数组中;例如,
var image_ids= ["#SZ0_1","#SZ0_2","#SZ0_3"];
然后,您还需要将每个图像的值放入另一个数组中;例如,
var image_sizes = [ [150, 150], [200, 90], [235, 100] ];
然后,您可以编写一个for loop
来执行相同的代码三次,用 ID 数组中的下一个值替换 ID,用 size 数组中的值替换宽度和高度值。
打开SZ_main.js
文件,输入以下新行(新文本以粗体显示):
//global vars
//need to store the ratio
var ratio;
//need easy access to the width
var newWidth;
//function that gets called when game starts
$(window).load(function () {
//need to grab an instance of our screen
var div = $(window);
//we can now work out the ratio
ratio = (div.width() / 1024);
//while we are here we can grab the width for future use
newWidth = div.width();
//let’s apply the ratio to our elements
main_call_setupContent();
});
在测试之前,我们需要将这个文件链接到我们的default.html
文件。重新打开default.html
文件并输入以下行(新文本以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" />
<img id="SZ0_1" src="img/SZ_gun.png" />
<img id="SZ0_2" src="img/SZ_reload.png" />
<img id="SZ0_3" src="img/SZ_score.png" />
</div>
</body>
</html>
回到My_Work_Files
文件夹,双击default.html
文件。现在,您应该可以看到三个元素的大小已经调整,如下面的屏幕截图所示:
恭喜你!我们现在已经完成了游戏的第一部分!我们将在接下来的章节中进一步开发这个游戏。然而,主要的构建模块已经完成。从现在开始,我们将添加更多的 HTML,更多的 CSS,是的,甚至更多的 JavaScript,直到我们的游戏最终可以玩。
理想的尺寸从何而来(比如枪:宽 175px,高 200px)?
在任何开发可以开始之前采取的步骤是每个屏幕的布局设计。这是你在屏幕上放置元素的地方,比如枪。
你首先需要选择一个正常的尺寸。在我们的例子中,我们选择了屏幕宽度 1025px 和高度 800px。当然,用户拥有这种精确屏幕尺寸的可能性非常小。这也是我们之前算出比例的原因。
您可以使用任何软件设计程序(如 Macromedia Photoshop 或 Fireworks)来创建布局文件。一旦我们创建了大小为 1024 × 800 的新画布,我们就可以调整元素的大小并将其重新定位在画布上我们想要的位置。例如,我们把枪放在右下角,宽 175 像素,高 200 像素。
现在,我们可以将我们的比例应用到宽度和高度,以获得所使用的屏幕的准确大小。
我们用 JavaScript 创建了如下函数:
function main_call_setupContent()
{
需要注意的是,在我们调用这个函数之前,这个函数中的指令不会被执行,这是我们在SZ_main.js
中通过调用main_call_setupContent();
完成的。main_call_setupContent()
;
现在指令才被程序执行。
最后,让我们看看下面一行代码:
- 我们可以直接从 JavaScript 中操作一个元素的 CSS。这是游戏开发中一个非常强大和有用的工具。例如,如果我们希望一个元素在拍摄后变大,我们可以直接从用于识别按钮点击的 JavaScript 代码中完成。
五、尝试一下:第一部分
代码从不说谎,但注释有时会说谎
罗恩·杰弗里斯
在这一章中,我们将研究我们想要我们的枪做什么。当用户按下屏幕上的任何地方,除了重新加载按钮,它应该被视为一个镜头。射击是如何发生的以及射击的后果在第七章中讨论。现在,让我们看看对用户点击的反应。
将会有一些令人敬畏的技术被使用,包括用于动画的精灵表和用于流体运动的数学。当你将来开发其他游戏时,你可能会发现自己会回来重用这些功能和技术。这正是商业游戏开发中发生的事情。
顺便说一下,这一章中的一些代码来自我最近为一个儿童游戏做的一个项目,这个项目在格拉斯哥的 Kelvingrove 艺术画廊和博物馆举办。
改变我们的光标并记录一次点击
在我们正在开发的射击游戏中,鼠标光标通常会变成十字准线。
只需使用 CSS 就可以改变光标。打开我们的CSS
文件夹中的SZ_master.css
文件。键入以下新行(粗体):
html {
height: 100%;
}
+
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
cursor: crosshair;
}
img {
max-width: 100%;
height: auto;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#SZ0_0 {
posi
tion: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
}
#SZ0_1 {
position: fixed;
bottom: 0;
right: 0;
}
#SZ0_2 {
position: fixed;
top: 0;
left: 0;
}
#SZ0_3 {
position: fixed;
top: 0;
right: 0;
}
保存文件,然后关闭它。回到我们的My_Work_Files
文件夹,双击default.html
文件。
请注意,鼠标光标已从箭头变为十字光标。
我们刚刚写的那行是什么?
cursor: crosshair;
指定用鼠标点击时显示的光标类型。
您可能想知道还有哪些其他类型的光标可供您使用。以下是光标类型的列表。如果您愿意,可以将 CSS 文件中的单词 crosshair 替换为这些单词中的任何一个。
- 电子调整大小
- 移动
- 西北-调整大小
- s-调整大小
- 文本
- 不掉线
- 夺取
- n-调整大小
- 指针
- se-调整大小
- w-调整大小
- 不允许
- 帮助
- 重新调整大小
- 进步
- SW-调整大小
- 等待
让我们的枪更真实
对用户来说,游戏越吸引人,他们就越喜欢一遍又一遍地玩。增加用户参与度的方法之一是在游戏中加入一些小细节。例如,如果当用户在屏幕上移动光标时,枪会做出反应,这不是很好吗?
为此,我们将使用 JavaScript。打开js
文件夹中的SZ_movement.js
文件。当文件打开时,它应该是完全空白的。键入以下几行:
function rotateGun(e) {
//using the e value we can deduce the X co-ordinates
var xPos = e.clientX;
//We need to work out where the mouse cursor is as a percentage of the width of the screen
//We will work this out by dividing the current X position by the overall screen width which if you remember we put in newWidth
var currentXPositionPercentage = xPos/newWidth;
//We now want to apply this
to the maximum amount of rotation which is 50 however the starting rotation is -15 not 0
var amountToRotate = -15 + (currentXPositionPercentage * 50);
//Let’s rotate the gun!
$("#SZ0_1").css('transform', 'rotate('+amountToRotate+'deg)');
}
我们现在可以保存并关闭该文件。
下面的“更多信息”一节详细解释了该代码。
在测试之前,我们需要将这个文件链接到我们的default.html
文件。重新打开default.html
文件,并在我们现有的一行中键入以下新行和额外的文本(所有新文本都以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" onmousemove="rotateGun(event)" src="img/SZ_background_image.jpg" />
<img id="SZ0_1" src="img/SZ_gun.png" />
<img id="SZ0_2" src="img/SZ_reload.png" />
<img id="SZ0_3" src="img/SZ_score.png" />
</div>
</body>
</html>
保存文件,然后关闭它。现在双击default.html
文件。
试着沿着屏幕移动鼠标。枪应该旋转,就好像瞄准我们要射击的目标。我相信你会同意这比一把静止的枪更吸引人。
我想讨论一下前面代码中的一个有趣的地方。onmousemove
顾名思义,当用户将鼠标移动到图像上时,触发一个 JavaScript 函数。它不会在手机等触摸屏设备上触发。您可能希望将来重新访问这部分代码,并修改它,以便当用户触摸图像的任何部分时它都会触发。
接下来,我们将看看使枪开火!
代码不起作用吗?其中一行不同于我们通常添加代码的方式。让我们一起经历这一切:
打开default.html
文件。
找到以开始的行
<img id="SZ0_0" onmousemove="rotateGun(event)" src="img/SZ_background_image.jpg" />
你是否完全按照显示的那样添加了额外的文本?
添加以下案文:
onmousemove="rotateGun(event)"
在...之间
id="SZ0_0" and src="
如果你的代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
我们用 JavaScript 写了下面一行:
var xPos = e.clientX;
按如下方式将e
传递给我们的函数:
function rotateGun(e) {
e
包含已发生事件的所有信息。在本例中,是鼠标在我们的图像上移动,我们声明如下:
<img id="SZ0_0" onmousemove="rotateGun(event)" src="img/SZ_background_image.jpg" />
当用户将鼠标移动到这个图像上时,我们的rotateGun
函数被调用,移动的数据被传入。
在第一行中,您可以看到我们从e
中提取了“clientX”。这是刚刚发生的鼠标事件的水平坐标(通常称为 x 轴)。
那么这个 X 轴是什么?试着把你的屏幕从左到右的用户动作想象成 X 轴。下图说明了我们想要的 X 轴和枪的旋转之间的关系。
最左边和最右边的枪之间的最大旋转是 50 度。因此,通过找出我们在屏幕上的确切位置,我们可以使用一个数学方程来确定枪的确切旋转角度。
还有一点:我们用来实际旋转枪的代码是通过使用 JavaScript 和 CSS 来完成的。JavaScript 通过执行以下操作来完成大部分工作:
- 每次用户移动鼠标时提醒一个功能
- 已确定鼠标移动了多少
- 将上面的值应用到我们的数学方程中
从这里,我们把这个值交给 CSS,然后它实际上旋转枪。
随意摆弄数字,测试出现的不同旋转;例如,更改以下行
var amountToRotate = -15 + (currentXPositionPercentage * 50);
把 50 改成 100。你会注意到枪的移动方式有了更大的变化。不断改变数字,直到你达到你满意的旋转水平。如果您想返回,只需键入前面的代码。
最后,我们在下面一行中遇到了另一个在 JavaScript 中操作 CSS 的例子:
$("#SZ0_1").css('transform', 'rotate('+amountToRotate+'deg)');
顾名思义,transform
属性将变换应用于任何元素。其他转换包括scale
、move
和skew
。
用精灵表制作枪的动画
当用户点击屏幕时,我们想让我们的枪开火。为了做到这一点,我们将使用一种叫做 sprite sheets 的东西。在开始添加 sprite 工作表之前,我们需要编写一些代码。这是因为 sprite 工作表不是你的浏览器(例如 Chrome)可以像处理我们目前使用的图片那样单独处理的。
我们需要编写一些代码来指导浏览器如何处理我们的 sprite 工作表图像。为此,我们将使用 JavaScript。提醒一句,这段代码比你目前所写的稍微长一点。我鼓励你坚持下去,因为这个特殊的函数可以在你做的每个项目中重用,而不需要改变代码中的任何东西。请确保您完全按照所示复制所有代码。
第一部分
打开js
文件夹中的SZ_SS.js
文件。当文件打开时,它应该是完全空白的。键入以下几行:
//We need a one stop function that will allow us to process sprite sheets
function setup_SpriteSheet(div_name, image_name, no_of_frames, widthx, heightx) {
//need the ratio of the container's width/height
var imageOrgRatio = $(div_name).height() / $(div_name).width() ;
//need to ensure no trailing decimals
var ratio2 = Math.round(ratio * 10) / 10;
//check that the width is completely divisible by the no of frames
var newDivisible = Math.round((widthx * ratio2) / no_of_frames);
//the new width will be the number of frames multiplied by our new divisible
var newWidthx = newDivisible * no_of_frames;
//also the new height will be our ratio times the height of the div containing our image
var newHeightx = heightx * ratio2;
//apply our new width to our CSS
$(div_name).css('width', (newWidthx));
//apply our new height to our CSS
$(div_name).css('height', newHeightx);
//
//take the image name and apply as a background image to our div
$(div_name).css('background-image', 'url(' + image_name + ')');
//finally we need to apply a background size remembering we need to multiply width by the number of frames
$(div_name).css('background-size', newWidthx * no_of_frames + 'px ' + newHeightx + 'px');
}
最初,我只是想添加一个标准的 sprite 工作表库;但是,通过我们自己编码,我们在未来的游戏中有更多的灵活性。随着你构建更多的游戏,你会发现并不是所有的 sprite sheet——或者使用它们的所有参数——都是相同的。因此,您需要重新访问前面的函数并对其进行调整,以确保它适合您当前的项目。如果我们只使用一个标准函数,它将严重限制你可以使用的 sprite 表的类型。
与所有其他标准函数一样,只要我们将来将这个文件链接到任何 HTML 文件,就可以使用我们的小函数。
什么是雪碧床单?
sprite sheet 是一种特殊的图像,它在平铺的网格排列中包含几个图像。
那么为什么要用雪碧床单呢?
Sprite sheets 让游戏运行得更快,更重要的是,占用更少的内存。通过将几个图形编译成一个文件,你可以让你的游戏在只需要加载一个文件的时候就可以使用这些图形。
雪碧床单是怎么设计的?
我们的精灵表有三个部分。第一,正常的静态是枪重新装弹的时候,枪开火的时候。下面说明了这一点:
为什么我们需要编写自己的特殊函数来使用 sprite 工作表?
有许多方法来处理雪碧表。每个程序员设计他们的代码来操作适合他们的 sprite 表。我在这里使用了一个非常简单的方法,处理线性布局的精灵。
此外,我们的游戏不需要复杂的使用任何精灵表。由于您正在编写所有的代码,我想确保您只需要编写最少的代码。然而,你可以使用这些代码作为你下一个游戏的基础,并在你认为必要的时候在其上进行构建。
第二部分
既然我们已经设置了处理任何 sprite 表的函数,我们可以用我们的枪来测试它。首先,我们需要用 sprite sheet 版本替换我们枪的静态图像。
转到My_Work_Files
文件夹的Raw Images
文件夹中的images
文件夹。将名为SZ_gun_SS.png
的文件复制到Images
文件夹,现在看起来应该像下面的截图。
第三部分
接下来,我们需要通知代码枪是一个 sprite 表,并传递关于它的所有信息(例如,您复制的图像名称)。
我们将使用 JavaScript 来做到这一点。重新打开js
文件夹中的SZ_SS.js
文件。键入以下新行(所有新文本都以粗体显示):
//We need a one stop function that will allow us to process sprite sheets
function setup_SpriteSheet(div_name, image_name, no_of_frames, widthx, heightx) {
//need the ratio of the container's width/height
var imageOrgRatio = $(div_name).height() / $(div_name).width() ;
//need to ensure no trailing decimals
var ratio2 = Math.round(ratio * 10) / 10;
//check that the width is completely divisible by the no of frames
var newDivisible = Math.round((widthx * ratio2) / no_of_frames);
//the new width will be the number of frames multiplied by our new divisible
var newWidthx = newDivisible * no_of_frames;
//also the new height will be our ratio times the height of the div containing our image
var newHeightx = heightx * ratio2;
//apply our new width to our CSS
$(div_name).css('width', (newWidthx));
//apply our new height to our CSS
$(div_name).css('height', newHeightx);
//
//take the image name and apply as a background image to our div
$(div_name).css('background-image', 'url(' + image_name + ')');
//finally we need to apply a background size remembering
we need to multiply width by the no of frames
$(div_name).css('background-size', newWidthx * no_of_frames + 'px ' + newHeightx + 'px');
}
//setup the Gun
function setup_gun_SS(){
//first let’s setup our gun SS
setup_SpriteSheet("#SZ0_1","img/SZ_gun_SS.png",28,150,150);
//need to access a special function in our js/ss.js file
$("#SZ0_1").animateSprite({
fps: 10,
animations: {
static: [0],
reload: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],
fire: [24,25,26,27,28],
},
duration: 50,
loop: false,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
}
});
}
保存并关闭文件。
为一个特定的动画编写所有不同的帧会变得非常乏味。想象一下,如果你有超过 500 帧!将来,当重新访问animateSprite
函数时,将其更改为取一系列值。您也可以尝试编写函数来获取一组范围;例如,帧(1 到 7、9 到 11 和 29 到 31)。
在测试之前,我们需要对 HTML 文件进行以下两项更改:
- 在头部引用新的 JavaScript 文件
- 将图像包含在它们自己的
div
中
重新打开default.html
文件并键入以下新行。请小心替换现有的代码行,使整个文件看起来像下面的代码(所有新文本都以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<script src="js/ss.js"></script>
<script src="js/SZ_SS.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)" />
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="img/SZ_reload.png" />
</div>
<div id="SZ0_3" >
<img src="img/SZ_score.png" />
</div>
</div>
</body>
</html>
在本节中,我们用下面的代码设置了第一个 sprite 表:
setup_SpriteSheet
("#SZ0_1","img/SZ_gun_SS.png",28,150,150);
让我们分别考虑括号中的每个参数。
#SZ0_1
是图像 IDimg/SZ_gun_SS.png
是精灵工作表的位置28
是我们的 sprite 工作表中包含的图像总数150,150
是 sprite 工作表中每个单独图像的大小
您可能已经注意到,我们对图像应用了一个特殊的功能。让我们仔细看看这个函数的每一行。
fps:
我们希望应用于精灵工作表动画的理想每秒帧数- 我们可以将一个 sprite 表中的图像细分成单独的动画
duration
:
我们希望每个动画运行的时间长度(以毫秒为单位)loop
:
一旦动画结束,我们希望动画重复播放还是停止播放?complete: function () {
如果loop
选项设置为假(即不重复),那么一旦动画完成,我们可以给出一组要执行的指令。
第四部分
最后,我们需要确保我们调用的是setup_gun_SS
函数。我们可以在SZ_setupContent
文件中这样做,它初始化我们所有的图像。
打开SZ_setupContent.js
文件,输入以下新行(所有新文本以粗体显示)。
//main function
function main_call_setupContent() {
//need to resize all elements
//first we set their normal sizes in CSS
//Gun
$('#SZ0_1').css('width', 150 * ratio);
$('#SZ0_1').css('height', 150 * ratio);
//Reload Button
$('#SZ0_2').css('width', 200 * ratio);
$('#SZ0_2').css('height', 90 * ratio);
//Score
$('#SZ0_3').css('width', 235 * ratio);
$('#SZ0_3').css('height', 100 * ratio);
//Any sprite sheets?
//Our Gun
setup_gun_SS();
}
我们现在准备测试!但是,不要期望太高,因为我们最初告诉代码只显示第一张图片。因此,让我们测试一下,确保我们的代码按预期运行。
保存所有文件,然后关闭它们。回到My_Work_Files
文件夹,双击default.html
文件。枪应该和以前一模一样。事实上,整个屏幕看起来应该是一样的。这很好,因为我们已经用一个 sprite 表替换了静态的枪图像,并告诉它显示第一个图像。
接下来,我们看看如何使用我们编写的代码来制作枪支重新上膛的动画。
为什么我的屏幕看起来和以前一样?
这是个好消息。做了这么多工作之后,我想期待有所不同是很自然的。也许是一些像开枪这样的动画。
事实上,尽管我们删除了枪的图像并用大规模的 sprite sheet 图像替换它,但一切看起来都很正常,这正是我们希望从代码中得到的。
枪已经不在屏幕上了。
由于这是很大一部分代码,下面是一些可能发生的典型编码错误的建议:
- 回顾每一行代码,确保它与书中所写的一致。
- 检查您是否将
}
符号放置在指示的位置。 - 确保
SZ_gun_SS.png
在images
文件夹中。 - 确保在 HTML 文件的头部包含了两个新的 JavaScript 文件(即
ss.js
和SZ_SS.js
)。
这把枪看起来不对劲。
要么是枪看起来比它应该的要大得多,要么看起来像是图像的一部分被切掉了。这意味着枪的精灵表的设置方式有问题;特别是SZ_SS.js
文件中的setup_gun_SS()
函数。请重新检查您的代码,确保所有代码行都完全如所示。
如果你的代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
给我们的枪装子弹
我们需要专注于重新装弹的两个方面:原因和结果。原因是用户点击了屏幕上的重新加载图像。效果是枪从 sprite 表中激活适当的图像。
打开js
文件夹中的SZ_touch.js
文件。当文件打开时,它应该是完全空白的。键入以下几行:
//this function is called to reload our gun
function reloadGun(e) {
//play the reload animation of our SS
$("#SZ0_1").animateSprite("play", "reload");
}
保存并关闭该文件。
当重新访问这个项目时,为重新加载序列提供选项是一个好主意。如果枪是空的,我建议一个较长的序列,如果枪不是空的,我建议一个较短的序列。您需要定义两个 reload 函数,然后在调用 reload 函数之前检查枪的状态。这样,你就是在奖励用户在枪没子弹之前重新装弹!
在测试之前,我们需要将这个文件和函数链接到default.html
文件。重新打开default.html
文件,键入以下新行和对现有行的添加(全部以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<script src="js/ss.js"></script>
<script src="js/SZ_SS.js"></script>
<script src="js/SZ_touch.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)" />
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="imag
es/SZ_reload.png" onmousedown="reloadGun(event)" />
</div>
<div id="SZ0_3" >
<img src="img/SZ_score.png" />
</div>
</div>
</body>
</html>
保存文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。
当屏幕出现时,尝试单击重新加载按钮。你应该看到枪会动。这一次,在第一个动画完成之前,单击几次该按钮。不顺利吧?我们需要解决这个问题,使枪不接受重新加载请求,直到前一个已经完成。
重新打开js
文件夹中的SZ_touch.js
文件。键入以下新行(粗体):
//this function is called to reload our gun
function reloadGun(e) {
//play the reload animation of our SS
$("#SZ0_1").animateSprite("play", "reload");
}
//We need a flag to keep track to avoid repetition of animations before the first has finished
var canIclick= 0;
//this function is called to reload our gun
function reloadGun(e) {
//Let’s check if we can allow this to occur
if(canIclick== 0){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "reload");
}
}
保存并关闭该文件。回到My_Work_Files
文件夹,双击default.html
文件。同样,在第一个动画完成之前,单击几次重新加载按钮。问题已经解决了。
然而,我们现在有另一个问题:游戏只接受一次重新加载请求。我们不能在第一次尝试后让枪重新装弹。这是因为我们没有在代码中的任何地方重置我们的标志。所以让我们现在就开始吧。重新打开SZ_SS.js
文件,键入以下新行(粗体):
//We need a one stop function that will allow us to process sprite sheets
function setup_SpriteSheet(div_name, image_name, no_of_frames, widthx, heightx) {
//need the ratio of the container's width/height
var imageOrgRatio = $(div_name).height() / $(div_name).width();
//need to ensure no trailing decimals
var ratio2 = Math.round(ratio * 10) / 10;
//check that the width is completely divisible by the no of frames
var newDivisible = Math.round((widthx * ratio2) / no_of_frames);
//the new width will be the number of frames multiplied by our new divisible
var newWidthx = newDivisible * no_of_frames;
//also the new height will be our ratio times the height of the div containing our image
var newHeightx = heightx * ratio2;
//apply our new width to our CSS
$(div_name).css('width', (newWidthx));
//apply our new height to our CSS
$(div_name).css('height', newHeightx);
//
//take the image name and apply as a background image to our div
$(div_name).css('background-image', 'url(' + image_name + ')');
//finally we need to apply a background size remembering we need to multiply width by the no of frames
$(div_name).css('background-size', newWidthx * no_of_frames + 'px ' + newHeightx + 'px');
}
//setup the Gun
function setup_gun_SS(){
//first let’s setup our gun SS
setup_SpriteSheet("#SZ0_1","img/SZ_gun_SS.png",28,150,150);
//need to access a special function in our js/ss.js file
$("#SZ0_1").animateSprite({
fps: 10,
animations: {
static: [0],
reload: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],
fire: [24,25,26,27,28],
},
duration: 50,
loop: false,
complete: function () {
// use complete
only when you set animations with 'loop: false'
//alert("animation End");
//we need to reset our universal flag
canIclick=0;
}
});
}
保存并关闭该文件。
当重新访问这个项目时,将我们所有的全局var
存储在一个单独的文件中是一个好主意。我确信您在自己的编程环境中实践了这一点;它确保您的项目在未来是可管理的。如果你创建了一个全局的var
文件,确保在你的 HTML 文件中使用<script>
标签链接它。
回到My_Work_Files
文件夹,双击default.html
文件。
第一个动画完成后,再次单击“重新加载”按钮。这个问题现在应该解决了。
接下来,我们将通过使我们的枪开火来完成这一章。
为什么枪在第一次试装后就停止了装弹?
在我们告诉我们的代码运行reload
命令之前,我们询问它我们的标志(即canIclick
)是否设置为 0。当程序启动时,我们将canIclick
初始化为 0。一旦通过测试,代码做的第一件事就是将canIclick
设置为 1。
下一次按下 Reload 按钮时,当询问canIclick
是否为 0 时,它返回负值。因此,理想情况下,我们希望在重新装填枪支的动画完成后,将canIclick
重置回 0。我们在animate
命令的一个特殊子功能中实现了这一点。该函数特别询问动画结束后是否有任何特殊指令需要执行。
回想一下我们讨论onmousemove
事件时,我们使用了以下内容:
<img src="img/SZ_reload.png" onmousedown="reloadGun(event)" /> -
顾名思义,每当图像被点击时,它就调用reloadGun
函数。下面是我们可以使用的事件函数的简短列表。
- 当鼠标移动到一个元素上时,这个事件发生。
- 当鼠标移出一个元素时,这个事件发生。
- 当鼠标移动到一个元素或它的一个子元素上时,这个事件发生。
- 当用户将鼠标指针移出元素或其子元素时,会发生此事件。
- 当用户在一个元素上释放鼠标按钮时,这个事件发生。
开枪吧
如你所料,让我们的枪开火的方法和我们给枪装子弹的方法非常相似。首先,我们需要注册请求开枪的用户。然后,我们需要使枪有生命。
重新打开js
文件夹中的SZ_touch.js
文件。键入以下新行(粗体):
//We need a flag to keep track to avoid repetition of animations before the first has finished
var canIclick= 0;
//this function is called to reload our gun
function reloadGun(e) {
//Let’s check if we can allow this to occur
if(canIclick== 0){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "reload");
}
}
//this function is called to fire our gun
function fireGun(e) {
//Let’s check if we can allow this to occur
if(canIclick== 0){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "fire");
}
}
保存文件并关闭它。
将来,在允许用户开枪之前,你可能会检查更多的变量(例如,如果屏幕暂停,或者在一关结束时)。此时,创建一个函数来检查所有参数值,然后输出结果决策是一个好主意。然后,在决定是否继续时,该输出将由其他功能(如是否可能暂停)和fireGun()
功能进行全面检查。
在测试之前,我们需要将函数添加到default.html
文件中。重新打开default.html
文件。键入以下新行(粗体)和对现有行的添加(修改后的文本为红色):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<script src="js/ss.js"></script>
<script src="js/SZ_SS.js"></script>
<script src="js/SZ_touch.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)"onmousedown="fireGun(event)" />
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="img/SZ_reload.png" onmousedown="reloadGun(event)" />
</div>
<div id="SZ0_3" >
<img src="img/SZ_score.png" />
</div>
</div>
</body>
</html>
保存文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。现在点击屏幕上的任何地方。枪应该激活射击序列。
到现在为止,枪应该在做以下事情:
- 响应屏幕上的鼠标而移动
- 当用户单击重新加载按钮时重新加载
- 当用户点击屏幕上的任何地方时触发
在本节中,我们使用以下代码行调用了一组特定的 sprite 动画:
$("#SZ0_1").animateSprite("play", "fire");
系统如何知道如何处理"fire"
?如果你回到代码,注意我们写了如下内容:
animations: {
static: [0],
reload: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],
fire: [24,25,26,27,28],
},
我们定义的一个动画叫做"fire"
,它是 24 到 28 张单独的精灵图片。
请注意,我们使用以下代码将两个鼠标事件插入到一个图像标记中:
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)" onmousedown="fireGun(event)" />
这是完全可能的,因为(a)您可以为一个元素定义多个鼠标事件,并且(b)这两个鼠标事件不会相互冲突。
最后一件事…
您可能已经注意到,当您单击重新加载按钮时,光标会保持为十字准线。如果它能变回一个更合适的光标就好了,这样更直观,也有助于更好的游戏体验。
我们可以通过改变 CSS 来做到这一点。您现在需要重新打开SZ_master.css
文件,并键入以下新行(粗体):
html {
height: 100%;
}
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
cursor: crosshair;
}
img {
max-width: 100%;
height: auto;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#SZ0_0 {
position: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
}
#SZ0_1 {
position: fixed;
bottom: 0;
right: 0;
}
#SZ0_2 {
position: fixed;
top: 0;
left: 0;
cursor: pointer;
}
#SZ0_3 {
position: fixed;
top: 0;
right: 0;
}
保存文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。
现在,当你将光标移到重载按钮上时,它会立即变成一个普通的“手”图像。类似地,当您将鼠标从重新加载按钮移开时,它应该会变回光标图像。
下一章将僵尸引入我们的游戏,这最终给了我们的玩家一些互动性。
光标在重载按钮上时不会变成指针?此错误可能来自 HTML 文件。首先,检查您是否插入了这一行
cursor: pointer;
在#SZ0_2
部分。
如果您已经这样做了,那么我们需要看一下default.html
文件。确保您已经按照指示将所有图像标签替换为div
标签。
比如,以前是什么
<img id="SZ0_1" src="img/SZ_gun.png" />
<img id="SZ0_2" src="img/SZ_reload.png" />
<img id="SZ0_3" src="img/SZ_score.png" />
现在应该是
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="img/SZ_reload.png" onmousedown="reloadGun(event)" />
</div>
<div id="SZ0_3" >
<img src="img/SZ_score.png" />
</div>
如果你的代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
六、僵尸在哪里?
“用代码行来衡量编程进度就像用重量来衡量飞机制造进度一样。”
比尔盖茨
让我们回顾一下我们的僵尸在游戏中需要做什么。我们需要六个走向屏幕的僵尸。每个僵尸都有一个精灵表和它的行走动画。当僵尸到达其动画的结尾时,它需要重置到原始位置。
创造僵尸:第一部分
首先,我们需要将以下四个 sprite 工作表添加到您的image
文件夹中:
zombiesSS_1.png
:僵尸行走的科学家zombiesSS_2.png
:行走的女丧尸zombiesSS_3.png
:行尸走肉男SZ_bubble.png
:被困在泡泡里的三只僵尸
转到My_Work_Files
文件夹的Raw Images
文件夹中的images
文件夹。找到名为zombiesSS_1.png
、zombiesSS_2.png
、zombiesSS_3.png
和SZ_bubble.png
的文件,并将这些文件复制到Images
文件夹中,现在看起来应该是这样的:
创造僵尸:第二部分
在这一节的结尾,你会看到一个僵尸在我们星球的边缘。为此,我们需要从头开始编写僵尸代码。再次,我提前道歉,因为将有相当多的编码。然而,看到自己的僵尸出现在屏幕上的兴奋感值得所有的努力。
打开SZ_zombie_movement.js
文件,里面应该是完全空白的。键入以下几行:
//let’s create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
}
您现在可以保存并关闭该文件。
我们在准则中引入了一些新的元素,在“更多信息”一节中有详细介绍。
在我们进一步编码之前,我们需要将这个新文件链接到我们的default.html
文件。重新打开default.html
文件,键入以下新行,以及现有行中的额外文本(所有新文本都以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<script src="js/ss.js"></script>
<script src="js/SZ_SS.js"></script>
<script src="js/SZ_touch.js"></script>
<script src="js/SZ_zombie_movement.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)" onmousedown="fireGun(event)" />
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="img/SZ_reload.png" onmousedown="reloadGun(event)" />
</div>
<div id="SZ0_3" >
<img src="img/SZ_score.png" />
</div>
</div>
</body>
</html>
保存文件,然后关闭它。现在,我们可以继续进一步发展我们的僵尸雪碧表。重新打开js
文件夹中的SZ_SS
文件。
键入以下新行(所有新文本都以粗体显示):
//We need a one stop function that will allow us to process sprite sheets
function setup_SpriteSheet(div_name, image_name, no_of_frames, widthx, heightx) {
//need the ratio of the container's width/height
var imageOrgRatio = $(div_name).height() / $(div_name).width() ;
//need to ensure no trailing decimals
var ratio2 = Math.round(ratio * 10) / 10;
//check that the width is completely divisible by the no of frames
var newDivisible = Math.round((widthx * ratio2) / no_of_frames);
//the new width will be the number of frames multiplied by our new divisible
var newWidthx = newDivisible * no_of_frames;
//also the new height will be our ratio times the height of the div containing our image
var newHeightx = heightx * ratio2;
//apply our new width to our CSS
$(div_name).css('width', (newWidthx));
//apply our new height to our CSS
$(div_name).css('height', newHeightx);
//
//take the image name and apply as a background image to our div
$(div_name).css('background-image', 'url(' + image_name + ')');
//finally we need to apply a
background size remembering we need to multiply width by the no of frames
$(div_name).css('background-size', newWidthx * no_of_frames + 'px ' + newHeightx + 'px');
}
//setup the Gun
function setup_gun_SS(){
//first let’s setup our gun SS
setup_SpriteSheet("#SZ0_1","img/SZ_gun_SS.png",28,150,150);
//need to access a special function in our js/ss.js file
$("#SZ0_1").animateSprite({
fps: 10,
animations: {
static: [0],
reload: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],
fire: [24,25,26,27,28],
},
duration: 50,
loop: false,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
//we need to reset our universal flag
canIclick=0;
}
});
}
//setup a newly
created
zombie
function setup_zombie_SS(whichOne){
//let’s identify what type of zombie we should create
var type_zombie = [1,2,3,1,2,3];
//let’s setup a speed for each type of zombie
var speed_zombie = [100,50,150];
//first let’s setup our zombie SS
setup_SpriteSheet("#zombie"+whichOne,"img/zombiesSS_"+type_zombie[whichOne-1]+".png",9,20,20);
//need to access a special function in our js/ss.js file
$("#zombie"+whichOne).animateSprite({
fps: 10,
animations: {
static: [0,1,2,3,4,5,6,7],
},
duration: speed_zombie[type_zombie[whichOne-1]-1],
loop: true,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
}
});
}
保存文件,然后关闭它。
您会注意到,我们一直在重复代码,以便在各种 JavaScript 文件中设置一个 sprite 表。我这样做是为了保持代码线性流动;但是,您可以决定为所有 sprite 工作表操作创建一个文件。
我们现在需要在安装文件中调用这个函数来创建一个僵尸。重新打开js
文件夹中的SZ_setupContent
文件,并键入以下新行(所有新文本均以粗体显示):
//main function
function main_call_setupContent() {
//need to resize all elements
//first we set their normal sizes in CSS
//Gun
$('#SZ0_1').css('width', 150 * ratio);
$('#SZ0_1').css('height', 150 * ratio);
//Reload Button
$('#SZ0_2').css('width', 200 * ratio);
$('#SZ0_2').css('height', 90 * ratio);
//Score
$('#SZ0_3').css('width', 235 * ratio);
$('#SZ0_3').css('height', 100 * ratio);
//Any sprite sheets?
//Our Gun
setup_gun_SS();
//Create a zombie
SZ_createZombie(1);
}
我们现在准备测试!保存所有文件,然后关闭它们。回到你的My_Work_Files
文件夹,双击default.html
文件。你应该看到的是行星表面边缘的科学家僵尸。
如果你点击浏览器的刷新按钮(或者,你可以按 F5),僵尸应该出现在一个不同的位置(但仍然在行星表面的边缘)。继续刷新几次,测试这种行为。
我们现在已经设法在我们的游戏中产生一个僵尸!我们的下一步将是让我们的僵尸向我们走来。
没用吗?以下是需要检查的几个方面:
- 检查您是否在
default.html
中正确链接了SZ_zombie_movement.js
文件。 - 我们第一次使用数组(参见“更多信息”一节关于什么是数组)。确保您使用的是键盘上 P 键旁边的方括号。
- 最后,确保下面一行代码完全如所示:
setup_SpriteSheet("#zombie"+whichOne,"img/zombiesSS_"+type_zombie[whichOne-1]+".png",9,20,20);
如果你的代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
这一部分有三个令人兴奋的特点。让我们更深入地探索一下。
- 动态创建一个
div
。动态是什么意思?意思是僵尸的div
是游戏运行时产生的;也就是说,我们没有在default.html
文件中为僵尸编写一个div
,因为我们在那里有所有其他的div
,这样做的主要原因是通过调用一个函数来生成多个僵尸,而不是手动写出每个div
。 - 数组。如果你在阅读任何计算机语言的传统编码书籍,你会在第一天就接触到数组。然而,我认为现在学习更好,因为你刚刚实际使用了一个,因此你能够更好地理解解释。让我们快速看一下我们的一个数组。我们有一个由六个整数组成的数组,以数字 1 开始,以数字 3 结束。
var
将数组声明为新元素。type_zombie
是数组的名称。[ ]
这些括号中的任何内容都是数组的内容,用逗号分隔
我们在编码中使用了几个数学函数。让我们来看看其中的一些。
Math.random()
是一个生成随机数的特殊 JavaScript 函数。这个随机数被用来(通过一些操作)随机放置我们的僵尸。Math.floor()
是一个基本上向下舍入数字的函数;例如,45.89 将返回 45。顺便说一下,这个函数的反函数(即上舍入)是Math.ceil()
,所以 45.89 将返回 46。
将僵尸移近
为了让僵尸靠近我们,我们将使用 JavaScript。代码将同时做两个动画。首先,它会把僵尸拉下屏幕。第二,僵尸将被缩放,看起来更大。通过一起做这两个动画,我们给出了僵尸向我们走来的错觉。
打开SZ_zombie_movement.js
文件,输入以下新行(所有新文本以粗体显示):
//let’s create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
}
//let’s animate
our
zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.animate({
//first bring our zombie slowly down the screen
left: amty+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//apply the scale
$(this).css('transform','scale('+xx+')');
}
}, complete: function () {
}
});
}
保存此文件,然后关闭它。回到My_Work_Files
文件夹,双击default.html
文件。
当屏幕出现时,你应该看到僵尸向你走来!根据你的屏幕分辨率,僵尸可能会越过边缘或在它应该停下来之前停下来。不要担心那个;我们将在第八章处理。
接下来,让我们来看看创造游戏所需的所有僵尸。
下面的数组有文本值,但是它们是什么呢?
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
这些值就是我们用于缓动函数的值。缓动函数指定僵尸随时间变化的速率。最简单和最广泛使用的缓动值是线性缓动值。这是僵尸行走期间以恒定速度移动的地方。然而,那会有点无聊和不现实。
我们有多种放松功能可供选择。以下是我们将使用的方法:
-
For our scientist zombie,
easeInSine
. It starts quite slowly and then accelerates for the rest of the journey. Here is a graph depicting the function: -
For our female zombie,
easeOutQuart
. There is no delay at the start; she begins abruptly and eases off near the end. Here is a graph depicting the function: -
For our male zombie,
easeInOutQuad
. There is a delay both at the start and at the finish. The midway section is fairly average as well. Here is a graph depicting the function:
创造所有的僵尸
我们将为我们的游戏创造六个僵尸。我们可以想创造多少就创造多少;但是,我们在创建游戏时需要考虑内存问题。如果我们创造了太多的僵尸,游戏可能会耗尽电脑内存。另一方面,如果我们创造的太少,那么游戏可能就不够有挑战性。从本质上来说,这一切都是为了找到游戏和玩家的完美参数。
打开js
文件夹中的SZ_setupContent.js
文件。首先,找到以下两行并删除它们:
//Create a zombie
SZ_createZombie(1);
键入以下新行(所有新文本都以粗体显示):
//main function
function main_call_setupContent() {
//need to resize all elements
//first we set their normal sizes in CSS
//Gun
$('#SZ0_1').css('width', 150 * ratio);
$('#SZ0_1').css('height', 150 * ratio);
//Reload Button
$('#SZ0_2').css('width', 200 * ratio);
$('#SZ0_2').css('height', 90 * ratio);
//Score
$('#SZ0_3').css('width', 235 * ratio);
$('#SZ0_3').css('height', 100 * ratio);
//Any sprite sheets?
//Our Gun
setup_gun_SS();
//Create all our 6 zombies
for (i = 1; i < 7; i++) {
//this will get called 6 times
SZ_createZombie(i);
}
}
保存此文件,然后关闭它。
出于本书的目的,我们决定将僵尸的最大数量保持在六个。然而,为了保证这个函数不会过时,我建议重新访问它,以接受最大僵尸数量作为参数。您可以用这个参数名替换for loop
中的 7。
回到My_Work_Files
文件夹,双击default.html
文件。
当屏幕出现时,你应该会看到所有六个僵尸向屏幕走去。你不仅会看到科学家僵尸,还会看到女性和男性僵尸。我应该注意到他们的速度是相对于我们在第一章中讨论的。
我没什么以下顾虑:
- 僵尸互相重叠。
- 枪藏在丧尸后面。
- 鼠标光标在僵尸上时不会变成十字准线。
我在下面的截图中说明了前两点:
在这个阶段不要太担心这些问题。所有这些问题都在第八章中解决。接下来,我们将着眼于回收我们的僵尸的生命,以便一旦它完成,该元素准备好为我们的程序再次使用。
在这一节中,我们遇到了一个for loop
来创造我们的六个僵尸:
for (i = 1; i < 7; i++) {
通过使用这个循环,我们消除了六次编写相同代码来创建僵尸的需要。如果是这样的话,那么想象一下,如果我们要创造 100 个僵尸!
如示例所示,如果您想用不同的值反复运行相同的代码,循环是必不可少的。
这里有四种不同的循环,我们可以在游戏中使用:
- 多次循环通过一个代码块
for/in
遍历对象的属性- 当指定的条件为真时,循环通过代码块
- 当指定的条件为真时,
do/
while
也循环通过代码块
生成僵尸生命周期
正如你所看到的,僵尸一旦到达屏幕,就一直留在屏幕上。最终,如果有僵尸出现在屏幕上,我们会想要结束游戏。现在,我们很乐意让他们回到起点。
打开SZ_zombie_movement.js
文件,输入以下新行(所有新文本以粗体显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie
(whichOne);
}
//let’s animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly
name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+1+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
$(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
}
}
}, complete: function () {
}
});
}
//a function to
comp
letely reset our zombie
function SZ_resetZombie(whichOne){
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px'});
//finally let’s make the zombie come towards the screen again
SZ_animateZombie(whichOne);
}
保存此文件,然后关闭它。
同样,重置僵尸的功能需要面向未来。我建议将僵尸起始位置的所有可能参数放在一个单独的文件中。在此函数中,我们指示代码访问新文件,以获取具有不同方差的可能参数的指令(例如,如果用户处于更高级别,则值可能不同)。
回到My_Work_Files
文件夹,双击default.html
文件。
当屏幕出现时,你应该看到僵尸像以前一样出现在屏幕上,但随后消失了。当它们再次出现时,它们应该出现在不同的位置。
在下一章,我们来看看如何射击我们的僵尸。
没用吗?这可能是由于我们在一些现有代码周围添加了几行代码。让我们看看。
最初的代码行是
$zombiex. animate({
确保新代码行如下所示:
$zombiex.delay(timex[whichOne-1]/2).animate({
最初的代码行是
//apply the scale
$(this).css('transform','scale('+xx+')');
确保新代码行如下所示:
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
$(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
}
}
如果你的代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
七、尝试一下:第二部分
"If you do it right, it will last forever"
最大葡萄园
好消息是我们已经接近尾声了。稍微坏一点的消息是,这一章将会有相当多的编码。那么在这一章的结尾我们会看到什么呢?
- 这把枪将能够向僵尸开火。
- 僵尸会记录点击次数。如果达到最大命中数,僵尸就会变成泡泡。
- 泡泡僵尸会飞向远方。
- 我们需要记录枪发射的次数,并要求用户在达到最大次数时重新装弹。
- 最后,如果僵尸出现在屏幕上,我们需要宣布游戏结束。
打僵尸
你可能已经注意到,当你试图点击一个僵尸,枪不发射。这是因为我们没有将鼠标点击事件绑定到僵尸元素。我们可以将这个鼠标点击代码放在创建每个僵尸的函数中。
打开SZ_zombie_movement.js
文件,输入以下新行(所有新文本以粗体显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//first we want to fire the gun
fireGun(event);
});
}
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+1+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
$(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
}
}
}, complete: function () {
}
});
}
//a function to completely reset our zombie
function SZ_resetZombie(whichOne){
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px'});
//finally let's make the zombie come towards the screen again
SZ_animateZombie(whichOne);
}
导航到菜单,单击文件,然后单击保存。您现在可以关闭该文件了。
我们现在准备测试!回到My_Work_Files
文件夹,双击default.html
文件。当你点击任何向屏幕走来的僵尸时,你应该会看到枪在开火。
这是通过给每个僵尸的div
添加'mousedown touchstart'
事件实现的。当再次访问这个项目时,您可能希望考虑安装一个“头像”类型的功能。为此,您需要执行以下操作:
- 在定义僵尸头部的
div
中放置一个div
。 - 将相同的
'mousedown touchstart'
事件放置到这个新的div
中,这将覆盖外部div
的功能。 - 如果这个新的 div
is
成功了,那么给用户更多的分数。 - 如果击中了
div
,您可能要考虑奖励最大命中数;例如,如果通常需要三次击中才能杀死一个僵尸(这个功能将在下一节中添加),那么只需一次爆头就足够了。
在前面的章节中,我们通过将鼠标事件添加到图像标签来绑定它们;例如
<img id="SZ0_0" onmousemove="rotateGun(event)" src="img/SZ_background_image.jpg" />
然而,在本节中,我们需要向僵尸添加一个鼠标事件,它不是在我们的 HTML 页面中创建的。
因此,正如我们动态创建僵尸一样,我们也必须在运行时将事件绑定到它们。这是通过使用 JavaScript 代码完成的,如下所示:
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
在这一行中,我们不仅将mousedown
事件绑定到每个僵尸,还定义了事件发生时执行的指令。
代码不起作用吗?检查你是否在你写的新代码中输入了whichOne
(确保O
是大写字母)。
当编写本书中的任何代码时,非常重要的一点是要意识到 JavaScript 在大写字母和小写字母之间有明显的区别。
JavaScript 不认为名为whichone
的变量与名为whichOne
的变量相同。
如果代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
让点击率发挥作用
让我们回顾一下每个僵尸在“死亡”前能够承受的打击次数
- 教授 Z:两次击中
- 颠茄:一击
- 布拉德:三支安打
为了记录每个僵尸被击中的次数,我们需要使用一个数组。此外,我们需要记住重置每个僵尸的命中计数时,它重置。
打开SZ_zombie_movement.js
文件,输入以下新行(所有新文本以粗体显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
zombieHit(whichOne-1);
});
}
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+1+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
// $(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
}
}
}, complete: function () {
}
});
}
//a function to completely reset our zombie
function SZ_resetZombie(whichOne){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies
animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px'});
//finally let's make the zombie come towards the screen again
SZ_animateZombie(whichOne);
}
保存并关闭该文件。
如果你将来要开发这个游戏的关卡,你想在重置僵尸之前添加一个检查。例如,如果杀死一个僵尸的结果是一个新的水平,那么不只是重置特定的僵尸,你想重置所有的僵尸。我甚至会让这个操作成为自己的功能,执行所有这些检查。
打开js
文件夹中的SZ_touch.js
文件。键入以下新行(所有新文本都以粗体显示):
//We need a flag to keep track to avoid repetition of animations before the first has finished
var canIclick= 0;
//this function is called to reload our gun
function reloadGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "reload");
}
}
//this function is called to fire our gun
function fireGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "fire");
}
}
//array to keep track of the zombie hits
var zombieHits_counter = [0,0,0,0,0,0];
//array for each zombies limit
var zombieHits_limits = [2,1,3,2,1,3];
//this function will keep track of the zombie hits and act accordingly
function zombieHit(whichOne){
//increment the counter
zombieHits_counter[whichOne]++;
//check to see if this zombie has reached its limit
if(zombieHits_counter[whichOne] >= zombieHits_limits[whichOne]){
//reset this zombie
SZ_resetZombie(whichOne+1);
}
}
保存并关闭该文件。我们现在准备测试!回到My_Work_Files
文件夹,双击default.html
文件。
现在,当我们对僵尸开火正确的次数后,僵尸在到达屏幕前应该会重置。所以如果我们向女僵尸开一枪,她应该会立刻复位。同样的,如果我们向男僵尸开三枪,他应该会复位。最后,如果我们向科学家僵尸开火两次,那么他应该会重置。
接下来,我介绍一下我们的泡泡僵尸。
我们使用了一些技术来跟踪和比较代码中的值。让我们仔细看看其中的一些。
var ZS_ease = zombieHits_counter[whichOne]++;
++
是一个赋值运算符,将变量的当前值加 1。
接下来,让我们看看如何检查是否达到了最大命中数。
if(zombieHits_counter[whichOne] >= zombieHits_limits[whichOne]){
一个if
语句就是我们所说的条件语句。当您希望对不同的决策执行不同的操作时,可以使用条件语句。所以在这种情况下,如果僵尸已经有了最大的命中次数,那么我们要重置它;否则,什么都不做。
你会在语句中看到>=
,这意味着如果第一个值大于或等于第二个值。以下是我们可以用于其他情况的一些其他条件语句:
<=
(小于或等于)==
(等于)<
(小于)>
(大于)!=
(不等于)
僵尸倒下了!
当僵尸被击中的次数达到最大值时,它会被重置。我们也需要僵尸出现在一个泡泡中,然而,给人一种僵尸已经在游戏中被制服和处理的错觉。
为此,需要完成以下工作。
- 创建六个泡沫僵尸元素,准备在需要时部署。
- 在重置僵尸之前,激活它的反泡沫僵尸。
- 确保泡沫僵尸具有相同的比例和位置值,使其看起来好像行走的僵尸已转化为泡沫。
- 最后,我们希望泡沫僵尸飘向太空。
第一部分:创建六个泡沫僵尸元素
为了创建六个泡泡僵尸,我们需要打开SZ_zombie_movement.js
文件并键入以下新行(所有新文本都以粗体显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//finally add in our bubble zombie to the screen too
document.body.appendChild(div2);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
zombieHit(whichOne-1);
});
}
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+1+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
// $(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
}
}
}, complete: function () {
}
});
}
//a function to completely reset our zombie
function SZ_resetZombie(whichOne){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px'});
//finally let's make the zombie come towards the screen again
SZ_animateZombie(whichOne);
}
保存并关闭该文件。
appendChild()
是这段代码中使用的一个有趣的方法。此方法将一个节点作为另一个节点的最后一个子节点追加。所以在我们的例子中,我们将僵尸的div
添加到 HTML 主体的末尾。
如果在未来,你需要添加一个元素,但不是作为最后一个孩子。你不妨用insertBefore()
而不是appendChild()
。insertBefore()
方法将一个节点作为子节点插入,就在您指定的现有子节点之前。
在我们测试新创建的泡泡僵尸之前,我们需要添加他们的精灵表功能。
为此,我们现在需要打开js
文件夹中的SZ_SS.js
文件。键入以下新行(所有新文本都以粗体显示):
//We need a one stop function that will allow us to process sprite sheets
function setup_SpriteSheet(div_name, image_name, no_of_frames, widthx, heightx) {
//need the ratio of the container's width/height
var imageOrgRatio = $(div_name).height() / $(div_name).width() ;
//need to ensure no trailing decimals
var ratio2 = Math.round(ratio * 10) / 10;
//check that the width is completely divisible by the no of frames
var newDivisible = Math.round((widthx * ratio2) / no_of_frames);
//the new width will be the number of frames multiplied by our new divisible
var newWidthx = newDivisible * no_of_frames;
//also the new height will be our ratio times the height of the div containing our image
var newHeightx = heightx * ratio2;
//apply our new width to our CSS
$(div_name).css('width', (newWidthx));
//apply our new height to our CSS
$(div_name).css('height', newHeightx);
//
//take the image name and apply as a background image to our div
$(div_name).css('background-image', 'url(' + image_name + ')');
//finally we need to apply a background size remembering we need to multiply width by the no of frames
$(div_name).css('background-size', newWidthx * no_of_frames + 'px ' + newHeightx + 'px');
}
//setup the Gun
function setup_gun_SS(){
//first let's apply our gun to our SS function
setup_SpriteSheet("#SZ0_1","img/SZ_gun_SS.png",28,150,150);
//need to access a special function in our js/ss.js file
$("#SZ0_1").animateSprite({
fps: 10,
animations: {
static: [0],
reload: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],
fire: [24,25,26,27,28],
},
duration: 50,
loop: false,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
//we need to reset our universal flag
canIclick=0;
}
});
}
//setup a newly created zombie
function setup_zombie_SS(whichOne){
//let's identify what type of zombie we should create
var type_zombie = [1,2,3,1,2,3];
//let's setup a speed for each type of zombie
var speed_zombie = [100,50,150];
//first let's setup our zombie SS
setup_SpriteSheet("#zombie"+whichOne,"img/zombiesSS_"+type_zombie[whichOne-1]+".png",9,20,20);
//need to access a special function in our js/ss.js file
$("#zombie"+whichOne).animateSprite({
fps: 10,
animations: {
static: [0,1,2,3,4,5,6,7],
},
duration: speed_zombie[type_zombie[whichOne-1]-1],
loop: true,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
}
});
//now let’s setup our bubble zombie SS
setup_SpriteSheet("#bubble_zombie"+whichOne,"img/SZ_bubble.png",3,20,20);
//need to access a special function in our js/ss.js file
$("#bubble_zombie"+whichOne).animateSprite({
fps: 10,
animations: {
z1: [type_zombie[whichOne-1]-1],
},
duration: 1,
loop: false,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
}
});
}
保存并关闭该文件。
尽管我们之前已经提到过这一点,但是当试图找出代码中可能存在的问题时,使用alert()
命令是非常有用的。在前面的代码中,您会看到一个带注释的alert()
语句。在某个时候,我可能在这个函数的完成函数触发方面遇到了一些问题。通过放置这个alert()
语句,我能够测试函数的完成时间是否准确。
像往常一样,记住删除(或注释掉)所有的alert()
语句。
现在我们准备测试!回到My_Work_Files
文件夹,双击default.html
文件。
在僵尸冲向屏幕之前,你应该先看到六个随机放置在星球边缘的泡泡僵尸。
代码不起作用吗?检查你是否在你写在SZ_zombie_movement.js
中的新代码中输入了div2
。
同样在SZ_SS.js
的新代码中,确保你已经输入了bubble_zombie
而不仅仅是zombie
(按照前面的代码)。
如果代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
第二部分:激活反泡沫僵尸
在这一节中,当达到最大命中次数时,我们将集中精力用新的泡沫僵尸替换行走僵尸。此外,我们必须确保相应的泡沫僵尸不会显示,直到达到最大点击次数。
首先,打开js
文件夹中的SZ_touch.js
文件。修改以下粗体行:
//We need a flag to keep track to avoid repetition of animations before the first has finished
var canIclick= 0;
//this function is called to reload our gun
function reloadGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "reload");
}
}
//this function is called to fire our gun
function fireGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "fire");
}
}
//array to keep track of the zombie hits
var zombieHits_counter = [0,0,0,0,0,0];
//array for each zombies limit
var zombieHits_limits = [2,1,3,2,1,3];
//this function will keep track of the zombie hits and act accordingly
function zombieHit(whichOne){
//increment the counter
zombieHits_counter[whichOne]++;
//check to see if this zombie has reached its limit
if(zombieHits_counter[whichOne] >= zombieHits_limits[whichOne]){
//reset this zombie
SZ_resetZombie(whichOne+1,1);
}
}
保存并关闭该文件。打开SZ_zombie_movement.js
文件。仔细修改一些旧行,并键入以下新行(所有修改的和新的文本都以粗体显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//finally add in our bubble zombie to the screen too
document.body.appendChild(div2);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
//hide the bubble zombies at the start
$("#bubble_zombie"+whichOne).css('transform','scale('+0+')');
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
zombieHit(whichOne-1);
});
}
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+0.001+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
// $(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne,0);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
//record this new scale value
scalex_zombie[whichOne-1]=xx;
}
}
}, complete: function () {
}
});
}
//a function to completely reset our zombie
function SZ_resetZombie(whichOne, zombieBubble_generate){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//should we generate a bubble zombie?
if(zombieBubble_generate==1){
//assign a user friendly name for our bubble zombie div
var $bubble_zombiex = $("#bubble_zombie"+whichOne);
//let's re-position our bubble zombie to our stored value
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left")});
//apply the scale $bubble_zombiex.css('transform','scale('+scalex_zombie[whichOne-1]+')');
}
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px'});
//finally let's make the zombie come towards the screen again
SZ_animateZombie(whichOne);
}
您现在可以保存并关闭该文件。还有更多的代码要写,但是,让我们快速测试一下目前为止我们有什么。回到My_Work_Files
文件夹,双击default.html
文件。您应该观察到我们游戏中的以下变化:
- 游戏开始的时候,远处的泡泡僵尸已经不见了。
- 当你对一个僵尸达到最大命中数时,它会被一个泡泡僵尸取代。
- 泡泡僵尸应该与你刚刚击中的僵尸相对应。
- 泡沫僵尸应该与它要替换的僵尸的大小和位置大致相同。
- 请注意,你可能会注意到,僵尸可能会停止一起。这是为了我们后面要写的一些代码而特意做的。现在,只要刷新你的浏览器(按 F5),游戏应该会重新启动。
接下来,我们让泡泡僵尸进入太空。
代码不起作用吗?首先,检查您是否已经修改了SZ_resetZombie
函数以包含一个额外的参数(这个代码是红色的):
function SZ_resetZombie(whichOne, zombieBubble_generate){
同样,我们调用这个函数的两次需要修改。请确保您的调用类似于以下代码:
在SZ_touch.js
SZ_resetZombie(whichOne+1,1);
而在SZ_zombie_movement.js
SZ_resetZombie(whichOne,0);
最后,确保以下代码行完全按照所示放置(即,在函数调用之外):
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
如果代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
第三部分:动画的泡沫僵尸
在最后一节中,我们将为每个气泡僵尸设置动画,这又会触发其对应的僵尸再次开始向屏幕播放动画。然后我们为泡泡僵尸提供了一个复位功能,让它可以再次安全使用。
首先,打开SZ_movement.js
文件,键入以下新行(所有新文本以粗体显示):
function rotateGun(e) {
//using the e value we can deduce the X co-ordinates
var xPos = e.clientX;
//We need to work out where the mouse cursor is as a percentage of the width of the screen
//We will work this out by dividing the current X position by the overall screen width which if you remember we put in newWidth
var currentXPositionPercentage = xPos/newWidth;
//We now want to apply this to the maximum amount of rotation which is 50 however the starting rotation is -15 not 0
var amountToRotate = -15 + (currentXPositionPercentage * 50);
//Let's rotate the gun!
$("#SZ0_1").css('transform', 'rotate('+amountToRotate+'deg)');
}
//movement for our bubble zombie
function bubbleZombie_flyAway(whichOne){
//assign a user friendly name for our div
var $zombiex = $("#bubble_zombie"+whichOne);
//first it should animate upwards with a
bounce
$zombiex.animate({
//bring our zombie up the screen
top: "-="+100*ratio+ "px",
},{ easing:"easeOutElastic", duration: 400,
complete: function () {
//now the final animation where the bubble zombie disappears into
space
$(this).delay(150).animate({
//slowly turn the alpha down
opacity: "-="+1,
},{ easing:"easeOutQuint", duration: 1000,
step: function(now, fx){
//at each step we can adjust the scale to make it look smaller
if (fx.prop == "opacity" && fx.pos>=0.1) {
//work out the amount to scale
var xx = 0.5/(fx.pos);
//apply the scale
$(this).css('transform','scale('+xx+')');
}
}, complete: function () {
}//end of second complete
function
});//end of second animation
}//end of first complete function
}); //end of first animation
}
保存并关闭该文件。
In the code above we have written
$(this).delay(150).animate({
这通常会被写成
$(this).animate({
然而,顾名思义,我们在调用这个函数之前应用了一个延迟。jQuery
函数设置一个定时器来延迟队列中项目的执行。它接受一个整数作为参数,指示延迟执行的毫秒数。所以在我们的例子中,我们要求代码在执行我们的animate
函数之前等待 150 毫秒。
我想补充一点,这个delay()
方法只对某些游戏引擎是最好的,在这些引擎中,你在排队的 jQuery 效果之间有延迟。它没有提供取消延迟的方法;因此,在某些情况下,delay()
并不能替代 JavaScript 的原生setTimeout
函数,后者可能更合适。
接下来,我们需要调用我们的新函数。打开js
文件夹中的SZ_zombie_movement.js
文件。仔细修改一些旧行,并键入以下新行(所有修改的和新的文本都以粗体显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//finally add in our bubble zombie to the screen too
document.body.appendChild(div2);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
//hide the bubble zombies at the start
$("#bubble_zombie"+whichOne).css('transform','scale('+0+')');
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the zombie is currently walking
if($("#zombie"+whichOne).css('opacity') != 0) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
zombieHit(whichOne-1);
}
});
}
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//reset the zombies opacity
$zombiex.css({opacity:1});
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+0.001+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
// $(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne,0);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
//record this new scale value
scalex_zombie[whichOne-1]=xx;
}
}
}, complete: function () {
}
});
}
//a function to completely reset our zombie
function SZ_resetZombie(whichOne, zombieBubble_generate){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//should we generate a bubble zombie?
if(zombieBubble_generate==1){
//assign a user friendly name for our bubble zombie div
var $bubble_zombiex = $("#bubble_zombie"+whichOne);
//let's re-position our bubble zombie to our stored value
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left"), opacity:1});
//apply the scale
$bubble_zombiex.css('transform','scale('+scalex_zombie[whichOne-1]+')');
//call our bubble zombie animation function
bubbleZombie_flyAway(whichOne);
}
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px', opacity:0});
//finally let's make the zombie come towards the screen again
//SZ_animateZombie(whichOne);
}
您现在可以保存并关闭该文件。我们现在准备测试我们的代码。回到My_Work_Files
文件夹,双击default.html
文件。你现在应该可以看到泡泡僵尸向上移动,然后向远处移动。
再次,请注意,你可能会注意到,僵尸可能会停止一起。这是为了我们稍后要写的一些代码而特意做的。现在,只要刷新你的浏览器(按 F5),游戏应该会重新启动。
接下来,我们将只允许我们的枪在需要重新装弹之前发射一定数量的子弹。
代码不起作用吗?首先,检查您是否已经修改了SZ_resetZombie
函数,以包含一个额外的参数(红色代码):
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left"), opacity:1});
和
$zombiex.css({top: top_position+'px', left: left_position+'px', opacity:0});
另外,SZ_movement.js 文件中代码的末尾有一些重复的字符。请确保它们都按此处所示进行编写:
}
}, complete: function () {
}//end of second complete function
});//end of second animation
}//end of first complete function
}); //end of first animation
如果代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti.
是什么原因导致泡沫僵尸向上反弹?
你在第六章中学到了缓动功能。提醒一下,缓动函数指定了僵尸随时间变化的速率。所以在我们的例子中,我们想让泡沫僵尸向上弹跳,所以我们用了easeOutElastic:
。
什么是不透明?
属性设置或返回一个元素的透明度。这里是 1 完全不透明,0.5 是 50%透视,0 是完全透明。
泡沫僵尸飞走后发生了什么?
值得注意的是,我们要做的第一件事是执行以下检查:
if (fx.prop == "opacity" && fx.pos>=0.1) {
&&
表示逻辑与(即如果fx
属性是opacity
并且fx
位置大于或等于 0.1)。
我们需要对该位置进行检查的原因是因为第一个值几乎总是 0。如果我们允许这样,那么我们将被零除,当然,这是未定义的。这将导致问题和不确定的行为。
最后,我们将这个值作为泡沫僵尸的比例值。随着时间的推移,这使得泡沫僵尸显得更小,从而给人以消失在远处的印象。
给枪装子弹
你可能已经注意到我们的重载按钮到目前为止是相当多余的。当然,它确实会让我们的枪动起来,尽管它在我们的游戏中没有任何作用。到目前为止,最终的目标是在枪停止射击之前给用户一个固定的射击次数。此时,重新加载按钮提示用户按下它以继续他们的游戏。
我们要做的第一件事是在游戏开始时隐藏重装按钮。打开我们的CSS
文件夹中的SZ_master.css
文件。键入以下新行(所有新文本都以粗体显示):
html {
height: 100%;
}
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
cursor: crosshair;
}
img {
max-width: 100%;
height: auto;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#SZ0_0 {
position: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
}
#SZ0_1 {
position: fixed;
bottom: 0;
right: 0;
}
#SZ0_2 {
position: fixed;
top: 0;
left: 0;
cursor: pointer;
opacity:0;
}
#SZ0_3 {
position: fixed;
top: 0;
right: 0;
}
保存并关闭该文件。
在第三章,我建议你将这三个div
(即#SZ0_1
、#SZ0_2
、#SZ0_3
)组合在一起,因为它们具有相同的属性。然而,我们怎么能只为#SZ0_2
增加一个新的属性呢?我们将使用以下代码来实现这一点:
#SZ0_1, #SZ0_2, #SZ0_3 {
position: fixed;
top: 0;
right: 0;
}
#SZ0_2 {
opacity:0;
}
用额外的#SZ0_2
代码编写的任何东西都会附加上已经为它编写的任何东西。
回到My_Work_Files
文件夹,双击default.html
文件。重新加载按钮应该已经消失了。然而,如果你试图点击它,它仍然会开枪。因此,我们需要做一个检查,以确保我们只在 Reload 按钮可见时执行枪支动画。而且,这是一个在枪上放置最大数量子弹的好时机。
打开SZ_zombie_movement.js
文件,输入以下修改的行(所有修改的文本为红色):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//finally add in our bubble zombie to the screen too
document.body.appendChild(div2);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
//hide the bubble zombies at the start
$("#bubble_zombie"+whichOne).css('transform','scale('+0+')');
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the zombie is currently walking
if($("#zombie"+whichOne).css('opacity') != 0 && $("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
zombieHit(whichOne-1);
}
});
}
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//reset the zombies opacity
$zombiex.css({opacity:1});
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+0.001+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
// $(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne,0);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
//record this new scale value
scalex_zombie[whichOne-1]=xx;
}
}
}, complete: function () {
}
});
}
//a function to completely reset our zombie
function SZ_resetZombie(whichOne, zombieBubble_generate){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//should we generate a bubble zombie?
if(zombieBubble_generate==1){
//assign a user friendly name for our bubble zombie div
var $bubble_zombiex = $("#bubble_zombie"+whichOne);
//let's re-position our bubble zombie to our stored value
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left"), opacity:1});
//apply the scale
$bubble_zombiex.css('transform','scale('+scalex_zombie[whichOne-1]+')');
//call our bubble zombie animation function
bubbleZombie_flyAway(whichOne);
}
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px', opacity:0});
//finally let's make the zombie come towards the screen again
//SZ_animateZombie(whichOne);
}
您现在可以保存并关闭该文件。
这里我们的决定是基于各种元素的不透明性。我们将在下一段代码中继续这样做。将这些检查放在一个单独的函数中是一个好主意,然后在需要它的代码中简单地调用那个函数。
接下来,我们需要确保在适当的时候显示和隐藏重载按钮。打开SZ_touch.js
文件,输入以下新的和修改过的行(所有新文本以粗体显示):
//We need a flag to keep track to avoid repetition of animations before the first has finished
var canIclick= 0;
//this function is called to reload our gun
function reloadGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0 && $("#SZ0_2").css('opacity') == 1){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "reload");
//reset the current shots
current_shots=0;
//hide the reload button
$("#SZ0_2").css({opacity:0});
}
}
//place a maximum number of shots
var max_shots=5;
//keep track of current number of shots
var current_shots=0;
//this function is called to fire our gun
function fireGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0 && $("#SZ0_2").css('opacity') != 1){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "fire");
//increment our shots
current_shots++;
//check to see if we have reached the maximum
if(current_shots>=max_shots){
//show the reload button
$("#SZ0_2").css({opacity:1});
}//if
}
}
//array to keep track of the zombie hits
var zombieHits_counter = [0,0,0,0,0,0];
//array for each zombies limit
var zombieHits_limits = [2,1,3,2,1,3];
//this function will keep track of the zombie hits and act accordingly
function zombieHit(whichOne){
//increment the counter
zombieHits_counter[whichOne]++;
//check to see if this zombie has reached its limit
if(zombieHits_counter[whichOne] >= zombieHits_limits[whichOne]){
//reset this zombie
SZ_resetZombie(whichOne+1,1);
}
}
您现在可以保存并关闭该文件。
回到My_Work_Files
文件夹,双击default.html
文件。你应该看到枪在开了五枪后需要重新装弹。按下重新装弹按钮,会发生两件事:重新装弹按钮消失,你可以再次开枪。
接下来,我们解决游戏中出现的一些问题。
代码是怎么知道什么时候开枪,什么时候给枪装弹的?
这是一个很好的问题,我想指出的是,您刚刚输入的代码可能是我们迄今为止编写的较为复杂和有趣的代码之一。确定这个问题的答案有两个步骤:(1)检查并设置重新加载按钮的不透明度,以及(2)根据允许的最大值检查射击次数。所以让我们仔细看看我们写了什么。
你会注意到我们经常处理不透明性;例如,我们编写的第一部分代码是
opacity:0;
正如您在上一节中发现的,不透明度本质上是对象的透明度。在本例中,对象是SZ0_2
,它是重新加载按钮图像。
开始时,我们将重新加载按钮的不透明度设置为 0。从这一点开始,我们所要做的就是在继续之前检查这个按钮的不透明度。这通过使用以下检查来完成:
$("#SZ0_2").css('opacity') != 1
!=
意为“不等于”
通过使用这个检查,我们可以告诉代码开枪或显示重新加载按钮。
一旦按下重新加载按钮,我们可以隐藏它,从而允许枪再次发射。
最后一个想法。我们什么时候不再允许开枪?以下代码设置了重新加载前可以发射的最大发射次数:
var max_shots=5;
我们还需要记录当前的射击次数。我们使用以下变量来实现这一点:
var current_shots=0;
现在,每次射击时,我们可以比较两个变量,如下所示:
if(current_shots>=max_shots){
如果这是真的,我们停止任何进一步的射击,并迫使用户重新加载他们的枪。
清理深度并单击区域
你可能已经注意到,在地球表面的某些地方,点击鼠标不会引起枪战。这是因为你点击的是一个不可见的僵尸或者泡泡僵尸。但是,我们目前的法规不允许开枪。
第一部分:确保火炮射击
打开SZ_zombie_movement.js
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行(全部以红色显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//finally add in our bubble zombie to the screen too
document.body.appendChild(div2);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
//hide the bubble zombies at the start
$("#bubble_zombie"+whichOne).css('transform','scale('+0+')');
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
if($("#zombie"+whichOne).css('opacity') != 0){
zombieHit(whichOne-1);
}
}
});
//bind the users mouse click to the bubble zombie
$("#bubble_zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
}
});
}
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//reset the zombies opacity
$zombiex.css({opacity:1});
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+0.001+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
// $(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne,0);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
//record this new scale value
scalex_zombie[whichOne-1]=xx;
}
}
}, complete: function () {
}
});
}
//a function to completely reset our zombie
function SZ_resetZombie(whichOne, zombieBubble_generate){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//should we generate a bubble zombie?
if(zombieBubble_generate==1){
//assign a user friendly name for our bubble zombie div
var $bubble_zombiex = $("#bubble_zombie"+whichOne);
//let's re-position our bubble zombie to our stored value
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left"), opacity:1});
//apply the scale
$bubble_zombiex.css('transform','scale('+scalex_zombie[whichOne-1]+')');
//call our bubble zombie animation function
bubbleZombie_flyAway(whichOne);
}
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px', opacity:0});
//finally let's make the zombie come towards the screen again
//SZ_animateZombie(whichOne);
}
您现在可以保存并关闭该文件。
我想解释一下前面的红色代码。最初,该行代码是
if($("#zombie"+whichOne).css('opacity') != 0 && $("#SZ0_2").css('opacity') != 1) {
然而,我们删除了if
语句的第一部分,并将其放在更靠下的位置。这是因为我们需要确保重载按钮可见,不管僵尸的div
。
回到My_Work_Files
文件夹,双击default.html
文件。你应该看到枪可以在地球表面的任何地方开火。
接下来,我们来看看僵尸的深度等级。
第二部分:僵尸深度等级
你可能已经注意到的另一个问题是,有时一个僵尸会从另一个僵尸身上走过,比如下面这个游戏的截图:
这里发生的是,女僵尸是一个较低的深度;然而,因为她在这种情况下更快,所以她出现在较慢的僵尸之上。为了解决这个问题,我们需要在玩游戏的时候不断检查和调整深度。还有,我们总希望自己的枪在丧尸上面。
打开SZ_zombie_movement.js
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行(全部以红色显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0;')
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0;')
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//finally add in our bubble zombie to the screen too
document.body.appendChild(div2);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
SZ_animateZombie(whichOne);
//hide the bubble zombies at the start
$("#bubble_zombie"+whichOne).css('transform','scale('+0+')');
//set the zindex for the zombie
$("#zombie"+whichOne).css("z-index", whichOne+100);
//set the zindex for the bubble zombie
$("#bubble_zombie"+whichOne).css("z-index", whichOne);
//ensure the zindex for the gun is the highest
$("#SZ0_1").css("z-index", 200);
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
if($("#zombie"+whichOne).css('opacity') != 0){
zombieHit(whichOne-1);
}
}
});
//bind the users mouse click to the bubble zombie
$("#bubble_zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
}
});
}
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//reset the zombies opacity
$zombiex.css({opacity:1});
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+0.001+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
// $(this).stop();
//call a function to reset this zombie
SZ_resetZombie(whichOne,0);
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
//record this new scale value
scalex_zombie[whichOne-1]=xx;
//check the depth levels
var i = 0;
while (i < 6) {
//check to see if the scale is bigger
if(scalex_zombie[whichOne-1] >scalex_zombie[i] && ($(this).zIndex() < $("#zombie"+(i+1)).zIndex()) && scalex_zombie[i]!=0){
var i_index = $("#zombie"+(i+1)).zIndex();
//change the i one first
$("#zombie"+(i+1)).css("z-index", $(this).css( "z-index" ));
//now change this one
$(this).css("z-index", i_index);
} //end of if
i++;
}//end of while loop
}
}
}, complete: function () {
}
});
}
//need to keep track of the current zindex for zombies
var zindex_current=0;
//a function to completely reset our zombie
function SZ_resetZombie(whichOne, zombieBubble_generate){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//should we generate a bubble zombie?
if(zombieBubble_generate==1){
//assign a user friendly name for our bubble zombie div
var $bubble_zombiex = $("#bubble_zombie"+whichOne);
//let's re-position our bubble zombie to our stored value
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left"), opacity:1});
//apply the scale
$bubble_zombiex.css('transform','scale('+scalex_zombie[whichOne-1]+')');
//call our bubble zombie animation function
bubbleZombie_flyAway(whichOne);
}
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px', opacity:0});
//set the zindex for the zombie
zindex_current++;
$("#zombie"+whichOne).css("z-index", zindex_current);
//finally let's make the zombie come towards the screen again
//SZ_animateZombie(whichOne);
}
您现在可以保存并关闭该文件。
回到My_Work_Files
文件夹,双击default.html
文件。你应该看到僵尸不再像以前那样互相重叠。还有,枪要一直出现在丧尸的上方。
接下来,我们看看如何在屏幕上创建一个游戏。
z 索引到底是什么?
z 索引是每个元素的顺序。想象一下,我们所有的元素,比如枪的图像或者分数的图像,就像一副纸牌一样排成一行。如果你稍微展开卡片,你可以完全看到最上面的卡片,而下面的卡片只能稍微看得见。这是因为当卡片重叠时,一张特定卡片的可见性取决于它在顶部的位置。
同样,当我们的元素相互重叠时,它们的可见性取决于它们的堆栈顺序或 z 索引。因此,z 索引较大的元素在与 z 索引较低的元素重叠时会更明显。
当我们连续随机放置的僵尸开始相互重叠时,这真的变得很有用。JavaScript 允许我们操纵 z-index,因此我们可以对元素进行重新排序,以确保离我们较远的元素位于较近的元素之后。
我们刚刚编写的下面一行代码中发生了什么?
while (i < 6) {
这是一个while
循环的例子。几乎所有的编程语言都有一个while
循环,它本质上允许代码根据放置的条件重复执行。
所以在我们的例子中,while
循环中的代码会持续执行,直到i
变量不再小于 6。
介绍飞溅和“游戏结束”屏幕
到目前为止,我们的游戏立即开始,并没有真正结束。这将是很好的介绍屏幕和游戏屏幕。
第一部分:图像文件夹
转到My_Work_Files
文件夹的Raw Images
文件夹中的images
文件夹。找到名为splash_intro.png
和splash_gameover.png
的文件,并将它们复制到Images
文件夹,现在应该看起来像下面的截图:
第二部分:停止和启动
为了停止和开始游戏的启动画面和游戏结束画面,我们需要添加许多新代码,并对现有代码进行更改。请密切注意新行(粗体)和修改后的行(红色)。
几乎所有我们现有的文件都需要修改。因此,让我们从打开default.html
文件开始,键入以下新行(所有新文本都以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<script src="js/ss.js"></script>
<script src="js/SZ_SS.js"></script>
<script src="js/SZ_touch.js"></script>
<script src="js/SZ_zombie_movement.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)" onmousedown="fireGun(event)" />
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="img/SZ_reload.png" onmousedown="reloadGun(event)" />
</div>
<div id="SZ0_3" >
<img src="img/SZ_score.png" />
</div>
<div id="SZ0_4" onmousedown="start_game();"/>
</div>
</body>
</html>
保存文件,然后关闭它。
打开SZ_master.css
文件,输入以下新行(所有新文本以粗体显示):
html {
height: 100%;
}
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
cursor: crosshair;
}
img {
max-width: 100%;
height: auto;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#SZ0_0 {
position: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
}
#SZ0_1 {
position: fixed;
bottom: 0;
right: 0;
opacity:0;
}
#SZ0_2 {
position: fixed;
top: 0;
left: 0;
cursor: pointer;
opacity:0;
}
#SZ0_3 {
position: fixed;
top: 0;
right: 0;
opacity:0;
}
#SZ0_4 {
position: fixed;
cursor: pointer;
background-size:cover;
opacity:0;
}
保存文件,然后关闭它。
打开SZ_setupContent.js
文件,输入以下新行(所有新文本以粗体显示):
//we will need a new ratio var
var ratio_use = ratio;
//main function
function main_call_setupContent() {
//need to resize all elements
//first we set their normal sizes in CSS
//Gun
$('#SZ0_1').css('width', 150 * ratio);
$('#SZ0_1').css('height', 150 * ratio);
//Reload Button
$('#SZ0_2').css('width', 200 * ratio);
$('#SZ0_2').css('height', 90 * ratio);
//Score
$('#SZ0_3').css('width', 235 * ratio);
$('#SZ0_3').css('height', 100 * ratio);
//Intro and Game over
if($(window).height()<$(window).width()){
//work out a ratio based on height
ratio_use = $(window).height()/800;
}//end
if
//apply this new ratio to our intro/game over
$('#SZ0_4').css('width', 868 * ratio_use);
$('#SZ0_4').css('height', 701 * ratio_use);
$('#SZ0_4').css('left', ($(window).width()/2)-((868 * ratio_use)/2));
//make sure it is half way
$('#SZ0_4').css('top', ($(window).height()/2)-((701 * ratio_use)/2));
//Any sprite sheets?
//Our Gun
setup_gun_SS();
//Create all our 6 zombies
for (i = 1; i < 7; i++) {
//this will get called 6 times
SZ_createZombie(i);
}
//call the
intro
start_end_game(0);
}
var gameEnded=0;
//Intro or Game Over of game
function start_end_game(whichOne) {
//hide the elements
for (i = 1; i < 4; i++) {
//this will get called 3 times
$('#SZ0_'+i).css({opacity:0});
}//for
//hide the zombies
for (i = 1; i < 7; i++) {
//we need to stop this zombies animations
$('#zombie_'+i).stop();
$('#zombie_'+i).css({opacity:0});
$('#bubble_zombie_'+i).css({opacity:0});
}//for
if(whichOne==0){
//START OF
GAME
//change the background image
$('#SZ0_4').css('background-image', 'url(img/splash_intro.png)');
} else {
//GAME OVER
//show the score
$('#SZ0_3').css({opacity:1});
//change the background image
$('#SZ0_4').css('background-image', 'url(img/splash_gameover.png)');
}
//make sure it is half way
$('#SZ0_4').css('top', ($(window).height()/2)-((701 * ratio_use)/2));
//finally show the intro or game over image
$('#SZ0_4').css({opacity:1});
//stop the user from firing
gameEnded= 1;
}//end of function
//start the
game
function start_game() {
//reset the zindex
zindex_current=0;
//reload the gun
current_shots=0;
//allow user to fire
gameEnded= 0;
//hide the intro or game over image
$('#SZ0_4').css({opacity:0});
//make sure it is out of the way
$('#SZ0_4').css('top', ($(window).height()));
//show the
elements
for (i = 1; i < 4; i++) {
//this will get called 3 times
$('#SZ0_'+i).css({opacity:1});
}//for
//hide the reload button!
$('#SZ0_2').css({opacity:0});
//show the zombies
for (i = 0; i < 7; i++) {
//reset the Zombie
SZ_resetZombie(i,0);
}//for
//ensure the score board is half opacity
$('#SZ0_3').css({opacity:0.5});
}//end of function
保存文件,然后关闭它。
在start_end_game
函数中,一次有过多的检查和动作。随着游戏的深入,这个函数将变得不可管理,所以我建议你试着把对另一个文件的检查分开,这样这个函数就可以检查各种元素的状态。
一种可能性是为某些条件设置标志。可以在某些事件上设置这些标志。这意味着当我们使用这个函数时,我们所做的只是检查标志的状态。
同样,有些行动应该是在它们自己的功能中;例如,基于前面的检查隐藏各种元素。
打开SZ_touch.js
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行(全部以红色显示):
//We need a flag to keep track to avoid repetition of animations before the first has finished
var canIclick= 0;
//this function is called to reload our gun
function reloadGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0 && $("#SZ0_2").css('opacity') == 1){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "reload");
//reset the current shots
current_shots=0;
//hide the reload button
$("#SZ0_2").css({opacity:0});
}
}
//place a maximum number of shots
var max_shots=5;
//keep track of current number of shots
var current_shots=0;
//this function is called to fire our gun
function fireGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0 && gameEnded==0 && $("#SZ0_2").css('opacity') != 1){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "fire");
//increment our shots
current_shots++;
//check to see if we have reached the maximum
if(current_shots>=max_shots){
//show the reload button
$("#SZ0_2").css({opacity:1});
}//if
}
}
//array to keep track of the zombie hits
var zombieHits_counter = [0,0,0,0,0,0];
//array for each zombies limit
var zombieHits_limits = [2,1,3,2,1,3];
//this function will keep track of the zombie hits and act accordingly
function zombieHit(whichOne){
//increment the counter
zombieHits_counter[whichOne]++;
//check to see if this zombie has reached its limit
if(zombieHits_counter[whichOne] >= zombieHits_limits[whichOne]){
//reset this zombie
SZ_resetZombie(whichOne+1,1);
}
}
保存文件,然后关闭它。
给变量和函数起一个有意义的名字对于代码的可读性是很重要的。这在前面的代码中可以看到。随着你的游戏变得越来越大越来越复杂,如果你能像读小说一样阅读你之前写的代码,你将会减少理解它所需的时间。
打开SZ_zombie_movement.js
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行(全部以红色显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0; opacity:0');
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0;');
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//finally add in our bubble zombie to the screen too
document.body.appendChild(div2);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
// SZ_animateZombie(whichOne);
//hide the bubble zombies at the start
$("#bubble_zombie"+whichOne).css('transform','scale('+0+')');
//set the zindex for the zombie
$("#zombie"+whichOne).css("z-index", whichOne+100);
//set the zindex for the bubble zombie
$("#bubble_zombie"+whichOne).css("z-index", whichOne);
//ensure the zindex for the gun is the highest
$("#SZ0_1").css("z-index", 200);
//also ensure the zindex for the intro/game over is the highest
$("#SZ0_4").css("z-index", 201);
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
if($("#zombie"+whichOne).css('opacity') != 0){
zombieHit(whichOne-1);
}
}
});
//bind the users mouse click to the bubble zombie
$("#bubble_zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
}
});
}
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//reset the zombies opacity
$zombiex.css({opacity:1});
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+0.001+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
if(gameEnded==1){
xx=999;
}
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
$(this).stop();
//call a function to reset this zombie
//SZ_resetZombie(whichOne,0);
//game Over
$(this).css({opacity:0});
$(this).stop(true, true);
$(this).finish();
if(gameEnded==0 && xx!=999){
start_end_game(1);
}
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
//record this new scale value
scalex_zombie[whichOne-1]=xx;
//check the depth levels
var i = 0;
while (i < 6) {
//check to see if the scale is bigger
if(scalex_zombie[whichOne-1]>scalex_zombie[i] && ($(this).zIndex() < $("#zombie"+(i+1)).zIndex()) && scalex_zombie[i]!=0){
var i_index = $("#zombie"+(i+1)).zIndex();
//change the i one first
$("#zombie"+(i+1)).css("z-index", $(this).css( "z-index" ));
//now change this one
$(this).css("z-index", i_index);
} //end of if
i++;
}//end of while loop
}
}
}, complete: function () {
}
});
}
//need to keep track of the current zindex for zombies
var zindex_current=0;
//a function to completely reset our zombie
function SZ_resetZombie(whichOne, zombieBubble_generate){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//should we generate a bubble zombie?
if(zombieBubble_generate==1){
//assign a user friendly name for our bubble zombie div
var $bubble_zombiex = $("#bubble_zombie"+whichOne);
//let's re-position our bubble zombie to our stored value
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left"), opacity:1});
//apply the scale
$bubble_zombiex.css('transform','scale('+scalex_zombie[whichOne-1]+')');
//call our bubble zombie animation function
bubbleZombie_flyAway(whichOne);
}
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px', opacity:0});
//set the zindex for the zombie
zindex_current++;
$("#zombie"+whichOne).css("z-index", zindex_current);
//finally let's make the zombie come towards the screen again
if(zombieBubble_generate==0){
SZ_animateZombie(whichOne);
}
}
保存文件,然后关闭它。
回到My_Work_Files
文件夹,双击default.html
文件。你应该马上看到介绍启动画面。您还应该注意到,分数和枪的图像不在那里,正如我们所希望的那样。当我们开始游戏的时候,它看起来应该和最近修改之前一样。当僵尸到达其路径的尽头时,你现在应该在屏幕上看到游戏。
恭喜你!你开发了一个工作游戏。
接下来,让我们改进我们的游戏,让它对我们的用户更有吸引力。
代码不起作用吗?我们在这里修改了很多文件,所以我的建议是仔细检查每个文件中的每一行代码,并与您自己的代码进行比较。甚至旧的灰色代码。耐心点,完成你的代码。
以下是我发现的一些问题:
确保在下面一行中有结束标记(显示为红色):
<div id="SZ0_4" onmousedown="start_game();"/>
确保将这条线放在主函数的上方:
var ratio_use = ratio;
//main function
确保您在以下行中使用的是ratio_use
而不是 ratio:
$('#SZ0_4').css('width', 868 * ratio_use);
$('#SZ0_4').css('height', 701 * ratio_use);
确保您已经注释掉(//
)以下行:
// SZ_animateZombie(whichOne);
//SZ_resetZombie(whichOne,0);
如果代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息@zarrarchishti
。
游戏如何知道什么时候显示介绍闪屏,什么时候在屏幕上显示游戏?
当被调用时,下面的函数停止游戏并在屏幕上显示介绍或游戏:
function start_end_game(whichOne) {
该函数的任务之一是根据传入的参数whichOne
显示介绍图像或游戏结束图像。下面几行将显示这些图像中的任何一个:
$('#SZ0_4').css('background-image', 'url(img/splash_intro.png)');
$('#SZ0_4').css('background-image', 'url(img/splash_gameover.png)');
八、给我们的游戏增添一些光彩
简单是最高级的复杂
莱昂纳多达芬奇
我相信你已经注意到在我们的分数箱里没有实际的分数。我们需要的是,每当我们把一个僵尸送上太空,文本就会增加。为此,我们需要做到以下几点:
- 在我们的 HTML 中添加一个文本字段。
- 格式化此文本字段,使其根据屏幕大小改变大小和位置。
- 从零开始,每出现一个泡泡僵尸就递增一次。
- 每次玩新游戏时重置为零。
比分是多少?
打开default.html
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行(全部以红色显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<script src="js/ss.js"></script>
<script src="js/SZ_SS.js"></script>
<script src="js/SZ_touch.js"></script>
<script src="js/SZ_zombie_movement.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)" onmousedown="fireGun(event)" />
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="img/SZ_reload.png" onmousedown="reloadGun(event)" />
</div>
<div id="SZ0_3" style="background-image: url(img/SZ_score.png);">
<div id="textx">0</div>
</div>
<div id="SZ0_4" onmousedown="start_game();"/>
</div>
</body>
</html>
保存此文件,然后关闭它。
在这个项目中,我们遇到过几次。正如您所猜测的,background-image
属性为一个元素设置了一个或多个背景图像。重要的是要记住,默认情况下,background-image
位于元素的左上角,并且在垂直和水平方向重复出现。因此,如果您希望属性以不同的方式运行,就需要处理这些属性。我总是建议也设置一个background-color
属性。这是为了防止图像不可用或加载时间过长。
打开SZ_master.css
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行(全部以红色显示):
html {
height: 100%;
}
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
cursor: crosshair;
}
img {
max-width: 100%;
height: auto;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#SZ0_0 {
position: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
}
#SZ0_1 {
position: fixed;
bottom: 0;
right: 0;
opacity:0;
}
#SZ0_2 {
position: fixed;
top: 0;
left: 0;
cursor: pointer;
opacity:0;
}
#SZ0_3 {
position: fixed;
top: 0;
right: 0;
opacity:0;
background-size:cover;
}
#SZ0_4 {
position: fixed;
cursor: pointer;
background-size:cover;
opacity:0;
}
#textx {
position: relative;
float: left;
top: 40%;
text-align:center;
font-size: 4vmax;
font-weight: bolder;
colour: white;
font-family: "Arial Black";
}
保存此文件,然后关闭它。
快好了。现在打开SZ_setupContent.js
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行:
//we will need a new ratio var
var ratio_use = ratio;
//main function
function main_call_setupContent() {
//need to resize all elements
//first we set their normal sizes in CSS
//Gun
$('#SZ0_1').css('width', 150 * ratio);
$('#SZ0_1').css('height', 150 * ratio);
//Reload Button
$('#SZ0_2').css('width', 200 * ratio);
$('#SZ0_2').css('height', 90 * ratio);
//Score
$('#SZ0_3').css('width', 235 * ratio);
$('#SZ0_3').css('height', 100 * ratio);
//Intro and Game over
if($(window).height()<$(window).width()){
//work out a ratio based on height
ratio_use = $(window).height()/800;
}//end if
//apply this new ratio to our intro/game over
$('#SZ0_4').css('width', 868 * ratio_use);
$('#SZ0_4').css('height', 701 * ratio_use);
$('#SZ0_4').css('left', ($(window).width()/2)-((868 * ratio_use)/2));
//make sure it is half way
$('#SZ0_4').css('top', ($(window).height()/2)-((701 * ratio_use)/2));
$('#textx').css('width', '100%');
$('#textx').css('height', '50%');
//Any sprite sheets?
//Our Gun
setup_gun_SS();
//Create all our 6 zombies
for (i = 1; i < 7; i++) {
//this will get called 6 times
SZ_createZombie(i);
}
//call the intro
start_end_game(0);
}
var gameEnded=0;
//Intro or Game Over of game
function start_end_game(whichOne) {
//hide the elements
for (i = 1; i < 4; i++) {
//this will get called 3 times
$('#SZ0_'+i).css({opacity:0});
}//for
//hide the zombies
for (i = 1; i < 7; i++) {
//we need to stop this zombies animations
$('#zombie_'+i).stop();
$('#zombie_'+i).css({opacity:0});
$('#bubble_zombie_'+i).css({opacity:0});
//set the zindex for the zombie
$("#zombie"+i).css("z-index", i+100);
}//for
if(whichOne==0){
//START OF GAME
//change the background image
$('#SZ0_4').css('background-image', 'url(img/splash_intro.png)');
} else {
//GAME OVER
//show the score
$('#SZ0_3').css({opacity:1});
//change the background image
$('#SZ0_4').css('background-image', 'url(img/splash_gameover.png)');
}
//make sure it is half way
$('#SZ0_4').css('top', ($(window).height()/2)-((701 * ratio_use)/2));
//finally show the intro or game over image
$('#SZ0_4').css({opacity:1});
//stop the user from firing
gameEnded= 1;
}//end of function
//need to store the current score
var current_score=0;
//we can call this function to update the score
function updateScore(){
$("#textx").text(current_score);
}
//start the game
function start_game() {
//reset the score
current_score=0;
updateScore();
//reset the zindex
zindex_current=0;
//reload the gun
current_shots=0;
//allow user to fire
gameEnded= 0;
//hide the intro or game over image
$('#SZ0_4').css({opacity:0});
//make sure it is out of the way
$('#SZ0_4').css('top', ($(window).height()));
//show the elements
for (i = 1; i < 4; i++) {
//this will get called 3 times
$('#SZ0_'+i).css({opacity:1});
}//for
//hide the reload button!
$('#SZ0_2').css({opacity:0});
//show the zombies
for (i = 0; i < 7; i++) {
//reset the Zombie
SZ_resetZombie(i,0);
}//for
//ensure the score board is half opacity
$('#SZ0_3').css({opacity:0.5});
}//end of function
保存此文件,然后关闭它。
当你进一步开发这个游戏或者开始一个新游戏时,我建议你将 z 索引值(或者甚至起始值)存储在另一个文件中。这将有助于您将来进行修改,并需要跟踪各种元素的 z 索引值。
最后,打开SZ_movement.js
文件,键入以下新行(所有新文本都用粗体显示)和一些修改过的行(都用红色显示):
function rotateGun(e) {
//using the e value we can deduce the X co-ordinates
var xPos = e.clientX;
//We need to work out where the mouse cursor is as a percentage of the width of the screen
//We will work this out by dividing the current X position by the overall screen width which if you remember we put in newWidth
var currentXPositionPercentage = xPos/newWidth;
//We now want to apply this to the maximum amount of rotation which is 50 however the starting rotation is -15 not 0
var amountToRotate = -15 + (currentXPositionPercentage * 50);
//Let's rotate the gun!
$("#SZ0_1").css('transform', 'rotate('+amountToRotate+'deg)');
}
//movement for our bubble zombie
function bubbleZombie_flyAway(whichOne){
//update the score
current_score++;
updateScore();
//assign a user friendly name for our div
var $zombiex = $("#bubble_zombie"+whichOne);
//first it should animate upwards with a bounce
$zombiex.animate({
//bring our zombie up the screen
top: "-="+50*ratio+ "px",
},{ easing:"easeOutElastic", duration: 400,
complete: function () {
//now the final animation where the bubble zombie disappears into space
$(this).delay(150).animate({
//slowly turn the alpha down
opacity: "-="+1,
},{ easing:"easeOutQuint", duration: 1000,
step: function(now, fx){
//at each step we can adjust the scale to make it look smaller
if (fx.prop == "opacity" && fx.pos>=0.1) {
//work out the amount to scale
var xx = 0.5/(fx.pos);
//apply the scale
$(this).css('transform','scale('+xx+')');
}
}, complete: function () {
//finally let's make the zombie come towards the screen again
SZ_animateZombie(whichOne);
}//end of second complete function
});//end of second animation
}//end of first complete function
}); //end of first animation
}
保存此文件,然后关闭它。
我们现在准备测试!回到My_Work_Files
文件夹,双击default.html
文件。您现在应该会看到乐谱文本出现。每次我们把僵尸送上太空,你都得一分。
这不起作用吗?如果不是,很有可能是default.html
里写的代码。
最初,代码是这样的:
<div id="SZ0_3" >
<img src="img/SZ_score.png" />
</div>
现在,我们把它改成这样:
<div id="SZ0_3" style="background-image: url(img/SZ_score.png);">
<div id="textx">0</div>
</div>
请确保您已经完全按照显示的方式对这些行进行了编码。
如果代码仍然不工作,那么请不要犹豫,在 Twitter 上给我发消息(@zarrarchishti
)。
updateScore(){实际上是如何更新屏幕上的文本的?
您可能已经注意到,我们最初创建了一个名为
var current_score=0;
每当调用泡泡僵尸动画时,就会使用下面的行来更新:
current_score++;
从前面我们知道++将变量加 1。仅仅这样并不能更新屏幕上的文本。看看我们函数中的代码。
updateScore(){ :
$("#textx").text(current_score);
这将使用当前得分变量中的值替换文本div
中的文本。此时,屏幕文本值会发生变化。
特效的喷洒
你可能已经注意到,当用户向僵尸开火时,没有视觉反馈。只有当达到最大点击数时,你才会看到气泡僵尸形式的反馈。所以在这一章中,我们给被击中的僵尸添加了一个特效。
第一部分:开始
转到My_Work_Files
文件夹的Raw Images
文件夹中的images
文件夹。找到名为SZ_effect_ss.png
的文件,并复制到Images
文件夹中,现在看起来应该是这样的:
第二部分:显示效果
为了给屏幕添加特殊效果,我们需要精确定位用户击中僵尸的位置。一旦我们完成了这些,我们就可以使用我们的 sprite sheet 库来显示我们的效果。
打开SZ_zombie_movement.js
文件,键入以下新行(所有新文本均为粗体)和一些修改过的行(均为红色):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//and another for the special effect SS
var div3 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0; opacity:0');
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0;');
//and the same for our special effect SS
div3.setAttribute('style','position: fixed; top:0; left:0;');
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//and the same for our special effect SS
div3.style.left = left_position+'px'; div3.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//also for our special effect SS
div3.id = 'zombie_effect'+whichOne;
//finally let's add our zombie to the screen
document.body.appendChild(div);
//finally add in our bubble zombie to the screen too
document.body.appendChild(div2);
//finally add in our special effect SS to the screen too
document.body.appendChild(div3);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
// SZ_animateZombie(whichOne);
//hide the bubble zombies at the start
$("#bubble_zombie"+whichOne).css('transform','scale('+0+')');
//ensure no hits are registered on the special effects
$("#zombie_effect"+whichOne).css( 'pointer-events', 'none' );
//set the zindex for the zombie
$("#zombie"+whichOne).css("z-index", whichOne+100);
//set the zindex for the bubble zombie
$("#bubble_zombie"+whichOne).css("z-index", whichOne);
//set the zindex for the special effect SS
$("#zombie_effect"+whichOne).css("z-index", whichOne+150);
//ensure the zindex for the gun is the highest
$("#SZ0_1").css("z-index", 200);
//also ensure the zindex for the intro/game over is the highest
$("#SZ0_4").css("z-index", 201);
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
if($("#zombie"+whichOne).css('opacity') != 0){
var offset = $(this).offset();
zombieHit(whichOne-1, e.pageX, e.pageY);
}
}
});
//bind the users mouse click to the bubble zombie
$("#bubble_zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
}
});
}
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//reset the zombies opacity
$zombiex.css({opacity:1});
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+0.001+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
if(gameEnded==1){
xx=999;
}
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
$(this).stop();
//call a function to reset this zombie
//SZ_resetZombie(whichOne,0);
//game Over
$(this).css({opacity:0});
$(this).stop(true, true);
$(this).finish();
if(gameEnded==0 && xx!=999){
start_end_game(1);
}
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
//record this new scale value
scalex_zombie[whichOne-1]=xx;
//check the depth levels
var i = 0;
while (i < 6) {
//check to see if the scale is bigger
if(scalex_zombie[whichOne-1]>scalex_zombie[i] && ($(this).zIndex() < $("#zombie"+(i+1)).zIndex()) && scalex_zombie[i]!=0){
var i_index = $("#zombie"+(i+1)).zIndex();
//change the i one first
$("#zombie"+(i+1)).css("z-index", $(this).css( "z-index" ));
//now change this one
$(this).css("z-index", i_index);
} //end of if
i++;
}//end of while loop
}
}
}, complete: function () {
}
});
}
//need to keep track of the current zindex for zombies
var zindex_current=0;
//a function to completely reset our zombie
function SZ_resetZombie(whichOne, zombieBubble_generate){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//should we generate a bubble zombie?
if(zombieBubble_generate==1){
//assign a user friendly name for our bubble zombie div
var $bubble_zombiex = $("#bubble_zombie"+whichOne);
//let's re-position our bubble zombie to our stored value
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left"), opacity:1});
//apply the scale
$bubble_zombiex.css('transform','scale('+scalex_zombie[whichOne-1]+')');
//call our bubble zombie animation function
bubbleZombie_flyAway(whichOne);
}
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px', opacity:0});
//set the zindex for the zombie
zindex_current++;
$("#zombie"+whichOne).css("z-index", zindex_current);
//finally let's make the zombie come towards the screen again
if(zombieBubble_generate==0){
SZ_animateZombie(whichOne);
}
}
保存文件,然后关闭它。
我们在前面的代码中引用了e.pageX
和e.pageY
属性。它们返回鼠标指针相对于文档左边缘的位置。这些属性考虑了页面的任何水平或垂直滚动。如果您将来要引用一些旧的代码,这个属性最初被定义为一个长整数;但是,CSSOM 视图模块将其重新定义为双浮点型。
打开SZ_touch.js
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行(全部以红色显示):
//We need a flag to keep track to avoid repetition of animations before the first has finished
var canIclick= 0;
//this function is called to reload our gun
function reloadGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0 && $("#SZ0_2").css('opacity') == 1){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "reload");
//reset the current shots
current_shots=0;
//hide the reload button
$("#SZ0_2").css({opacity:0});
}
}
//place a maximum number of shots
var max_shots=5;
//keep track of current number of shots
var current_shots=0;
//this function is called to fire our gun
function fireGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0 && gameEnded==0 && $("#SZ0_2").css('opacity') != 1){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "fire");
//increment our shots
current_shots++;
//check to see if we have reached the maximum
if(current_shots>=max_shots){
//show the reload button
$("#SZ0_2").css({opacity:1});
}//if
}
}
//array to keep track of the zombie hits
var zombieHits_counter = [0,0,0,0,0,0];
//array for each zombies limit
var zombieHits_limits = [2,1,3,2,1,3];
//this function will keep track of the zombie hits and act accordingly
function zombieHit(whichOne, xx, yy){
//increment the counter
zombieHits_counter[whichOne]++;
//check to see if this zombie has reached its limit
if(zombieHits_counter[whichOne] >= zombieHits_limits[whichOne]){
//reset this zombie
SZ_resetZombie(whichOne+1,1);
}
//let’s add in our special effect
var whichOne2=whichOne+1;
var $effect_zombiex = $("#zombie_effect"+whichOne2);
//let's re-position our bubble zombie to our stored value
$effect_zombiex.css({top: yy+'px',left: xx+'px', opacity:1});
$effect_zombiex.animateSprite("play", "z1");
//apply the scale
$effect_zombiex.css('transform','scale('+scalex_zombie[whichOne]+')');
}
保存文件,然后关闭它。
在前面的代码中,我们动态定位我们的特效,使其正好出现在僵尸的div
上。在某些情况下,根据特效精灵的大小,你可能需要考虑其他因素。例如,一些效果可能需要定位在除僵尸之外的元素上。你可能想要一个特殊的效果来显示当用户射击地面时,大块的地面被炸飞。这不是很难;我们会用僵尸div
代替地面的div
。
当我们偏移拍摄区域,使其包括僵尸的脚和地面区域时,这将成为一个轻微的挑战——给出一个地面效果和僵尸击中效果被触发的整体现实场景。正如我所建议的,我们需要考虑到这两种情况。
打开SZ_SS.js
文件,输入以下新行(所有新文本以粗体显示):
//We need a one stop function that will allow us to process sprite sheets
function setup_SpriteSheet(div_name, image_name, no_of_frames, widthx, heightx) {
//need the ratio of the container's width/height
var imageOrgRatio = $(div_name).height() / $(div_name).width() ;
//need to ensure no trailing decimals
var ratio2 = Math.round(ratio * 10) / 10;
//check that the width is completely divisible by the no of frames
var newDivisible = Math.round((widthx * ratio2) / no_of_frames);
//the new width will be the number of frames multiplied by our new divisible
var newWidthx = newDivisible * no_of_frames;
//also the new height will be our ratio times the height of the div containing our image
var newHeightx = heightx * ratio2;
//apply our new width to our CSS
$(div_name).css('width', (newWidthx));
//apply our new height to our CSS
$(div_name).css('height', newHeightx);
//
//take the image name and apply as a background image to our div
$(div_name).css('background-image', 'url(' + image_name + ')');
//finally we need to apply a background size remembering we need to multiply width by the no of frames
$(div_name).css('background-size', newWidthx * no_of_frames + 'px ' + newHeightx + 'px');
}
//setup the Gun
function setup_gun_SS(){
//first let's apply our gun to our SS function
setup_SpriteSheet("#SZ0_1","img/SZ_gun_SS.png",28,150,150);
//need to access a special function in our js/ss.js file
$("#SZ0_1").animateSprite({
fps: 10,
animations: {
static: [0],
reload: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],
fire: [24,25,26,27,28],
},
duration: 50,
loop: false,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
//we need to reset our universal flag
canIclick=0;
}
});
}
//setup a newly created zombie
function setup_zombie_SS(whichOne){
//let's identify what type of zombie we should create
var type_zombie = [1,2,3,1,2,3];
//let's setup a speed for each type of zombie
var speed_zombie = [100,50,150];
//first let's setup our zombie SS
setup_SpriteSheet("#zombie"+whichOne,"img/zombiesSS_"+type_zombie[whichOne-1]+".png",9,20,20);
//need to access a special function in our js/ss.js file
$("#zombie"+whichOne).animateSprite({
fps: 10,
animations: {
static: [0,1,2,3,4,5,6,7],
},
duration: speed_zombie[type_zombie[whichOne-1]-1],
loop: true,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
}
});
//now let's setup our bubble zombie SS
setup_SpriteSheet("#bubble_zombie"+whichOne,"img/SZ_bubble.png",3,20,20);
//need to access a special function in our js/ss.js file
$("#bubble_zombie"+whichOne).animateSprite({
fps: 10,
animations: {
z1: [type_zombie[whichOne-1]-1],
},
duration: 1,
loop: false,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
}
});
//not to forget our special effects SS
setup_SpriteSheet("#zombie_effect"+whichOne,"img/SZ_effect_ss.png",4,13,15);
//need to access a special function in our js/ss.js file
$("#zombie_effect"+whichOne).animateSprite({
fps: 10,
animations: {
z1: [0,1,2,3],
},
duration: 20,
loop: false,
complete: function () {
// use complete only when you set animations with 'loop: false'
//alert("animation End");
$("#zombie_effect"+whichOne).css({opacity:0});
}
});
}
保存文件,然后关闭它。
我们现在准备测试!回到My_Work_Files
文件夹,双击default.html
文件。现在当你向僵尸开火时,你应该会看到我们的特效出现在你点击的地方。我相信你会同意,这增加了我们玩家的游戏体验的深度!
我们如何准确定位我们点击的特效?
为了定位特效,我们首先需要确保当有鼠标点击僵尸时,我们通过 x 和 y 坐标。我们在SZ_zombie_movement.js
中这样做。
var offset = $(this).offset();
zombieHit(whichOne-1, e.pageX, e.pageY);
offset()
方法为我们返回坐标。然后我们可以将它们传递给我们的zombieHit
函数。这意味着我们必须修改SZ_touch.js
中的zombieHit
功能。
function zombieHit(whichOne, xx, yy){
我们已经添加了两个变量,现在可以传入供我们使用。
$effect_zombiex.css({top: yy+'px',left: xx+'px', opacity:1});
所以当我们开始定位特效时,我们可以用两个新的变量来表示 x 和 y 的位置。
打开声音效果
我们注意到,通过添加特效,我们可以增强用户的游戏体验。类似地,我们可以更进一步,为玩家执行的各种动作提供音频反馈。
第一部分:入门
转到My_Work_Files
文件夹的Raw Images
文件夹中的images
文件夹。找到名为sounds
的文件夹,并将其复制到My_Work_Files
文件夹中。
您的My_Work_Files
文件夹现在应该是这样的:
第二部分:添加声音效果
我们将在游戏中加入两种音效。第一个是玩家开枪的时候。第二次是玩家重新装弹的时候。让游戏播放声音并不困难,但是控制声音播放的时间就比较棘手了。
打开default.html
文件,输入以下新行(所有新文本以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="sounds/jquery.playSound.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<script src="js/ss.js"></script>
<script src="js/SZ_SS.js"></script>
<script src="js/SZ_touch.js"></script>
<script src="js/SZ_zombie_movement.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)" onmousedown="fireGun(event)" />
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="img/SZ_reload.png" onmousedown="reloadGun(event)" />
</div>
<div id="SZ0_3" style="background-image: url(img/SZ_score.png);">
<div id="textx">999</div>
</div>
<div id="SZ0_4" onmousedown="start_game();"/>
</div>
</body>
</html>
保存文件,然后关闭它。
你当然可以创建自己的声音库,甚至可以自己编写声音库!然而,你选择的库类型取决于你的游戏需求。例如,在我们的游戏中,我们需要短的声音爆炸,而不是长的音乐文件。
在最基本的层面上,您可以使用<audio>
标记,然后调用play()
方法。
打开SZ_touch.js
文件,输入以下新行(所有新文本以粗体显示):
//We need a flag to keep track to avoid repetition of animations before the first has finished
var canIclick= 0;
//this function is called to reload our gun
function reloadGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0 && $("#SZ0_2").css('opacity') == 1){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "reload");
//reset the current shots
current_shots=0;
//hide the reload button
$("#SZ0_2").css({opacity:0});
//play the reload sound
$.playSound('sounds/reload');
}
}
//place a maximum number of shots
var max_shots=5;
//keep track of current number of shots
var current_shots=0;
//this function is called to fire our gun
function fireGun(e) {
//Let's check if we can allow this to occur
if(canIclick== 0 && gameEnded==0 && $("#SZ0_2").css('opacity') != 1){
//looks like we can so we better set our flag
canIclick=1;
$("#SZ0_1").animateSprite("play", "fire");
//increment our shots
current_shots++;
//play the fire sound
$.playSound('sounds/fire');
//check to see if we have reached the maximum
if(current_shots>=max_shots){
//show the reload button
$("#SZ0_2").css({opacity:1});
}//if
}
}
//array to keep track of the zombie hits
var zombieHits_counter = [0,0,0,0,0,0];
//array for each zombies limit
var zombieHits_limits = [2,1,3,2,1,3];
//this function will keep track
of the zombie hits and act accordingly
function zombieHit(whichOne, xx, yy){
//increment the counter
zombieHits_counter[whichOne]++;
//check to see if this zombie has reached its limit
if(zombieHits_counter[whichOne] >= zombieHits_limits[whichOne]){
//reset this zombie
SZ_resetZombie(whichOne+1,1);
}
//let's add in our special effect
var whichOne2=whichOne+1;
var $effect_zombiex = $("#zombie_effect"+whichOne2);
//let's re-position our bubble zombie to our stored value
$effect_zombiex.css({top: yy+'px',left: xx+'px', opacity:1});
$effect_zombiex.animateSprite("play", "z1");
//apply the scale
$effect_zombiex.css('transform','scale('+scalex_zombie[whichOne]+')');
}
保存文件,然后关闭它。
我们现在准备测试!回到My_Work_Files
文件夹,双击default.html
文件。当你开枪的时候,你应该会听到声音。此外,当您按下重新加载按钮时,您应该会听到重新加载的声音。同样,我相信你会同意这给我们的游戏增加了一个非常需要的维度。
声音不起作用吗?首先,确保您已经按照您的default.html
中所示编写了下面的代码行。
<script src="sounds/jquery.playSound.js"></script>
另外,确保已经复制了sounds
文件夹,如第一部分所示。
如果仍然不起作用,请确保复制如下所示的行;注意小写字母。
$.playSound('sounds/reload');
$.playSound('sounds/fire');
如果还是不行,请不要犹豫,在 Twitter 上给我发消息(@zarrarchishti
)。
嵌入游戏
你已经注意到游戏跨越了你的整个浏览器窗口。虽然有一些游戏是以这种方式运行的,但是大多数游戏都嵌入在一个较小的窗口中。我们将通过对我们的文件做一些调整,把我们所有的代码放在一个窗口中。
第一部分:入门
转到My_Work_Files
文件夹的Raw Images
文件夹中的images
文件夹。找到名为html_web
的文件夹,并将其复制到My_Work_Files
文件夹,现在看起来应该是这样的:
第二部分:修改 default.html 文件
要嵌入游戏,首先需要修改default.html
文件。打开default.html
文件并输入以下新行(所有新文本以粗体显示):
<html>
<head>
<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="sounds/jquery.playSound.js"></script>
<script src="js/SZ_main.js"></script>
<script src="js/SZ_setupContent.js"></script>
<script src="js/SZ_movement.js"></script>
<script src="js/ss.js"></script>
<script src="js/SZ_SS.js"></script>
<script src="js/SZ_touch.js"></script>
<script src="js/SZ_zombie_movement.js"></script>
<link href="css/SZ_master.css" rel="stylesheet" />
</head>
<body>
<div id="logo"></div>
<div id="box1"></div>
<div id="SZ_maincontent">
<img id="SZ0_0" src="img/SZ_background_image.jpg" onmousemove="rotateGun(event)" onmousedown="fireGun(event)" />
<div id="SZ0_1" ></div>
<div id="SZ0_2" >
<img src="img/SZ_reload.png" onmousedown="reloadGun(event)" />
</div>
<div id="SZ0_3" style="background-image: url(img/SZ_score.png);">
<div id="textx">999</div>
</div>
<div id="SZ0_4" onmousedown="start_game();"/>
</div>
</body>
</html>
保存文件,然后关闭它。
现在打开SZ_master.css
文件,键入以下新行(所有新文本都用粗体显示)和一些修改过的行(都用红色显示):
html {
height: 100%;
background: url(../html_web/webBG.jpg); background-size:cover;
}
body {
padding: 0 0 0 0;
margin: 0;
user-select: none;
cursor: crosshair;
}
img {
max-width: 100%;
height: 100%;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#logo {
position: absolute;
z-index:9999;
background: url(../html_web/logo.png); background-size:cover;
pointer-events:none;
}
#box1 {
position: absolute;
z-index:9998;
background: url(../html_web/box.png); background-size:cover;
pointer-events:none;
}
#SZ_maincontent {
position: relative;
overflow: hidden;
}
#SZ0_0 {
position: absolute;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
}
#SZ0_1 {
position: absolute;
bottom: 0;
right: 0;
opacity:0;
}
#SZ0_2 {
position: absolute;
top: 0;
left: 0;
cursor: pointer;
opacity:0;
}
#SZ0_3 {
position: absolute;
top: 0;
right: 0;
opacity:0;
background-size:cover;
}
#SZ0_4 {
position: absolute;
cursor: pointer;
background-size:cover;
opacity:0;
}
#textx {
position: relative;
float: left;
top: 40%;
text-align:center;
font-size: 4vmax;
font-weight: bolder;
colour: white;
font-family: "Arial Black";
}
保存此文件,然后关闭它。
你差不多完成了。打开SZ_setupContent.js
文件,键入以下新行(所有新文本以粗体显示)和一些修改过的行(全部以红色显示):
//we will need a new ratio var
var ratio_use = ratio;
//main function
function main_call_setupContent() {
//need to resize all elements
//first we set their normal sizes in CSS
//Main Div
$('#SZ_maincontent').css('width', 600 * ratio);
$('#SZ_maincontent').css('height', 400 * ratio);
//make sure it is half way
$('#SZ_maincontent').css('left', ($(window).width()/2)-((600 * ratio)/2));
$('#SZ_maincontent').css('top', ($(window).height()/2)-((400 * ratio)/2));
//box1
$('#box1').css('width', 631 * ratio);
$('#box1').css('height', 457 * ratio);
//make sure it is half way
$('#box1').css('left', ($(window).width()/2)-((637 * ratio)/2));
$('#box1').css('top', ($(window).height()/2)-((457 * ratio)/2));
//logo
$('#logo').css('width', 400 * ratio);
$('#logo').css('height', 146 * ratio);
//make sure it is half way
$('#logo').css('left', 0);
$('#logo').css('top', 0);
//Gun
$('#SZ0_1').css('width', 150 * ratio);
$('#SZ0_1').css('height', 150 * ratio);
//Reload Button
$('#SZ0_2').css('width', 200 * ratio);
$('#SZ0_2').css('height', 90 * ratio);
//Score
$('#SZ0_3').css('width', 235 * ratio);
$('#SZ0_3').css('height', 100 * ratio);
//Intro and Game over
if($(window).height()<$(window).width()){
//work out a ratio based on height
ratio_use = $(window).height()/800;
}//end if
//apply this new ratio to our intro/game over
$('#SZ0_4').css('width', 458 * ratio);
$('#SZ0_4').css('height', 370 * ratio);
$('#SZ0_4').css('left', 71 * ratio);
// $('#SZ0_4').css('left', ($(window).width()/2)-((600 * ratio_use)/2));
//make sure it is half way
//$('#SZ0_4').css('top', ($(window).height()/2)-((400 * ratio_use)/2));
$('#textx').css('width', '100%');
$('#textx').css('height', '50%');
//Any sprite sheets?
//Our Gun
setup_gun_SS();
//Create all our 6 zombies
for (i = 1; i < 7; i++) {
//this will get called 6 times
SZ_createZombie(i);
}
//call the intro
start_end_game(0);
}
var gameEnded=0;
//Intro or Game Over of game
function start_end_game(whichOne) {
//hide the elements
for (i = 1; i < 4; i++) {
//this will get called 3 times
$('#SZ0_'+i).css({opacity:0});
}//for
//hide the zombies
for (i = 1; i < 7; i++) {
//we need to stop this zombies animations
$('#zombie_'+i).stop();
$('#zombie_'+i).css({opacity:0});
$('#bubble_zombie_'+i).css({opacity:0});
//set the zindex for the zombie
$("#zombie"+i).css("z-index", i+100);
}//for
if(whichOne==0){
//START OF GAME
//change the background image
$('#SZ0_4').css('background-image', 'url(img/splash_intro.png)');
} else {
//GAME OVER
//show the score
$('#SZ0_3').css({opacity:1});
//change the background image
$('#SZ0_4').css('background-image', 'url(img/splash_gameover.png)');
}
//make sure it is half way
$('#SZ0_4').css('top', 0);
//finally show the intro or game over image
$('#SZ0_4').css({opacity:1});
//stop the user from firing
gameEnded= 1;
}//end of function
//need to store the current score
var current_score=0;
//we can call this function to update the score
function updateScore(){
$("#textx").text(current_score);
}
//start the game
function start_game() {
//reset the score
current_score=0;
updateScore();
//reset the zindex
zindex_current=0;
//reload the gun
current_shots=0;
//allow user to fire
gameEnded= 0;
//hide the intro or game over image
$('#SZ0_4').css({opacity:0});
//make sure it is out of the way
$('#SZ0_4').css('top', ($(window).height()));
//show the elements
for (i = 1; i < 4; i++) {
//this will get called 3 times
$('#SZ0_'+i).css({opacity:1});
}//for
//hide the reload button!
$('#SZ0_2').css({opacity:0});
//show the zombies
for (i = 0; i < 7; i++) {
//reset the Zombie
SZ_resetZombie(i,0);
}//for
//ensure the score board is half opacity
$('#SZ0_3').css({opacity:0.5});
}//end of function
保存此文件,然后关闭它。
最后,打开SZ_zombie_movement.js
文件,键入以下新行(所有新文本都用粗体显示)和一些修改过的行(都用红色显示):
//let's create a zombie
function SZ_createZombie(whichOne){
//create a new div to hold the zombie SS
var div = document.createElement('div');
//and another for the bubble zombie SS
var div2 = document.createElement('div');
//and another for the special effect SS
var div3 = document.createElement('div');
//we need to hard code the CSS styles we want
div.setAttribute('style','position: fixed; top:0; left:0; opacity:0; position: absolute; display: inherit;');
//and the same for our bubble zombie
div2.setAttribute('style','position: fixed; top:0; left:0; position: absolute;');
//and the same for our special effect SS
div3.setAttribute('style','position: fixed; top:0; left:0; position: absolute;');
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's position our zombie
div.style.left = left_position+'px'; div.style.top = top_position+'px';
//and the same for our bubble zombie
div2.style.left = left_position+'px'; div2.style.top = top_position+'px';
//and the same for our special effect SS
div3.style.left = left_position+'px'; div3.style.top = top_position+'px';
//give it an id
div.id = 'zombie'+whichOne;
//also for our bubble zombie
div2.id = 'bubble_zombie'+whichOne;
//also for our special effect SS
div3.id = 'zombie_effect'+whichOne;
//finally let's add our zombie to the screen
//document.body.appendChild(div);
$('#SZ_maincontent').append(div);
//finally add in our bubble zombie to the screen too
//document.body.appendChild(div2);
$('#SZ_maincontent').append(div2);
//finally add in our special effect SS to the screen too
document.body.appendChild(div3);
//put this new zombie through our SS function
setup_zombie_SS(whichOne);
//put this new zombie through our animate function
// SZ_animateZombie(whichOne);
//hide the bubble zombies at the start
$("#bubble_zombie"+whichOne).css('transform','scale('+0+')');
//ensure no hits are registered on the special effects
$("#zombie_effect"+whichOne).css( 'pointer-events', 'none' );
//set the zindex for the zombie
$("#zombie"+whichOne).css("z-index", whichOne+100);
//set the zindex for the bubble zombie
$("#bubble_zombie"+whichOne).css("z-index", whichOne);
//set the zindex for the special effect SS
$("#zombie_effect"+whichOne).css("z-index", whichOne+150);
//ensure the zindex for the gun is the highest
$("#SZ0_1").css("z-index", 200);
//also ensure the zindex for the intro/game over is the highest
$("#SZ0_4").css("z-index", 201);
//bind the users mouse click to this zombie
$("#zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
//acknowledge the hit
if($("#zombie"+whichOne).css('opacity') != 0){
var offset = $(this).offset();
zombieHit(whichOne-1, e.pageX, e.pageY);
}
}
});
//bind the users mouse click to the bubble zombie
$("#bubble_zombie"+whichOne).bind('mousedown touchstart', function (e) {
//make sure the reload button is showing
if($("#SZ0_2").css('opacity') != 1) {
//first we want to fire the gun
fireGun(event);
}
});
}
//we need to keep track of the current scale values
var scalex_zombie = [0,0,0,0,0,0];
//we also need to keep track of the left position
var leftx_zombie = [0,0,0,0,0,0];
//let's animate our zombie towards us
function SZ_animateZombie(whichOne){
//assign the speed for each of our zombies
var timex = [13000,8000,16000,14000,10000,18000];
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//reset the zombies scale value
$zombiex.css('transform','scale('+0+')');
//reset the zombies opacity
$zombiex.css({opacity:1});
//work out the amount the zombie has to come towards us
var amty = ($(window).height()*0.7);// -($zombiex.height()*2));//topx);
//each type of zombie will have their own walking style
var ZS_ease = ['easeInSine','easeOutQuart','easeInOutQuad','easeInSine','easeOutQuart','easeInOutQuad'];
//finally we are ready to animate
$zombiex.delay(timex[whichOne-1]/3).animate({
//first bring our zombie slowly down the screen
left: "+="+0.001+ "px",
},{ easing:ZS_ease[whichOne-1], duration: timex[whichOne-1],
step: function(now, fx){
//at each step we can manipulate the scale of our zombie
if (fx.prop == "left") {
//work out the amount to scale
var xx = (fx.pos)*16;
if(gameEnded==1){
xx=999;
}
//do a check to see if we should end this animation
if(xx>15){
//stop all animation
$(this).stop();
//call a function to reset this zombie
//SZ_resetZombie(whichOne,0);
//game Over
$(this).css({opacity:0});
$(this).stop(true, true);
$(this).finish();
if(gameEnded==0 && xx!=999){
start_end_game(1);
}
} else {
//apply the scale
$(this).css('transform','scale('+xx+')');
//record this new scale value
scalex_zombie[whichOne-1]=xx;
//check the depth levels
var i = 0;
while (i < 6) {
//check to see if the scale is bigger
if(scalex_zombie[whichOne-1]>scalex_zombie[i] && ($(this).zIndex() < $("#zombie"+(i+1)).zIndex()) && scalex_zombie[i]!=0){
var i_index = $("#zombie"+(i+1)).zIndex();
//change the i one first
$("#zombie"+(i+1)).css("z-index", $(this).css( "z-index" ));
//now change this one
$(this).css("z-index", i_index);
} //end of if
i++;
}//end of while loop
}
}
}, complete: function () {
}
});
}
//need to keep track of the current zindex for zombies
var zindex_current=0;
//a function to completely reset our zombie
function SZ_resetZombie(whichOne, zombieBubble_generate){
//reset this zombies hit counter
zombieHits_counter[whichOne-1]=0;
//assign a user friendly name for our div
var $zombiex = $("#zombie"+whichOne);
//we need to stop this zombies animations
$zombiex.stop();
//we want to position our zombie exactly at the tip of the planet
var top_position= $('#SZ0_0').height() * 0.435;
//should we generate a bubble zombie?
if(zombieBubble_generate==1){
//assign a user friendly name for our bubble zombie div
var $bubble_zombiex = $("#bubble_zombie"+whichOne);
//let's re-position our bubble zombie to our stored value
$bubble_zombiex.css({top: top_position+'px',left: $zombiex.css("left"), opacity:1});
//apply the scale
$bubble_zombiex.css('transform','scale('+scalex_zombie[whichOne-1]+')');
//call our bubble zombie animation function
bubbleZombie_flyAway(whichOne);
}
//Xpos can be anywhere on our x axis
var left_position = Math.floor(Math.random() * ($('#SZ0_0').width())-(ratio*50)) + (ratio*50);
//record this left position
leftx_zombie[whichOne-1]=left_position;
//let's re-position our zombie
$zombiex.css({top: top_position+'px', left: left_position+'px', opacity:0});
//set the zindex for the zombie
zindex_current++;
$("#zombie"+whichOne).css("z-index", zindex_current);
//finally let's make the zombie come towards the screen again
if(zombieBubble_generate==0){
SZ_animateZombie(whichOne);
}
}
保存此文件,然后关闭它。
我们现在准备测试!回到My_Work_Files
文件夹,双击default.html
文件。您应该会看到以下屏幕:
你会同意,仅仅通过一些代码的调整,我们已经在游戏的整体设计上创造了一个巨大的进步。
没用吗?这里写的很多代码都是对已经写好的代码的修改。一行一行地检查加粗的代码并确保它看起来和显示的完全一样是很重要的。
请密切注意SZ_master.css
中的变化,尤其是我们在几个地方添加了相同的行。
最后,注意像这样的变化:
// $('#SZ0_4').css('left', ($(window).width()/2)-((600 * ratio_use)/2));
请确保您已经完全按照显示的方式对这些行进行了编码。
如果它仍然不起作用,那么请不要犹豫,在 Twitter 上给我发消息(@zarrarchishti
)。
游戏是怎么变小的?
游戏本身完全一样;我们所做的只是减少游戏在屏幕上所占的空间:
$('#SZ_maincontent').css('width', 600 * ratio);
$('#SZ_maincontent').css('height', 400 * ratio);
我们如何在屏幕上显示额外的图形?
让我们分开来看每一个。围绕我们游戏的盒子是在下面的线中创建的:
<div id="box1"></div>
左上角的标志是用
<div id="logo"></div>
最后,我们把背景图像使用
background: url(../html_web/webBG.jpg); background-size:cover;
游戏结束。重启?
我们的开发到此结束。我希望你和我一样喜欢编写这个游戏。我也希望你已经对游戏开发产生了热情,并继续创作一些真正令人敬畏的游戏。
如果你有任何问题或希望讨论你对其他游戏的想法,请联系我。
不知道该何去何从?我已经收集了一些关于如何进一步开发游戏的想法,现在你应该可以自己开发了。
- 如果您是数据库开发人员,您可能希望将分数记录到本地或服务器数据库中。为了更进一步,您可能想要创建一个屏幕来捕获用户的信息,比如他们的电子邮件地址。
- 有一个小蜘蛛一样的动画创建成精灵表。在游戏中随机加入这个。用户拍出来,给他们双倍积分。
- 我们在每章中都探讨了一些选项(例如,安装“头像”功能)。也许值得回去重温这些建议,并尝试自己重写代码。
- 放置一个暂停按钮。顾名思义,当点击时,所有的游戏都应该暂停。我通常会显示一个全屏图像,点击后会恢复游戏。
- 创建关卡!你需要一些方法来停止游戏并重置所有的游戏参数。此外,你会想给一些思考,为什么每个级别是不同的。我会在第一阶段介绍 Z 教授,然后在第二阶段介绍颠茄,最后在第三阶段介绍布拉德。在最后阶段,我会让三个僵尸随机出现。
- 最后,要有创意!用这个引擎做完全不同的事情。例如,我使用相同的引擎创建了一个马戏团主题的游戏。在这个游戏中,枪被替换为步枪,僵尸被替换为三种不同类型的目标(例如,一只鸭子)。目标会从左向右移动,并在三个不同的深度移动。虽然我不得不改变图形和微调代码,游戏的引擎保持不变。
让我知道你用这个引擎还创造了什么游戏。在 Twitter ( @zarrarchishti
)上和我一起讨论吧!