我是你们的索儿呀,很幸运我的文章能与你相见
愿萌新能直观的感受到Javascript的趣味性,愿有一定基础者有所收获,愿大佬不吝赐教

拼图游戏是一张图片分为若干块,打乱次序,将其中一块变为空白块,其只能与相邻块互换,发挥你的聪明才智来将其复原

而我本篇文章的目的是,介绍拼图游戏的玩法、技巧使用JavaScript语言实现拼图游戏

就算是萌新,也是可以阅读前半篇文章的,若能勾起你的兴趣将整篇文章看完那就更棒了

文章目录

  • 效果展览
  • 思路及代码实现
  • 部分JS知识点陈列
  • 游戏玩法及技巧
  • 代码获取

效果展览

  • 原图、2 × 2、3 × 3、6 × 6
  • 甚至还可以4 × 3、3 × 6
  • 图片也是可以更换的,其原图、4 × 4

思路及代码实现

若你对于JavaScript才初窥门径,我建议你使用电脑下载代码(文末提供的核心代码),然后配合本篇文章食用更佳

我先以 3 × 3 的九宫格来讲解拼图游戏的思路,实际上我提供的代码是很灵活的,可以更换图片和改变分割方式

补充一嘴:本人没有对打乱拼图的算法做逻辑处理,显示出来的拼图不一定可以复原哦~~

html代码只需要一行即可:

<div id="game"></div>

其余内容皆由JavaScript完成

一:游戏整体配置

此时,摆在我们眼前的有两种做法:

  1. div中放九个img作为拼图块(这个做法太死板了,若我要改变图片、分割方式,改动起来会非常麻烦琐碎)
  2. div只需要一个背景图片,里面生成九个div作为拼图块,即将其分割为3 × 3 的九宫格(将图片看作spirit图,然后配合background-position属性即可做到)

  • 根据图片的分辨率( 900 × 900 ),确定<div id="game"></div>的宽高
  • 拼图游戏( 3 × 3 ),确定每一行、每一列有多少个拼图块
  • 图片路径,相对的是页面路径

以上三个方面的配置是最基本的,可以通过更改以上配置来更改拼图游戏的图片或者分割方式

/*** 游戏配置*/
var config = {dom: document.getElementById("game"),  //游戏的dom对象width: 900,height: 900,rows: 3,  //行数cols: 3,  //列数url: "img/Ahri.png",  //图片路径
};
  • 每一块拼图块的宽高取决于<div id="game"></div>的宽高和行列数
  • 拼图块的数量取决于div的行列数
//每一个拼图块的宽高
config.blockWidth = config.width / config.cols;
config.blockHeight = config.height / config.rows;//拼图块的数量
config.blockNumber = config.rows * config.cols;

二:初始化游戏

1. 初始化游戏面板

  • config.dom获取的是<div id="game"></div>的dom对象
  • 然后使用这个dom对象设置其css属性(宽高、边框)
  • 为什么要设置为相对定位呢?因为拼图游戏中的拼图是可以移动的,它们的位置需要使用绝对定位
/*** 初始化游戏容器*/
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 = [];
  • i 对应行号,j 对应列号,left、top为定位元素
  • left = j × blockWidth
  • top = i × blockHeight

此时,<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其实是一一对应的

  • correctBgX = -left;
  • correctBgY = -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、topexchangeBlock(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){//交换leftvar temp = x.left;x.left = y.left;y.left = temp;//交换topvar temp = x.top;x.top = y.top;y.top = temp;//在页面重新显示x.show();y.show();
}

4. 给拼图块注册点击事件

交换看的见的拼图块与空白拼图块的坐标位置,

并且它们必须要挨着一起

  • 同一行,它们的left的差值的绝对值,等于拼图块的宽度
  • 同一列,它们的top的差值的绝对值,等于拼图块的高度
/*** 给拼图块注册点击事件*/
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知识点陈列

为了阅读的流畅性,我先陈列一些后文我将用到的JS函数、dom元素的玩法(大致瞟一眼,留个印象就好了,阅读到后面可以返回来看)

一:dom元素的玩法

  • document.getElementById(“id名”)
    通过id获取对应id的元素
  • document.createElement(“元素名”)
    创建一个dom元素对象(此时不影响文本结构)
  • this关键字在事件处理程序中指代当前发生的事件元素
  • dom.style.样式名 = 值
    设置该dom元素的行内样式
  • dom.appendChild(dom元素)
    在某个元素末尾加入一个子元素
  • dom.onclick() = function(){函数体}
    为dom元素注册点击事件,当鼠标在页面上点击该dom元素时,执行函数体内容

二:JS函数

  • push()
    可向数组的末尾添加一个或多个元素,并返回新的长度
  • parseInt(浮点数)
    返回整数,即将浮点数的小数点即其后的数去掉
  • Math.floor(数)
    向下取整,比如12.1~12.9都返回12
  • Math.random()
    返回介于 0 ~ 1 之间的一个随机数(包括0,不包括1)
  • Math.abs(数)
    返回该数的绝对值
  • 数组.find()
    为数组中的每个元素都调用一次函数执行:当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
  • 数组.filter()
    创建一个新的数组,新数组中的元素是通过调用数组中的每个元素并检查,返回满足条件的元素到新数组中
  • 数组.forEach()
    用于调用数组的每个元素,并将元素传递给回调函数。

个人所虑,必有不足。若有疑问,欢迎在评论区提问。

游戏玩法及技巧

既然决定写这篇文章,我花了一个上午研究拼图游戏,也某度、某乎查看了一些技巧,遂整体成文

本人建议,先到文末将核心代码下载下来,边玩边看玩法技巧,食用更佳

如果你是萌新,推荐使用百度网盘下载代码、查看运行哟

下载到电脑上后,只需要鼠标左键双击game.html文件,即可用浏览器打开,可以尝试玩一下哟(图片尺寸较大,缩放页面视觉效果更佳)

查看代码,就使用编辑器(或者电脑自带的记事本:鼠标右键该文件,打开方式,记事本)打开script文件夹下的game.js文件,就可以开始玩了(重新开一局也很简单:刷新页面)

若拼图是完全随机排序的,不一定可以复原,
拼图的复原在于旋转,旋转是受限制的(可以这么理解:把复原好的任意阶拼图,任意互换两块的位置,都不可能复原)

  • 2 × 2 比如下图是复原不了的:
  • 3 × 3 是拼图游戏的基础,因为它大概率可以复原(只有一种情况是无法复原的,后文会讲到);可以引申为一句话,无论是?× ?的拼图,只要包含3 × 3,就可以简化为3 × 3的拼图

只要会复原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 拼图是不可能复原的

代码获取

拼图游戏的完整核心代码,有两个方式获取

所谓核心代码,即本篇文章带你实现的拼图游戏的完整代码,没有加入许许多多花里胡哨的东西,以免文章无法连贯讲解

  1. 百度网盘
    文件夹链接:https://pan.baidu.com/s/1bFiosDk2uXjvOHuVYsI2IQ
    提取码:r1ua
    压缩包链接:https://pan.baidu.com/s/1nvYilW7vYErJZWQzXER6DQ
    提取码:zr7k
  2. github:https://github.com/Zhangguohao666/demo-front-end/tree/master/jigsaw

拼图游戏的完整提高版代码

未完待续…

本文代码思路参考自:渡一,袁进老师


拼图游戏 玩法介绍及其代码实现(有意思的JS 一)相关推荐

  1. python拼图游戏代码的理解_有意思的JS(1)拼图游戏 玩法介绍及其代码实现

    我是你们的索儿呀,很幸运我的文章能与你相见,愿萌新能直观的感受到Javascript的趣味性,愿有一定基础者有所收获,愿大佬不吝赐教 拼图游戏是一张图片分为若干块,打乱次序,将其中一块变为空白块,其只 ...

  2. Emacs 游戏彩蛋——游戏玩法介绍

    Emacs 内置了很多小游戏,可以参考 https://www.emacswiki.org/emacs/CategoryGames 下面将持续介绍这些游戏的玩法. 有些游戏的玩法可以通过使用: M-x ...

  3. 蚂蚁树林小游戏玩法介绍

    蚂蚁树林微信版小程序是一个类似于支付宝蚂蚁森林的能量收集小程序. 本次升级不但集成了微信运动步数兑换能量球奖励,而且对游戏UI做了重大升级! 1.能量球收取规则: 蚂蚁树林能量球有4种颜色[青.绿.黄 ...

  4. 一款科幻题材基地建设策略游戏——太空避难所中文版 附游戏玩法

    space haven中文称之为太空避难所,是由Bugbyte Ltd. 制作并发行一款以太空科幻为背景的模拟经营游戏,让玩家能够到科幻风格的基地建设的内容以及紧张刺激的战斗.在游戏中,玩家将率领一群 ...

  5. 手机计算机怎么玩24点游戏,计算器游戏怎么玩_计算器游戏新手玩法介绍_快吧手游...

    计算器游戏怎么玩,新手前期玩法介绍,计算器游戏是怎么操作的,新手要怎么操作上手,想必很玩家也是十分的想知道吧,下面快吧小编带来了这篇攻略,希望能帮助大家,赶紧去看看吧. 计算器游戏怎么玩? 计算器游戏 ...

  6. 手机计算机怎么玩24点游戏,计算器游戏怎么玩 新手前期玩法介绍

    类型:休闲益智大小:30.7M语言:中文 评分:10.0 标签: 立即下载 计算器游戏怎么玩,新手前期玩法介绍,计算器游戏是怎么操作的,新手要怎么操作上手,想必很玩家也是十分的想知道吧,下面西西带来了 ...

  7. 【Creator Kit - RPG 代码分析】(2)-游戏玩法-背包系统

    GamePlay 这篇开始来讲这个教程代码实现的游戏玩法逻辑 背包系统 效果 先看这个背包的UI 效果图 基本单元item 每个物体都有一个基本单元 Item namespace RPGM.Gamep ...

  8. 鸿蒙符碎片什么游戏,武林外传手游鸿蒙鼎怎么玩_武林外传手游鸿蒙鼎玩法介绍_游戏吧...

    武林外传手游中有很多的小伙伴们对鸿蒙鼎还不是很了解,下面游戏吧小编为大家带来武林外传手游鸿蒙鼎玩法介绍,感兴趣的小伙伴们快来一起了解一下吧! 武林外传手游鸿蒙鼎玩法介绍 鸿蒙鼎为合成分解系统,解决客官 ...

  9. Java小游戏流光之刃_战刃流光之地副本如何玩 副本玩法介绍

    战刃流光之地副本如何玩 副本玩法介绍 战刃流光之地副本如何玩呢?下面我就来大家介绍副本的玩法,还不知道如何打这个副本的玩家可不要错过了哦. 当玩家将等级提升至40级时,可通过日常副本"流光之 ...

最新文章

  1. 无法在数据库 'ycmis2' 中运行 BEGIN TRANSACTION,因为该数据库处于回避恢复模式。...
  2. 在tomcat下部署两个或多个项目时 log4j和web.xml配置webAppRootKey 的问题(转)
  3. 太相信书的人,格局不会太大
  4. Python探索记(01)——HelloWorld及Python的注释
  5. 【Android】Activity生命周期
  6. twisted.internet.error.TimeoutError错误的解决方法
  7. SAP CRM系统里Opportunity预期销售金额和货币相关的自动转换
  8. linux下的单机工具,Linux下单机模式的Hadoop部署
  9. 架构整洁之道 pdf_代码有整洁之道,而架构同样有整洁之道
  10. 5G( 9)---开发者:你如何迎接5G时代的到来?
  11. linux中副规则_Linux中的命名规范
  12. Unity Manual learning log
  13. 看完就能学会如何使用热敏电阻!
  14. Cognos资料整理
  15. react 中样式写法
  16. 题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个
第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下
的一半零一个。到第10天早上想再吃时,见
  17. echarts使用e_macarons方法
  18. LeetCode力扣刷题——巧解数学问题
  19. Epson针式打印机打印十六进制内容解决方案
  20. 基于佟刚老师视频教程的Spring MVC数据类型转换学习总结

热门文章

  1. MnTTS: 开源蒙古语语音合成数据集及其基线模型
  2. ESP8266 RTOSSDK3.0 的工程同步到3.4
  3. 访问www.baidu.com经历了什么
  4. Telnet访问百度www.baidu.com
  5. TCC(TinyC)编译器汉化(中文编译器、汉语编程)之一:主文件汉化
  6. 看不懂电路图?学会这10大原则7大步骤,电路图so easy!
  7. POST和GET请求,接码
  8. 22 《巴黎文学散步地图》 -豆瓣评分7.8
  9. Qt编写可视化大屏电子看板系统5-恢复布局
  10. 卡西欧4800坐标正反算通用程序(终极篇)