赞
踩
我是你们的索儿呀,很幸运我的文章能与你相见
愿萌新能直观的感受到Javascript的趣味性,愿有一定基础者有所收获,愿大佬不吝赐教
拼图游戏是一张图片分为若干块,打乱次序,将其中一块变为空白块,其只能与相邻块互换,发挥你的聪明才智来将其复原
而我本篇文章的目的是,介绍拼图游戏的玩法、技巧,使用JavaScript语言实现拼图游戏
就算是萌新,也是可以阅读前半篇文章的,若能勾起你的兴趣将整篇文章看完那就更棒了
若你对于JavaScript才初窥门径,我建议你使用电脑下载代码(文末提供的核心代码),然后配合本篇文章食用更佳
我先以 3 × 3 的九宫格来讲解拼图游戏的思路,实际上我提供的代码是很灵活的,可以更换图片和改变分割方式
补充一嘴:本人没有对打乱拼图的算法做逻辑处理,显示出来的拼图不一定可以复原哦~~
html代码只需要一行即可:
<div id="game"></div>
其余内容皆由JavaScript完成
一:游戏整体配置
此时,摆在我们眼前的有两种做法:
<div id="game"></div>
的宽高以上三个方面的配置是最基本的,可以通过更改以上配置来更改拼图游戏的图片或者分割方式
/**
* 游戏配置
*/
var config = {
dom: document.getElementById("game"), //游戏的dom对象
width: 900,
height: 900,
rows: 3, //行数
cols: 3, //列数
url: "img/Ahri.png", //图片路径
};
<div id="game"></div>
的宽高和行列数//每一个拼图块的宽高
config.blockWidth = config.width / config.cols;
config.blockHeight = config.height / config.rows;
//拼图块的数量
config.blockNumber = config.rows * config.cols;
二:初始化游戏
1. 初始化游戏面板
<div id="game"></div>
的dom对象/**
* 初始化游戏容器
*/
function initGameDom(){
config.dom.style.width = config.width + "px";
config.dom.style.height = config.height + "px";
config.dom.style.border = "2px solid #ccc";
config.dom.style.position = "relative";
}
2. 初始化拼图块
一个拼图块就是一个div,
需要在<div id="game"></div>
中创建九个div(都设置为绝对定位),
此时需要一个数组,数组的每一项是一个对象,存放每一个div(拼图块)的信息
//存放拼图块信息
var blocks = [];
此时,<div id="game"></div>
中的九个div(拼图块)的位置就安排好了
/**
* 初始化拼图块的数组
*/
function initBlocks(){
for(var i = 0; i < config.rows; i++){
for(var j = 0; j < config.cols; j++){
var isVisible = true;
if(i === config.rows-1 && j === config.cols-1){
isVisible = false;
}
blocks.push(new Block(j * config.blockWidth, i * config.blockHeight, isVisible));
}
}
}
同时,每个div(拼图块)对应的正确的背景图片的位置correctBgX、correctBgY与left、top其实是一一对应的
Block构造函数有三个参数:;
以下函数的第三个参数isVisible控制拼图块是否可见(最后一个拼图块是不可见的)
关键参数为前两个参数:定位元素left、right,
在构造函数Block中先生成div的dom元素,然后定义其必须要的属性:width、height、border、background、position,
box-sizing: border-box
决定div的宽高包括边框的宽度,这样不会影响到页面布局
cursor: pointer
鼠标移动到该标签上时变为手指
transition : .5s
css属性变化的时候,在0.5秒内完成(达到拼图块交互时的拖拽效果)
/** * 拼图块的构造函数 * @param {*} left * @param {*} top * @param {*} isVisible 是否可见 */ function Block(left, top, isVisible){ this.left = left; this.top = top; this.correctBgX = -this.left; this.correctBgY = -this.top; this.isVisible = isVisible;//是否可见 this.dom = document.createElement("div"); this.dom.style.width = config.blockWidth + "px"; this.dom.style.height = config.blockHeight + "px"; this.dom.style.boxSizing = "border-box"; this.dom.style.border = "2px solid #fff"; this.dom.style.background = 'url("'+ config.url + '") ' + this.correctBgX + "px " + this.correctBgY + "px"; this.dom.style.cursor = "pointer"; this.dom.style.transition = ".5s";//css属性变化的时候,在0.5秒内完成 if(!isVisible){ this.dom.style.display = "none"; } this.dom.style.position = "absolute"; /** * 根据当前的left、top,显示div的位置 */ this.show = function(){ this.dom.style.left = this.left + "px"; this.dom.style.top = this.top + "px"; } this.show(); config.dom.appendChild(this.dom); }
此时,拼图的大概样子是做出来了
3. 打乱拼图块
将存放拼图块的数组blocks重新排序,需要循环数组重新给其left、top值赋值,
最后一个拼图块是空白的,它的位置是确定的(不参与重新洗牌)
/** * 给blocks数组从新排序 */ function shuffle(){ for(var i = 0; i < blocks.length-1; i++){ //随机产生一个下标 var index = getRandom(0, blocks.length-2); //交换left、top exchangeBlock(blocks[i], blocks[index]); } } /** * 生成[min, max]范围内的随机数 * @param {*} min * @param {*} max */ function getRandom(min, max){ return Math.floor(Math.random() * (max +1 -min) + min); } /** * 交换两个拼图块的top、left,并在页面上重新显示 * 参数都为拼图块对象 * @param {*} x * @param {*} y */ function exchangeBlock(x, y){ //交换left var temp = x.left; x.left = y.left; y.left = temp; //交换top var temp = x.top; x.top = y.top; y.top = temp; //在页面重新显示 x.show(); y.show(); }
4. 给拼图块注册点击事件
交换看的见的拼图块与空白拼图块的坐标位置,
并且它们必须要挨着一起
/** * 给拼图块注册点击事件 */ function regEvent(){ //找到空白拼图块 var inVisibleBlock = blocks.find(function(b){ return !b.isVisible; }); blocks.forEach(function(b){ b.dom.onclick = function(){ if((isEqual(b.top, inVisibleBlock.top) && isEqual(Math.abs(b.left - inVisibleBlock.left), config.blockWidth)) || (isEqual(b.left, inVisibleBlock.left) && isEqual(Math.abs(b.top - inVisibleBlock.top), config.blockHeight))){ //交换看的见的拼图块与空白拼图块的坐标位置 exchangeBlock(b, inVisibleBlock); //游戏结束判定 此处有一个函数 } } }); }
/**
* 避免小数的不精确,将两数化整比较
* @param {*} x
* @param {*} y
*/
function isEqual(x, y){
return parseInt(x) === parseInt(y);
}
5. 游戏结束判定
关键:判断每一个拼图块是否都在正确的位置上
/** * 游戏结束判定 */ function isWin(){ //过滤不在正确位置上的拼图块 var mistakenBlocks = blocks.filter(function(b){ return !(isEqual(b.left, -b.correctBgX) && isEqual(b.top, -b.correctBgY)); }); if(mistakenBlocks.length === 0){ //游戏结束 blocks.forEach(function(b){ b.dom.style.border = "none"; b.dom.style.display = "block";//将最后一个拼图块也显示在页面上 }); } }
此时还没有完成哦~
拼图完成后,要将每一个拼图块设置为都不能点击,有兴趣的可以阅读文末提供的完整核心代码哟
为了阅读的流畅性,我先陈列一些后文我将用到的JS函数、dom元素的玩法(大致瞟一眼,留个印象就好了,阅读到后面可以返回来看)
一:dom元素的玩法
二:JS函数
个人所虑,必有不足。若有疑问,欢迎在评论区提问。
既然决定写这篇文章,我花了一个上午研究拼图游戏,也某度、某乎查看了一些技巧,遂整体成文
本人建议,先到文末将核心代码下载下来,边玩边看玩法技巧,食用更佳
如果你是萌新,推荐使用百度网盘下载代码、查看运行哟
下载到电脑上后,只需要鼠标左键双击game.html文件,即可用浏览器打开,可以尝试玩一下哟(图片尺寸较大,缩放页面视觉效果更佳)
查看代码,就使用编辑器(或者电脑自带的记事本:鼠标右键该文件,打开方式,记事本)打开script文件夹下的game.js文件,就可以开始玩了(重新开一局也很简单:刷新页面)
若拼图是完全随机排序的,不一定可以复原,
拼图的复原在于旋转,旋转是受限制的(可以这么理解:把复原好的任意阶拼图,任意互换两块的位置,都不可能复原)
只要会复原3 × 3,就可以玩转拼图游戏了(若它能够复原)
以可复原为基础,某乎高赞答案 给的复原3 × 3技巧,我发现非常好用:
除了以上技巧,还要会借位技巧(你就已经是拼图大神了)
我将情况尽量简化,来讲解借位技巧,给下图(1、4、7已经排好了,这一列不看了):
而且上图恰好是伪复原,2、8看似复原了,实际上这样想会进入死胡同的,实际上只复原了2或8
我就以2复原了作为基础,来讲解怎么将第二列(2、5、8)复原:
先将2、5复原(不用思考就做得到),
此时摆在我们面前的问题是,如何将8复原,此处需要借位技巧了
先将2移到上图3处,5移到上图2处(眼中只做到这两步即可)
然后就是最后一个难点了,将8移到上图6处,空白格再移回上图空白格处
接位的目标已达成,将空白格上移、右移,第二列复原完成
于是,你发现了一个问题…嘿嘿…我给的这个3 × 3 拼图是不可能复原的
拼图游戏的完整核心代码,有两个方式获取
所谓核心代码,即本篇文章带你实现的拼图游戏的完整代码,没有加入许许多多花里胡哨的东西,以免文章无法连贯讲解
拼图游戏的完整提高版代码
未完待续…
本文代码思路参考自:渡一,袁进老师
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。