游戏资源
前面的章节已经教会了如何用一个构造的Game对象来写一个简单的游戏例子。你也发现JavaScript可以获取canvas和canvasContext的值,用来进行canvas的运算。你也知道了如何改变画布背景色。也知道结合系统时间让一个方块在屏幕上进行移动。这章讲解如何在屏幕上绘画图片,这是创造出好看游戏的第一步。在计算机图形学中,这些图形被称作精灵。精灵一般都从文件里进行加载。这意味着任何绘画精灵的程序不再仅仅只是一个简单的指令,而是需要知道这些游戏资源放在何处。即将讲的几个事情你需要思考下:
可以从哪里加载精灵?
怎样从一个图像文件里获取信息
怎样将精灵显示在屏幕上
这章将会回答这些问题。
音乐是另一种类型的游戏资源。它的处理与精灵十分类似。因此,在本章的最末,你会看到如何在你的游戏中播放音乐和音效。
定位精灵
在一个程序可以使用任何资源前,需要知道在哪里找到这些资源。默认情况下,浏览器会自动在当前文件夹下寻找精灵,和寻找JavaScript文件一样。看这章的SpriteDrawing例子,你会发现一个spr_balloon.png文件和HTML文件和JavaScript文件在一个文件夹下。
加载精灵
现在看如何从一个文件里面加载精灵。通过一个变量来加载精灵。你需要在游戏循环里不同的地方使用这个变量。在start方法里,加载这个精灵并且存放在一个变量里。因此,在Game对象中添加一个 balloonSprite变量。如下所示:
var Game = {
canvas : undefined,
canvasContext : undefined,
balloonSprite : undefined
};
可以定义一个对象来包含关于这个精灵的所有信息。如下:
Game.balloonSprite = {
src : "spr_balloon.png",
width : 35,
height : 63,
...
}
如果你想在你的游戏里加载成百上千的精灵时,上述的就有问题了。每次你都必须定义一个类似上面的对象字变量。另外,你并不确定是否会用到一个对象里面的其他变量。因为一个图片有不同的表现。幸好,你可以通过类来避免这些问题。
类的基本定义就是一个对象的原型。它是一个对象的抽象。比如,JavaScript知道一个叫做Image的类,这个类定义一个关于图片的对象,对象有宽和高,有源文件地址等等。通过image类可以非常简单的创建一个对象,只需要只要new关键字:
Game.balloonSprite = new Image();
这比在一个对象里写出所有变量的值轻松多了,表达式new Image()做到了这些。通过使用类,你可以轻松的创造对象。而且可以确定这些对象都有相同的结构。
你还不知道这个变量里面究竟含有什么。你可以给src变量赋值为图片源文件的地址。
Game.balloonSprite.src = "spr_balloon.png";
当src一设置,浏览器就开始加载文件。而且浏览器自动的获取图片的宽和高。
有时候,加载源文件需要一些时间。比如,这个文件存在于世界另一边的网站上。这意味着如果你想马上就看到图片是很困难的。因此,你需要确保在游戏启动之前所有图片都加载完毕。这里有一个非常简洁的方式,使用一个事件处理函数。第七章你就会知道它是如何工作的。现在,想象半秒之内就可以加载这个图片。通过使用setTimeout方法,你可以在500ms后加载mainLoop方法。
window.setTimeout(Game.mainLoop, 500);
完成后的start方法如下:
Game.start = function () {
Game.canvas = document.getElementById("myCanvas");
Game.canvasContext = Game.canvas.getContext("2d");
Game.balloonSprite = new Image();
Game.balloonSprite.src = "spr_balloon.png";
window.setTimeout(Game.mainLoop, 500);
};
精灵可以从任何地方加载。如果你用JavaScript开发游戏,思考如何组织你的精灵是个好习惯。比如,你可以把你所有的精灵放在一个叫做sprites的文件夹里。那样你就可以像下面这样设置源文件。
Game.balloonSprite.src = "sprites/spr_balloon.png";
也许你不会使用自己的图片,而是来自互联网的图片:
Game.balloonSprite.src = "http://www.somewebsite.com/images/spr_balloon.png";
JavaScript允许你从任何你想的地方加载图片。只是但从互联网上加载图片时,需要确认图片地址是固定不变的。否则,当网站主人在没有告知你的情况下移动掉这些文件,你的游戏将无法运行。
绘画精灵
加载精灵并不意味着精灵就显示到了屏幕上。想要让精灵显示出来,需要在draw方法里面做些事情。为了在画布上画出精灵,你可以使用canvasContext对象的一个方法:drawImage。在JavaScript里,当图片需要显示在固定的位置,这个位置的坐标通常是图片的左上角。下面是显示精灵的指令:
Game.canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height,
0, 0, sprite.width, sprite.height);
drawImage方法有很多不同的参数。比如,你可以指定在何处绘画精灵,或者是否只是精灵的一部分进行显示。你可以简单的调用此方法达到目的。然而,如果你考虑到你以后创建的游戏,你可以使用绘画状态来绘画精灵。
绘画状态从根本上来说就是一组参数,这些参数用于所有绘画之间的转换。使用绘画状态而不是用单一的drawImage方法的好处是你可以对精灵做更复杂的转换。比如,使用绘画状态,你可以翻转或者比例缩放精灵,这在以后的游戏中非常有用。创造一个新的绘画状态是通过调用save方法。
Game.canvasContext.save();
现在你可以使用这个绘画状态来进行各种转换。比如,你可以移动精灵到一个确定位置:
Game.canvasContext.translate(100, 100);
如果你现在调用drawImage方法,精灵会被绘画在(100,100)处。一旦你完成绘画,你可以像下面这样移除绘画状态。
Game.canvasContext.restore();
为了方便,定义一个可以做上述一切的方法:
Game.drawImage = function (sprite, position) {
Game.canvasContext.save();
Game.canvasContext.translate(position.x, position.y);
Game.canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height,
0, 0, sprite.width, sprite.height);
Game.canvasContext.restore();
};
通过上述参数看出,这个方法需要两个信息,要被绘画的精灵和绘画精灵所在的位置。精灵应该是image类,这个地址是一个含有x和y部分的对象。当你调用这个方法,你需要提供上述这些信息。比如,像下面这样在(100,100)出画一个气球精灵:
Game.drawImage(Game.balloonSprite, { x : 100, y : 100 });
你使用花括号定义了一个含有x和y的对象。正如你看到的那样,允许在调用方法的指令中定义一个对象。当然,你可以开始就定义一个对象,储存在变量里,比如下面这样:
var balloonPos = {
x : 100,
y : 100
};
Game.drawImage(Game.balloonSprite, balloonPos);
这段代码和之前的代码是一样的,只是多写了些。你可以把drawImage方法放在draw方法里面,这样气球就会显示在你想要的位置:
Game.draw = function () {
Game.drawImage(Game.balloonSprite, { x : 100, y : 100 });
};
在浏览器上看到的结果如图4-1
(省略图4-1)
移动精灵
现在你可以在屏幕上显示一个精灵了,你可以使用游戏循环来让它移动,就像上章里面让方块移动那样。在这里我们让气球的位置基于经过的时间来进行移动。为了完成这个,你需要储存气球的位置。需要在update方法计算位置和在draw方法里绘画气球。因此,在Game对象里面添加一个位置变量:
var Game = {
canvas : undefined,
canvasContext : undefined,
balloonSprite : undefined,
balloonPosition : { x : 0, y : 50 }
};
如上所示,在game对象里面定义了一个含有两个变量的对象。现在你可以在update方法里面添加一条通过经过的时间来改变位置的指令,就像在移动方块例子里面一样。如下:
Game.update = function () {
var d = new Date();
Game.balloonPosition.x = d.getTime() % Game.canvas.width;
};
剩下的就是画出气球:
Game.drawImage(Game.balloonSprite, Game.balloonPosition);
加载和绘画多重精灵
创建一个只有白色背景的游戏怎么看都是乏味的。你可以让你的游戏通过绘画一个背景精灵来使其更具有可看性。这意味着你需要在start方法里面加载另一个精灵并且在draw里面进行显示。这个程序的最终版本叫做FlyingSprite。如果你在浏览器里面打开的话,你会看见两个精灵,一个背景图和一个气球。为了实现这个,增加一个含有背景精灵的变量,如下所示:
var Game = {
canvas : undefined,
canvasContext : undefined,
backgroundSprite : undefined,
balloonSprite : undefined,
balloonPosition : { x : 0, y : 50 }
};
当然,在draw里面,现在有两个要绘画方法调用:
Game.draw = function () {
Game.drawImage(Game.backgroundSprite, { x : 0, y : 0 });
Game.drawImage(Game.balloonSprite, Game.balloonPosition);
};
这些方法调用的顺序是非常重要的。因为你想气球显示在背景上面。首先就得画出背景,然后画气球。如果你反过来做,背景会覆盖掉气球,你将什么也看不到。图4-2展示了程序的输出结果:
(省略图4-2)
每次你想在屏幕上绘画一个精灵,你就在draw方法里多调用一次drawImage,你可以一个精灵绘画多次,显示在不同的位置。比如,你想在背景上画出一些不同位置的气球,你只需要调用多次调用drawImage和气球的位置参数:
Game.draw = function () {
Game.drawImage(Game.backgroundSprite, { x : 0, y : 0 });
Game.drawImage(Game.balloonSprite, { x : 0, y : 0 });
Game.drawImage(Game.balloonSprite, { x : 100, y : 0 });
Game.drawImage(Game.balloonSprite, { x : 200, y : 0 });
Game.drawImage(Game.balloonSprite, { x : 0, y : 300 });
Game.drawImage(Game.balloonSprite, { x : 200, y : 300 });
};
当然,再次注意绘画的顺序。
你可以同时画出多个移动的精灵。对于每一个气球,你可以定义一个专属于它的位置变量,如下所示:
Game.update = function () {
var d = new Date();
Game.balloonPosition1.x = d.getTime() % Game.canvas.width;
Game.balloonPosition2.x = (d.getTime() + 100) % Game.canvas.width;
Game.balloonPosition3.x = (d.getTime() + 200) % Game.canvas.width;
};
在draw方法里,可以用这些变量画出静止的和移动的气球:
Game.draw = function () {
Game.drawImage(Game.backgroundSprite, Game.balloonPosition1);
Game.drawImage(Game.balloonSprite, Game.balloonPosition2);
Game.drawImage(Game.balloonSprite, Game.balloonPosition3);
Game.drawImage(Game.balloonSprite, { x : 200, y : 0 });
Game.drawImage(Game.balloonSprite, { x : 0, y : 300 });
Game.drawImage(Game.balloonSprite, { x : 200, y : 300 });
};
音乐和音效
另一个常见的游戏资源是音效。很多游戏都有音效和背景音乐。这有很多重要的原因。音效可以告诉用户某些重要的线索。比如,当用户按下一个键时有一个确定的点击音效。听见脚步声表示敌人接近了,即使玩家没有看见他们。老游戏迷雾之岛就是一个典型的这类型的游戏,因为里面的很多线索都是通过音效来找到的。
好的音效能给游戏世界带来更好的感受。它们让环境更真实,即使屏幕上什么都没有发生。
在JavaScript中播放背景音乐或音效是非常简单的。使用音效之前首先需要音效文件。在FlyingSpriteWithSound这个例子里,文件是snd_music.mp3,被当做背景音乐。跟储存精灵一样,需要一个变量来储存音乐数据。如下:
var Game = {
canvas : undefined,
canvasContext : undefined,
backgroundSprite : undefined,
balloonSprite : undefined,
balloonPosition : { x : 0, y : 50 },
backgroundMusic : undefined
};
在start方法里需要加载音效。JavaScript提供了一个可以操作音效的类。类型叫做Audio,创建方法如下:
Game.backgroundMusic = new Audio();
Game.backgroundMusic.src = "snd_music.mp3";
上面看起来就跟加载精灵一样。现在你可以这个对象的方法了,比如下面这样播放音效:
Game.backgroundMusic.play();
也可以像下面这样减少背景音乐的声音:
Game.backgroundMusic.volume = 0.4;
volume变量的值的大小范围是0到1,0表示没有声音,1表示声音最大。
从技术上来说,背景音乐和音效之间没有什么不同。一般,背景音乐声音要小点。很多背景音乐在游戏循环里面播放,完了后又重新开始。后面你将看到怎么实现。这本书的所有游戏都用到了声音来让游戏更有趣。
你学到了什么
这章里,你学到了:
- 如何加载游戏资源比如精灵和音效
- 如何在屏幕上绘画多个精灵并且移动它们
- 如何在你的游戏里播放背景音乐和音效