H5实现拼图游戏

H5滑动拼图小游戏

这篇文章是关于使用H5 canvas、css和JavaScript技术实现滑动拼图游戏。

1.游戏实现的基本功能

  • 游戏划分为简单级别和复杂级别。主要指的是图片划分的行列数不一样,这里的等级可以自己设定。
  • 页面载入完成,即可选择游戏等级和图片,进行拼图。页面加载完成,需要初始化绘图环境和画布尺寸及参考图片。
  • 点击工具条上的缩略图,可以改变右侧参考图片。
  • 点击游戏等级,切割图片完成,并且预留一张空白图片,拼图过程中,必须点击空白图片的相邻图片才可以移动图片块。
  • 当拼图区域的图片和原来图片方块位置都对应时,游戏结束。

2.游戏效果及实现思路

游戏效果
根据游戏效果我们可以将页面分为上下两部分:

  1. 最上面部分为菜单栏,可选择难度等级和要拼的图片。
    点击缩略图可切换图片。
    按照简单和复杂的级别,将图片分为不等的切片。
  2. 下面分为拼图区域和参考图区域。
  3. 根据右边的参考图片分为n个切片随机显示到左边拼图区,拼图区有一张空白图片,将其锁定在左上角。如果切片与空白图片相邻,则可移动;如不相邻,则不可移动。

3.开发步骤

3.1 页面

在这里插入图片描述
用div+css实现页面布局。
index.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<link rel="stylesheet" href="css/game.css" />
		<script type="text/javascript" src="js/game.js" ></script>
	</head>
	<body> 
		<div id="container">
			<div id="toolbar">
				<input type="button" name="" id="" value="简单"/>
				<input type="button" name="" id="" value="复杂"/>
				&nbsp;&nbsp;
				<img src="img/1.jpg" />
				<img src="img/2.jpg" />
				<img src="img/3.jpg" />
			</div>
			<div id="game_left">
				<canvas id="puzzle" width="600" height="600"></canvas>
			</div>
			<div id="game_right">
				<img id="cankao" src="img/1.jpg"/>
			</div>
		</div>
	</body>
</html>

game.css

#toolbar{
	height: 80px;
	border: 1px solid blue;
}
#toolbar img{
	width: 50px;
	height: 50px;
	margin-top: 10px;
}
#game_left{
	width: 60%;
	float: left;
	border: 1px solid blue;
	height: 600px;
}
#game_right{
	width: 39%;
	float: left;
	border: 1px solid blue;
	height: 600px;
}

以上就做好了前台的页面。

3.2 功能实现

3.2.1 页面加载完毕后进行初始化

当网页刷新后,要初始化绘图环境。即准好参考图片和画布、画笔。
注册页面加载完毕事件。

<body onload="ready()"> 

在游戏中需要将参考图片分为n个切片,以分为4个切片为例。
将图片分为2*2的图片,用二维数组存储图片。例:[[img1,img2],[img3,img4]]
其中img1里有两个属性,分别为图片的x轴坐标和y轴坐标来标识切片。
图片切片
在JavaScript里定义全局变量和ready()函数。

var tileArray=new Array(); //存储切片的二维数组
var img=new Image();//在画布中要绘制的图片
var imgSize=600; //图片大小
var ctx;//画笔
var emptyObj=new Object();//空白图片
//游戏准备
function ready(){
	var puzzle=document.getElementById("puzzle"); //获得canvas标签
	ctx=puzzle.getContext("2d");
	img.src=document.getElementById("cankao").src; //获取参考图片
}

3.2.2 改变参考图片

实现的效果为点击上方缩略图,然后显示下方参考图片。
先在html里注册图片的onclick事件

<img src="img/1.jpg" onclick="changePic(this)"/>
<img src="img/2.jpg" onclick="changePic(this)"/>
<img src="img/3.jpg" onclick="changePic(this)"/>

js定义changePic()函数

//改变参考图片
function changePic(ele){
	document.getElementById("cankao").src=ele.src;
}

3.2.3 选择游戏级别并将图片绘制到画布

点击简单或复杂按钮,初始化canvas图片分割。
首先给游戏级别按钮注册initGame(num)点击事件,根据传入的num对原图片进行num*num的分割,将原图片每个切片的坐标按顺序存入到tileArray二维数组中,然后将数组洗牌打乱,空白图片设置为第0行第0列。
这里面涉及到两套坐标系,第一套是原图片的坐标系,第二套是画布的坐标系。
先将图片切割,按顺序将参考图片画到画布上。
给按钮注册onclick事件

<input type="button" name="" id="" value="简单" onclick="initGame(2)"/> //分割为2×2的图片
<input type="button" name="" id="" value="复杂" onclick="initGame(4)"/>//分割为4×4的图片

js定义initGame()函数

//根据游戏级别初始化
function initGame(num){   
	//将原图片坐标存入二维数组
	for (var i = 0; i < num; i++) {
		tileArray[i]=new Array(); //行
		for(var j = 0; j < num; j++){
			var obj=new Object(); //定义一个对象
			obj.x=i;
			obj.y=j;
			tileArray[i][j]=obj; //列
		}
	}
	//按照二维数组绘制图片到画布
	redraw();
}

需定义每个切片的大小

var tilelen;//切片大小
//根据游戏级别初始化
function initGame(num){
	//得到切片大小
	tilelen=imgSize/num;     
}

当更改图片时,img的src路径会改变

//改变图片
function changePic(ele){
	document.getElementById("cankao").src=ele.src;
	img.src=ele.src;
}

js定义redraw()函数,在画布中绘制图片

//重新绘制图片
function redraw(){
	var num=tileArray.length; //二维数组的长度
	for (var i = 0; i < num; i++) {
		for(var j = 0; j < num; j++){
			//原图片的坐标
			var curimg=tileArray[i][j];//得到真实图片的坐标
			//绘制一个小切片
			ctx.drawImage(img,curimg.x*tilelen,curimg.y*tilelen,tilelen,tilelen,i*tilelen,j*tilelen,tilelen,tilelen);
		}
	}
}

canvas绘制图像说明:
剪切图像,并在画布上定位被剪切的部分:

context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);

参数值

参数 描述
img 规定要使用的图像、画布或视频。
sx 可选。开始剪切的 x 坐标位置。
sy 可选。开始剪切的 y 坐标位置。
swidth 可选。被剪切图像的宽度。
sheight 可选。被剪切图像的高度。
x 在画布上放置图像的 x 坐标位置。
y 在画布上放置图像的 y 坐标位置。
width 可选。要使用的图像的宽度。(伸展或缩小图像)
height 可选。要使用的图像的高度。(伸展或缩小图像)

选择游戏级别并将图片绘制到画布

3.2.4 将画布上的切片随机排列

在初始化游戏还没有重新绘制之前,对二维数组切片随机排列

//根据游戏级别初始化
function initGame(num){
	//对二维数组切片进行随机排列
	shuffle();
	//按照二维数组绘制图片到画布
	redraw();
}	

js定义shuffle()函数对二维数组随机排列

//随机排列二维数组
function shuffle(){
	var num=tileArray.length; //二维数组的长度
	for (var i = 0; i < num; i++) {
		for(var j = 0; j < num; j++){
			//获取随机位置
			var ri=Math.floor(Math.random()*num);
			var rj=Math.floor(Math.random()*num);
			//当前ij元素交换位置
			var t=tileArray[i][j];
			tileArray[i][j]=tileArray[ri][rj];
			tileArray[ri][rj]=t;
		}
	}
}

图片完成随机排列后,将之前绘制的图片清空,

function redraw(){
	ctx.clearRect(0,0,imgSize,imgSize);
}

将画布上的切片随机排列

3.2.5 将画布的左上角图片设置为空白

由于完全随机排列,拼图可能无解,将参考图片的左上角搬到画布的左上角置为空白,保证左上角为正确的位置,其他图片打乱。

//找到左上角(0,0)的位置设为空白,而且保证在正确的位置上
//随机排列二维数组
function shuffle(){
	//找到左上角(0,0)的位置设为空白,而且保证切片在正确的位置上
	for (var i = 0; i < num; i++) {
		for(var j = 0; j < num; j++){
			//如果横纵坐标都为0,则与数组里的(0,0)交换位置,确保左上角对应左上角
			if(tileArray[i][j].x==0 && tileArray[i][j].y==0){
				var t=tileArray[i][j];
				tileArray[i][j]=tileArray[0][0];
				tileArray[0][0]=t;
				break;
			}
		}
	}
	//标记(0,0)为空白图片,画布中(i,j)坐标
	emptyObj.i=0; 
	emptyObj.j=0;
}

在绘制图片中将空白图片画出来

//重新绘制图片
function redraw(){
	for (var i = 0; i < num; i++) {
		for(var j = 0; j < num; j++){
			//原图片的坐标
			var curimg=tileArray[i][j];//得到真实图片的坐标
			//设置当前图片如果为空白的话就不绘制
			if(i==emptyObj.i && j==emptyObj.j){
			
			}else{
				//绘制一个小切片
				ctx.drawImage(img,curimg.x*tilelen,curimg.y*tilelen,tilelen,tilelen,i*tilelen,j*tilelen,tilelen,tilelen);
			}
		}
	}
}

在这里插入图片描述

3.2.6 实现图片点击移动

给图片注册一个onclick事件

<canvas id="puzzle" width="600" height="600" onclick="move(event)"></canvas>

js定义move()函数

function move(e){
	//获得点击的位置,获得下标
	var ci=Math.floor(e.offsetX/tilelen);
	var cj=Math.floor(e.offsetY/tilelen);
	//如果点击的切片与空白相邻,则与空白交换位置。
	//要么横坐标一样要么纵坐标一样,两坐标分别相减等于1则相邻
	if(Math.abs(ci-emptyObj.i)+Math.abs(cj-emptyObj.j)==1){
		var t=tileArray[ci][cj];
		tileArray[ci][cj]=tileArray[emptyObj.i][emptyObj.j];
		tileArray[emptyObj.i][emptyObj.j]=t;
		//修改空白图片的坐标
		emptyObj.i=ci;
		emptyObj.j=cj;
		//重新绘图
		redraw();
	}
}

实现图片点击移动

3.2.7 判断游戏是否完成

我们认为,除了空白图片之外原图的坐标xy与画布的坐标ij重合时就完成。
每一次移动后判断是否完成

function move(e){
	//判断游戏是否完成
	var success=isSuccess();
	//如果success为true则重新绘制整张图
	if(success){
		ctx.drawImage(img,0,0,imgSize,imgSize,0,0,imgSize,imgSize);
		//加文字
		ctx.font='bold 50px 宋体'; //字体样式
		ctx.fillStyle='green'; //字体颜色
		ctx.fillText("游戏完成!",imgSize/2,imgSize/2); //文字内容,位置
	}
}

js定义isSuccess()函数

//判断游戏是否完成
function isSuccess(){
	var num=tileArray.length;
	var success=true;
	for (var i = 0; i < num; i++) {
		for(var j = 0; j < num; j++){
			//当前ij与tileArray[i][j]存储元素完全相同,则完成;如果有一个不一样则返回false
			if(tileArray[i][j].x!=i||tileArray[i][j].y!=j){
				success=false;
				break;
			}
		}
	}
	return success;
}

在这里插入图片描述

3.2.8 解决拼图无解情况

空白经过偶数次移动才能结束。 在数学中有一个逆序数的概念,若有一列数字为0123,则逆序数为0;若有一列数字为3210,则逆序数为6。
若逆序数为偶数,则为偶排列,若逆序数为奇数,则为奇排列。 打乱后只要为偶排列就有解,否则为奇排列。

在随机之后判断是否有解

function initGame(num){
	//对二维数组切片进行洗牌
	shuffle();
	//判断随机之后的拼图是否有解,如果有解就绘制,如果无解重新排列
	var solved=workabel();
	if(solved){
		//按照二维数组绘制图片到画布
		redraw();
	}else{
		initGame(num);
	}
}

js定义workabel()函数

//判断拼图是否有解
function workabel(){
	var num=tileArray.length;
	var arr1=new Array();
	//将二维数组坐标转换为数字存入一维数组
	for (var i = 0; i < num; i++) {
		for(var j = 0; j < num; j++){
			var ri=tileArray[i][j].x;
			var rj=tileArray[i][j].y;
			arr1.push(ri*num+rj);
		}
	}
	//求arr1的逆序数
	var total=0;
	for (var i = 0; i < num*num; i++) {
		for(var j = i+1; j < num*num; j++){
			if(arr1[i]>arr1[j]){total++;}
		}
	}
	if(total%2==0){
		return true;
	}else{
		return false;
	}
}

完成!

发布了9 篇原创文章 · 获赞 1 · 访问量 460

猜你喜欢

转载自blog.csdn.net/cMengZ/article/details/104295427