连连看

哇,终于写完了,算是初步实现连连看的基本功能吧,其他的等以后在慢慢完善吧~

记录一下,这次代码实现中遇到的一些问题和想法吧,要不然时间久了,自己怎么写的也没印象了

言归正传,开始:

功能的实现主要是:javascript + vue2.js + jquery 来实现的

文件目录如下图所示:

文件目录

功能实现图片


1、第一步,是界面的实现,准备工作总要做好的,先来放一张目标图片,然后照着图片一步一步来

根据上图可以看到,界面的实现其实和 table 差不多,行(row) 里 包含 列(col)  ,每个人写代码的风格不一样,我是通过ul 来实现。 第一个 li 是 行(row) ,里面的 ul 下所包含的 li 是 列(col) 。html代码如下图所示:

html代码


2、第二步,构造假数据 ,界面基于数据来实现,通过数据让页面丰富起来

定义所需数据变量

绘制界面不可或缺的需要用到 各种图片、行、列,其实开始这三种(type、row、col)是单独定义的,在写 createData 这个函数时,将这三个做为参数传进去,根据传进来的值创建 行 和 列。不过为了防止后期增加其他的参数或者参数过多造成混乱 所以直接定义成一个 json ,也就是上面定义的 pGroup数组 来存储这些 json数据 。包括后面要实现的增加游戏的难易程度都可以通过这组数据来实现。

下面来写 创建数据函数 (createData) ,函数调用默认 createData(pGroup[0]) ,(相当于关卡1)

循环 count ;用 i % 总数据的一半, 得到 一组相同 的数据;用得到的数据  % 传参进来的图片类型数量 ;然后添加到数组中。这样能保证数组中的 数据都是成对 出现,且范围都在传参进来的类型数量以内。

将 arr 里的同类型的数据进行随机排序,这样,每次获取数据时,图片的排列顺序都不一样,展示效果也不一样。

打乱类型数据

循环传参进来的 parameter.row 和 parameter.col 并分别 加2 。界面上格子数量如下图所示。在循环 col 里 声明 定义 json ,每一个json代表一个格子所包含的信息,当两个格子链接时,起始格子通过自己本身的上,下,左,右四个方向的格子 json 下的 isRemove 的值是 true 或者 false 来判断线路是否可以到达

而最外层(上,下,左,右)四个方向的格子是缺少一个或两个相邻的格子的,所以在循环时给行和列分别 加2 ,相当于在外面包上一层,这样处于 上部,下部,左部,右部的格子就可以不受没有相邻格子的方向的限制且默认设置为 isRemove = true ( 也就是隐藏 )。这样四边的格子在链接时,也有四个方向用来判断,而因为默认是隐藏,而不会影响正常的显示。效果如下图。(为了看的清晰,下图中的空格子设置是false,使用时改成true即可)

所以在定义每个格子下的 json 信息时,做判断。如果是最外面的四个边,只需要存 x , y , isRemove 三个参数信息,且 isRemove = true 因为其他的参数是用不到的所以也无需设置。

用到的数据参数进行一下说明,将他存到 json = {

    x , y : 格子分别的横纵坐标

    type : 图片的类型,后期根据类型去获取图片

    ID : �每个格子单独的标识,代号

    imgSrc : 通过 type 去 imgData 里找到相对应的路径信息,并记录

    isRemove : 通过赋值 true 或者 false 来定义格子是否显示或者隐藏,用来实现消除后的效果

    active : 通过 true 或者 false 来定义是否为选中状态

}

第一层循环是行,第二层循环是列,arr 里存的是一行的格子的信息,arrData里存的是多行的格子的信息,所以我们现在界面需要展示的所有信息都存在 arrData 里。



3、第三步,将数据放到界面中进行展示

不得不说,vue真是好用啊!将之前定义好存储所有格子信息的 arrData ,传给 实例的 msg 属性。

msg : 格子信息数组

o1 : 点击的第一个格子

o2 : 点击的第二个格子

level : 当前的关卡

line : 存放路线

在接下来的代码中通过指令,对 msg 进行操作。不进行过多的说明,具体可以看文档,

v-show : 根据表达式之真假值,切换元素的displayCSS 属性。

v-for : 基于源数据多次渲染元素或模板块。此指令之值,必须使用特定语法alias in expression,为当前遍历的元素提供别名;另外也可以为数组索引指定别名(或者用于对象的键):(row , i1) in msg

@click : 监听事件,触发点击事件时,将 当前元素的 索引(i1,i2) 传参到函数中

:class : 绑定属性,通过格子的 isRemove 和 active 来控制 class : hide 和 active 的使用



第四步,来看当格子连接时触发的 add() 函数 

点击时,判断 this.o1 下真或者假,如果是假(没有值) ,用传参进来的 行索引 和 列索引 去 存放所有格子信息的 arrData 里找到对应的 json数据,赋值给 this.o1 ,因为当前应该是选中状态,所以 this.o1.active = true ;

this.o1 的选中状态


如果 this.o1 已经存在,那么当前将值赋给 this.o2,同样是存放格子对应的 json数据。

接下来进行判断,除去连接时的是否有障碍物的规则,最基本的要求是:

         1、两次点击不能是同一个格子,格子所对应的 json数据 下的 ID 是独一无二的哦;

        2、两个格子的类型必须是相同的(举个例子:蚊子和鱼是没法配对消除的哦)。而我们之前定义的图片是根据不同的 type 来定义的相对应的图片,

所以当 ID相同,类型不同 ,是没法消除格子的,这时调用 clearOther(this) 函数。

clearOther(this)

因为以上条件,现在无法完成消除,这时 this.o1 对于我们来说,作用已经不大了,而 this.o2 将变成新的 this.o1 。所以先将两个的 active 状态都定义为 false 。 在取代完成后 ,赋予最新的 this.o1 的 active 状态 为 true 。因为后面需要常用到,所以将它封装成函数,在接下来的代码中,如果不满足格子配对消除的规则都将调用这个函数。


反之如果不满足以上两个条件,进入 else,即 ID不同,类型相同,则正好满足我们的基本条件。

ID 和 类型 的判断,只是连接成功的基本判断,接下来我们在满足这种基本条件的情况下,进行深入判断:

        1、两个挨在一起的相同的格子;

        两个挨在一起,在保证基本条件的情况下,必然是能够连接成功的,下面来写符合两个挨           在一起的规则,首先是知道每个格子的索引的 ( json.x 和 json.y ) ,也就是下图中所出现的         数字。例: 1,1  -->  2,1   两者分别相减,得到 x = -1 || 1 和 y = 0,然后 x + y = 1 ||               -1,因为两者只要相差是1就好,所以用绝对值来处理,总结规则如下:

        Math.abs(this.o1.x - this.o2.x) + Math.abs(this.o1.y-this.o2.y) == 1

        如果满足以上条件则调用 sameHide(this) 函数,drawLine(this,arr) 作用是画两个格子连接时的线,具体稍后在说,现在只要知道里面传两个参数 this 和 一个数组,就可以。

sameHide(this)函数

      �首先让目标盒子也有选中效果 active = true。接下来声明变量并赋值 _this.o1 和 _this.o2 ;成功后,清空 _this.o1 和 _this.o2。因为在选中状态的时候有一个0.3秒放大的效果,所以用定时器,让数据在0.3秒后让o1,o2在 arrData 相对应的数据的状态 isRemove = true 。之所以用 a1,a2 来代替 _this.o1,_this.o2,是因为当 点完 o1 在点 o2 ,接着快速点击 其他同类型格子的时候,o2 会变成 o1,而最后点的格子会相继变成 o2 ,进而在满足条件下再次配对消除,所以找其他的变量储存信息后,就将 o1 和 o2 清空。 

_this.line = [ ] ,配对连接完成后,因为连接时线的位置和展示是根据数组的值和length 决定的 所以在配对成功后,让线隐藏,将其清空。关于连线,后面会详细说明 。


        2、双方处于外层四条边上且在同一排的格子;

        接下来定义规则,单独把他抽出来,因为它本身比较特殊,如上图所示,拿底部的格子来举例,除去线路2 和 线路3 是需要判断中间是否有障碍物,线路1是直接就可以进行的,所以当满足这些条件后(上边 || 下边 || 左边 || 右边)来接着进行下一步。调用 route(this) 函数,因为第3种情况也需要调用此函数,所以在描述完第三种情况后一起说。

        上边 :_this.o1.x == 1 && _this.o2.x == 1

        下边 :_this.o1.x == pGroup[this.level].row && _this.o2.x == pGroup[this.level].row

        左边 :_this.o1.y == 1 && _this.o2.y == 1

        右边 :_this.o1.y == pGroup[this.level].col && _this.o2.y == pGroup[this.level].col

        3、其他情况:一种复杂的连接 和 连接失败,因为还要做更复杂的判断,所以我们将两个放在一起

第三种情况

很明显除去上面2种不用通过特别复杂的路线就可以实现配对消除的情况之外,第三种包含了两种情况。一种是通过复杂路线实现配对,一种是抛开基本要求(ID相同或类型不同),经过复杂的路线后依然不成功的情况。而这两种情况都需要在 调用 router(this) 函数之后才可以知道结果。



第五步,调用 router(this) 。在说这个函数之前,要先说两个已经封装好的函数。 

1、direction(o1,o2)      作用:判断方向 。 o1代表点击的第一个格子,o2代表第二个格子。 如果 : o1.x > o2.x ,是 h = -1 路线向左走 。两者 x 相等 ,h = 0 水平不动。除去前两种只剩下一种情况  o1.x < o2.x ,是 h = 1 路线向右走。y 同理,是垂直方向。

direction(o1,o2)

2、direction2(o1,o2)      作用:优先路线排序。在做判断的时候,我是以左右来做大方向,在大方向里在进行二次条件判断,这个根据个人的想法来这写,第一个判断 ,如果 o1.x > o2.x ,优先方向为 左,接着进行二次判断 如果 o1.y > o2.y ,那就是目标格子(o2)在 起始格子(o1)的左上角,所以优先路线定义为 arr = [ '左','上','下','右' ] 。 后面的判断同理,不一一做解释。判断全部结束后将数组做为函数的返回值。

direction(o1,o2)


其实除去两者出现 x 相同或 y 相同是有三种路线以外,其他的都是四种路线,如下图所示,所以需要封装四种路线的函数

四种连接路线

接下来来看 route(this) ,将之前 direction 和 direction2 的返回值接收,循环 direction2 的优先路线返回值 arr , 并做判断,上面已经说过,分别封装了四个方向的函数,这时根据循环数组的值来调用相对应的方向函数。

而调用方向函数本身也是有返回值 ,如果配对成功,返回true,配对不成功则返回false,所以依次做判断,将返回值 pusharr里,如果当前路线配对成功,则跳出循环,如果当前配对不成功,则继续判断直到最后,并在结束后将 arr 做为函数的返回值。

route(this)

拿左 routeLeft(_this,x,y) 来举例,需要将之前判断方向(direction)中的值也就是后面声明定义的 x , y 传参进去。

声明定义变量

声明定义变量

switchBtn :相当于一个开关,默认为false ,当路线没有障碍物是 设置为 true。

mainLine(主干路线) , childLine(第一次转弯后路线) , subBranch(第二次转弯后路线) :记录路线经过哪些格子所对应的索引,最后由sum 进行数组整合,记录配对成功的完整路线。开始 push 初始路线(this.o1的x , y)的值到mainLine 。


a)、如果 y = 0 , 说明两个格子的 y 值相同 ,都在 x 轴上,从 o1 起始,循环 o1 到 o2  的区间,将路线存到 mainLine 下,并对区间格子当前的 isRemove(隐藏或显示) 状态,进行判断,如果状态为 false ,则证明两者之前依然有障碍,无法连接成功,那 switchBtn 依然是 false ,将mainLine 清空,并结束整个循环。如果直到 目标格子的前一个,则证明区间状态都为 true (隐藏),配对格子之间已无障碍,连接配对成功,switchBtn = true 。

接下来判断 swithcBtn ,这个判断代码后面会经常出现,对其进行下 说明。如果是真,push 目标格子索引,并调用以下函数

sameHide(_this) 配对成功,改变状态,格子消失。refresh(_this)

drawLine(_this , sum) : 之前已经说过是用来画线的函数,�稍后做详细说明

sameHide(_this) : 已描述,配对成功,改变状态,格子消失

refresh(_this) : 当没有满足配对要求的情况下,刷新界面,重新排序绘制

如果是假 return false ;


注:因为在开始存储格子的json信息的时候是 json.x = j ,json.y = i 方便按照正常的逻辑来展示,所以在取的时候同样需要向开始存储那样�获取 j 和 i 的位置下相对应的信息。




b)、反之,则不在同一个轴上,如下图所示,只要和起始格子处于同一轴,那么这条轴相当于主干。如果主干上有障碍(格子 isRemove = false),那么这条线路将无法进行下去,如下图左,如果主干线上没有障碍(格子 isRemove = true), 那么可继续下去,再对支干 和 支干下的子支干 进行判断,如下图右,所有路线开始的前提是主干可以进行下去,否则白搭。所以先对主干线进行判断

路线图

循环 x 轴,直到最左侧,x = 0 , 循环判断如果主干线上格子当前的状态,如果 isRemove = false ,则证明障碍物的存在,也没有必要继续下去,将记录主干线路的数组清空,结束整个循环。配对连接失败 (如上图左所示)。

(如图右所示)如果没有障碍物,则进行下一步,通过之前传参进来的 x , y ,因为 y > 0 得到所处的方向(左上),而支干路线又分为两种,一种是拐弯后直接到达。如上面路线图 5 线路,还有两种比较特别的是 4 和 6 线路,因为在路线过程中,只会去判断两个格子区间的状态,并不包含要配对的格子本身,所以无障碍到达目标格子高度时,即配对成功。而1 ,2 ,3 线路是当到达和目标格子高度一致的时候,再经过一个拐弯后才可到达。


左上直达

就像 5 线路那样,拐弯向上后直达就可到达目标格子。

但是也有一种情况就是,假如现在连接的是(6,3)  -->  (1,2) , 这种情况的特点就是,在主干线格子的x = o2.x 的情况下,目标格子 y 坐标 - 初始格子 y 坐标 = 1 就行,因为现在是左上,所以是1,如果是左下就是 -1 ,所以直接给绝对值,避免因为数字导致判断错误。格子走到这一步,相当于配对成功,swichBtn = true  ,sum 整合 主干上的路线和支干上的路线。

在说另一种,两个配对格子如上路线图 红框所示。主干上拐弯后,依然要向上走,直到目标格子的前一个,说明期间没有跳出循环,所有格子状态均为 isRemove = true ,sum 整合主干路线和支干上的路线。 最后通过 switchBtn 进行判断。


非左上直达

接着说 1,2,3,4,6 路线 。 第一次拐弯后,y 一直走,如果期间有格子 isRemove == false,则结束该循环。

这时,4 和 6 路线比较特别,因为拐弯后就到达目标格子,所以同样规则:o2.x - i = 1 或者 -1 同样用绝对值来处理,配对成功,swichBtn = true  ,sum 整合 主干上的路线和支干上的路线。

1,2,3 路线,当 y == o2.y 时,又该拐弯了,如果当前 x > o2.x ,往左拐,如果 x < o2.x ,往右拐,判断和之前大同小异,不一一做解释,如果到达 o2.x的前一个,依然没有结束循环,则证明 格子状态为 (isRemove = true), sum 整合数组,通过 swithBtn 做判断。

push 目标格子的位置索引添加到 sum 中。调用函数。

支干下的路线

最后的最后 ,switchBtn 在做最后一次判断 ,函数中需要注意的是,全程只有在同一条轴上直达和代码的结尾 switchBtn 的判断有 else { return false } , 因为�情况只有直达和非直达,如果直达中有障碍物,那么代码可以结束。而非直达,要等到所有的路线确定完毕后,在反馈结果。

routeLeft函数

每个方向分别有一个函数,代码内容大同小异,现在回到 第四步 下所描写的第三种情况,复杂路线配对成功或者配对失败,因为在调用route(this)时,已经得到一个数组。里面是路线是否连接成功的状态,

所以在这做代码判断,循环数组,如果有一个为true,则表示格子配对成功。如果都是false,则说明通过 上,下,左,右 都配对失败。所以调用 clearOther(this) 函数。


第六步,关卡难度升级,界面重绘

当界面所有的格子的 isRemove状态 等于 true 的时候,说明界面中所有的格子都已经消除成功。因为格子在消除时有0.3秒的动画效果,所以在0.3秒之后,格子的 isRemove 状态才会更改,所以在定时器里去循环判断每个格子的状态。如果还有格子的存在都 return 。

如果 当前的关卡(从0开始计数) 已经 等于 数组的leng - 1 ,则说明已经到了最后一关,更改界面友情提示。如果不是最后一关,则进入下一关。给 this.leval + 1 ,并且去调用 �createData函数并将最新关卡数据传参进去 createData(pGroup[_this.level]) ,并将格子状态 isRemove 改成 false 。然后将同步数据 _this.msg = arrData 。

难度升级,界面重绘


第七步,画线

现在配对成功后可以消除两个格子,但是当页面中的所有格子消除完成后,要进行下一个关卡,也就是之前定义好的 存储关卡的数据 pGrout  。但是连接线还没有画,接下来,画线。

这需要用到之前已经整合好路线的数组 sum,循环数组,用当前数组 和 下一个做对比,如果 x有变化就是横线,如是是 y 有变化 就是竖线。前提条件 i < arr.length - 1 ,是因为最后一组数据是目标格子的数据,只做参考,不画线。

当 arr[i].x < arr[i+1].x 时,从左往右连,直接设置left 即可,反之,则需要做针对位置进行调整,y 轴同理,将数值进行调整即可。


第八步,无可链接数据时,刷新界面重新排序。点击按钮调用 repaint_page() 函数。

点击调用

循环 行 和 列 如果,当前的格子已经隐藏(isRemove = true),则跳过当前循环,如果依然显示在界面上,则将其位置 push 到 arr数组中。 全部添加完成后,将数组随机排序。再次循环 行 和 列 ,已经消失的依然消失,没消失的位置 重新根据 数组存储 下来的 type 进行设置,展示相对应的图片。

repaint_page函数


以上,是全部内容,感觉总结比写代码还累,不过也算再整理一遍思路,好外还是有的。。。希望下次自己忘了的时候,看到这些记录能回忆起来

github地址:https://github.com/juanjuanGit/llk

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容