前面的章节已经教会了如何用一个构造的Game对象来写一个简单的游戏例子。你也发现JavaScript能够获取canvas和canvasContext的值,用来进行canvas的运算。你也知道了如何改变画布背景色。也知道结合系统时间让一个方块在屏幕上进行移动。这章讲解如何在屏幕上绘画图片,这是创造出好看游戏的第一步。在计算机图形学中,这些图形被称做精灵。精灵通常都从文件里进行加载。这意味着任何绘画精灵的程序再也不仅仅只是一个简单的指令,而是须要知道这些游戏资源放在何处。即将讲的几个事情你须要思考下:web
能够从哪里加载精灵?canvas
怎样从一个图像文件里获取信息浏览器
怎样将精灵显示在屏幕上markdown
这章将会回答这些问题。函数
音乐是另外一种类型的游戏资源。它的处理与精灵十分相似。所以,在本章的最末,你会看到如何在你的游戏中播放音乐和音效。oop
在一个程序可使用任何资源前,须要知道在哪里找到这些资源。默认状况下,浏览器会自动在当前文件夹下寻找精灵,和寻找JavaScript文件同样。看这章的SpriteDrawing例子,你会发现一个spr_balloon.png文件和HTML文件和JavaScript文件在一个文件夹下。网站
如今看如何从一个文件里面加载精灵。经过一个变量来加载精灵。你须要在游戏循环里不一样的地方使用这个变量。在start方法里,加载这个精灵而且存放在一个变量里。所以,在Game对象中添加一个 balloonSprite变量。以下所示:rest
var Game = { canvas : undefined, canvasContext : undefined, balloonSprite : undefined };
能够定义一个对象来包含关于这个精灵的全部信息。以下:code
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表示声音最大。
从技术上来讲,背景音乐和音效之间没有什么不一样。通常,背景音乐声音要小点。不少背景音乐在游戏循环里面播放,完了后又从新开始。后面你将看到怎么实现。这本书的全部游戏都用到了声音来让游戏更有趣。
这章里,你学到了: