
拼图游戏是指将一幅图片分割成若干拼块并将它们随机打乱顺序,当将所有拼块都放回原位置时,就完成了拼图(游戏结束)。
游戏介绍
在“游戏”中,单击滑块选择游戏难易,“容易”为3行3列拼图游戏,中间为一个4行4列拼图游戏,“难”为5行5列拼图游戏。拼块以随机顺序排列,玩家用鼠标单击空白块的四周来交换它们的位置,直到所有拼块都回到原位置。
拼图游戏的运行界面如图7-1所示。
■ 图7-1拼图游戏的运行界面
游戏设计的思路
HTML5可以把图片整合到网页中。使用Canvas元素可以在这个空白的画布上填充线条,载入图片文件,甚至动画效果。这里制作拼图游戏用来展示HTML5 Canvas的图片处理能力。
游戏程序首先显示以正确顺序排列的图片缩略图,根据玩家设置的分割数,将图片分割成相应tileCount行列数的拼块,并按顺序编号。动态生成一个大小tileCount×tileCount的数组boardParts,用于存放0、1、2到tileCount×tileCount-1的数,每个数字代表一个拼块。例如,4×4的游戏拼块编号如图7-2所示。
■ 图7-2拼块编号示意图
游戏开始时,随机打乱这个数组boardParts,假如boardParts[0]是5,则在左上角显示编号是5的拼块。根据玩家用鼠标单击的拼块和空白块所在位置,来交换该boardParts数组对应的元素,最后依据元素排列顺序来判断是否已经完成游戏。
游戏设计的步骤
1. 游戏页面
<!doctype html>
<html>
<head>
<title>拼图游戏</title>
<style>
.picture {
border: 1px solid black;
}
</style>
</head>
<body>
<div id="title">
<h2>拼图游戏</h2>
</div>
<div id="slider">
<form>
<label>容易</label>
<input type="range" id="scale" value="4" min="2" max="5" step="1">
<label>难</label>
<img id="source" width="120px" height="120px" src="defa.jpg" >
</form>
<br>
</div>
<div id="main" class="main">
<canvas id="puzzle" width="480px" height="480px"></canvas>
</div>
<script src="sliding.js"></script>
</body>
</html>复制
在网页中使用Canvas标记创建画板。
<canvas width="480px" height="480px"></canvas>
复制
Canvas的宽度和高度使用像素为单位。如果这两个属性没有被指定,它们的默认宽度为300px,高度为150px。
网页中< div id="slider">包括了另一个HTML5标记: range input,这个标记<input type="range">可以让用户拖放滑块并选择一个数值。这里设置滑块最小值为3,最大值为5。滑块值为3表明拼图游戏是3行3列的,滑块值为4表明拼图游戏是4行4列的,滑块值为5表明拼图游戏是5行5列的。
<img id="source" width="120px" height="120px" src="defa.jpg" >显示原图defa.jpg的缩小图,供玩家参照移动拼块。
2. sliding.js文件
在页面上画图需要使用Canvas的上下文环境,通过调用getContext()方法获取上下文环境。
var context = document.getElementById('puzzle').getContext('2d');
复制
然后,还需要一个和Canvas相同大小的图片'defa.jpg'。
var img = new Image();
img.src = 'defa.jpg';
img.addEventListener('load', drawTiles, false);//load事件监听,即图片加载完成事件复制
加入这个'load'事件是确保图片完成加载后,再把图片放入Canvas中。drawTiles()函数绘制打乱的图块。
var boardSize = document.getElementById('puzzle').width; //获取画板(画布)的宽度
var tileCount = document.getElementById('scale').value; //获取滑块的值复制
boardSize是Canvas的宽度,通过range input设置拼图的数量tileCount,数据范围从3到5(几行几列)。
var tileSize = boardSize tileCount; //计算出拼块的大小宽度
复制
最后定义3个变量,其中两个Object对象变量,emptyLoc保存空白拼图的位置(emptyLoc.x,emptyLoc.y),clickLoc记录用户单击的位置(clickLoc.x,clickLoc.y)。而一个bool变量solved是指拼图是否完成,所有的拼图都找到正确的位置后,设置为True。
var context = document.getElementById('puzzle').getContext('2d');
var img = new Image();
img.src = 'defa.jpg';
img.addEventListener('load', drawTiles, false); //load事件监听,即图片加载完成事件
var boardSize = document.getElementById('puzzle').width; //获取画板(画布)的宽度
var tileCount = document.getElementById('scale').value; //获取滑块的值
var tileSize = boardSize tileCount;
var clickLoc = new Object; //记录单击鼠标所在的位置
clickLoc.x = 0;
clickLoc.y = 0;
var emptyLoc = new Object; //记录空白拼图的位置
emptyLoc.x = 0;
emptyLoc.y = 0;
var solved = false; //拼图是否完成,false为未完成复制
下面实现拼块的随机排列——使用一个一维数组存储每个拼块的编号。每个元素代表一个拼块,初始时元素的数组下标与拼块的编号相同,说明位置正确。所以需要打乱数组的元素顺序,实现拼块的随机排列。而数组的元素顺序打乱使用带有排序函数的Array.sort()方法来实现。
var boardParts = new Object();
initBoard(); //初始化拼块,并随机排列
function initBoard() {
boardParts = new Array(tileCount * tileCount);
for (var i = 0; i < tileCount * tileCount; i++) {
boardParts[i] = i;
}
shift(); //拼块的随机排列
}
function sortNumber(a,b) {//随机排序函数
return Math.random() > 0.5 ? -1 : 1;
//return a-b;
}
function shift() { //拼块的随机排列
boardParts.sort(sortNumber);
emptyLoc.x = 0;
emptyLoc.y = 0;
solved = false;
}复制
以上就实现了拼块的随机放置。但是真正显示拼块在屏幕上的是drawTiles()函数。drawTiles()函数用于显示各个拼块,该函数判断是否是空白拼图的位置(emptyLoc.x,emptyLoc.y),不是则调用drawImage()函数绘制相应图块。
drawImage()函数最常用的是传入3个参数:image对象,以及图片相对于画布的x、y坐标。
drawImage(image, x, y);
复制
还可以加入两个参数用于设置图片的宽度和高度。
drawImage(image, x, y, width, height);
复制
最复杂的drawImage()函数有9个参数,按顺序分别为:图片对象、图片x坐标、图片y坐标、图片宽、图片高、目标x坐标、目标y坐标、目标宽和目标高。后4个参数主要是为了截取原图部分用来显示。这里把boardParts记录的拼块显示在(i*tileSize,j*tileSize)处。
//绘制所有拼块
function drawTiles() {
context.clearRect(0, 0, boardSize, boardSize);
for (var i = 0; i < tileCount; i++) { //列号
for (var j = 0; j < tileCount; j++) { //行号
var n = boardParts[i * tileCount + j];
//计算出编号为n的拼块在原图的位置坐标(行列号)
var x = parseInt(n tileCount); //丢弃小数部分, 保留整数部分
var y = n % tileCount;
console.log(x + ":" + Math.floor(n tileCount) + ":" + y);
if (i!= emptyLoc.x||j!= emptyLoc.y||solved==true) {//不是空白拼图的位置且游戏未结束
//或者if( !(i== emptyLoc.x&&j== emptyLoc.y&&solved==false))可能更容易明白
//将编号为n的拼块显示在(i * tileSize, j * tileSize)处
context.drawImage(img, x * tileSize, y * tileSize, tileSize, tileSize,
i * tileSize, j * tileSize, tileSize, tileSize);
}
}
}
}复制
以下是事件定义。
首先为滑块定义触发事件,当它改变了,要重新计算拼块的数量和大小。滑块被移动时触发onchange事件,事件中计算拼块宽度大小,重新初始化画布,显示各个拼块。
document.getElementById('scale').onchange = function() {
tileCount = this.value;
tileSize = boardSize tileCount;// 计算拼块宽度大小
initBoard();//重新初始化拼块,并随机排列
drawTiles();//显示各个拼块
};复制
追踪鼠标经过的拼块以及哪个拼块被单击。画板中移动鼠标的onmousemove事件中,计算出鼠标所在网格坐标clickLoc.x,clickLoc.y。
document.getElementById('puzzle').onmousemove = function(e) {
clickLoc.x = Math.floor((e.pageX - this.offsetLeft) tileSize);
clickLoc.y = Math.floor((e.pageY - this.offsetTop) tileSize);
};复制
画布中单击鼠标的onmousemove事件中,计算出鼠标所在网格坐标clickLoc.x,clickLoc.y与空块位置间隔,如果间距为1则移动被单击的拼块。
document.getElementById('puzzle').onclick = function() {
if (distance(clickLoc.x, clickLoc.y, emptyLoc.x, emptyLoc.y) == 1) {
slideTile(emptyLoc, clickLoc); //交换被单击的拼块与空块位置
drawTiles(); //显示各个拼块
}
if (solved) {//如果成功
setTimeout(function() {alert("你成功了!");}, 500);
}
};
function distance(x1, y1, x2, y2) {
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
}复制
注意,一些浏览器会在重画画布之前弹出对话框,为了防止弹出,一定要用延迟。
setTimeout(function() {alert("你成功了!");}, 500);
复制
设置延迟0.5秒后再弹出提示框“你成功了!”。
slideTile(emptyLoc,clickLoc)是移动被单击的拼块clickLoc到空块位置emptyLoc。移动拼图的做法是:交换对应的boardParts元素,然后把单击位置设置成空块位置。
function slideTile(emptyLoc, clickLoc) {
if (!solved) {
var t;
t= boardParts[emptyLoc.x * tileCount+emptyLoc.y];
boardParts[emptyLoc.x*tileCount+emptyLoc.y]=boardParts[clickLoc.x*tileCount+clickLoc.y];
boardParts[clickLoc.x * tileCount + clickLoc.y] = t;
emptyLoc.x = clickLoc.x; //emptyLoc重新记录空白块位置
emptyLoc.y = clickLoc.y;
checkSolved(); //检查是否成功
}
}复制
一旦拼图移动了,还要检查一下拼图是否全部在正确的位置。checkSolved()函数检查是否成功。如果有一个拼块不正确,函数就会返回False,否则返回True。
function checkSolved() {
var flag = true;
for (var i = 0; i < tileCount * tileCount; i++) {
if (boardParts[i] != i) //判断元素排列顺序
flag = false;
}
solved = flag;
}复制
至此,完成拼图游戏的设计。
参考书籍
作者:夏敏捷、尚展垒
定价:69.90元
ISBN:9787302629771
编辑推荐:11个游戏实战案例,提供视频、源代码、教学课件、教学大纲、扩展案例
本书分为基础篇和实战篇。
基础篇包括第1~6章,主要讲解HTML5的基础知识和相关新技术,如JavaScript、Canvas API画图、CSS3和jQuery及其使用技巧;
实战篇包括第7~17章,综合应用前面技术,开发经典的大家耳熟能详的游戏,如人物拼图、扑克翻牌、推箱子、五子棋、黑白棋、俄罗斯方块、贪吃蛇、雷电飞机射击、Flappy Bird、中国象棋。通过本书读者将学会如何利用HTML5和JavaScript、CSS3制作交互式游戏、平台类游戏,学会网页游戏设计。



