背景

思否社区首届「猫猫杯」线上代码共创大赛日程已靠近序幕,各路大神狂拽炫酷的我的项目曾经出炉了。奈何迁延症,我还在想“创意点”,同时思考到实现周期要赶上deadline,纠结之时,无意间在地铁上瞟见有人在看《最强大脑》,灵光一现,对哦!记得有一期有个拼图游戏——数字华容道,自此赌王之子(何猷君)一战成名。游戏原理不难,益智又好玩。

先来理解一下这款游戏规则:

  1. 将面板划分nxn个方格,除了最初一格,每一个方格有一个滑块;
  2. 初始游戏会将滑块程序打乱,空出最初一格;
  3. 空格相邻的滑块才能够滑动,并且只能滑倒空白格里;
  4. 将所有滑块按程序拼接则游戏闯关胜利;

思考齐全这次与思否猫主题联合,能够将滑块编码映射成背景图片的部分编码,这样就变成了拼图版数字华容道游戏,奈斯,想法有了,当初就开撸。

实现根底布局

首先实现根本布局,这里将600px X 600px面板均匀划分3x3方格。

<div class="main">  <section class="content">    <ul class="row" v-for="row in level" :key="row" :style="{width: `${appWidth}px`}">      <li class="col" v-for="col in level" :key="col" :style="{width: `${itemWidth}px`}">        <div          class="item"          :style="{            height: `${itemWidth}px`,           }"        >          {{row}}:{{col}}        </div>      </li>    </ul>  </section></div>
data() {  return {    appWidth: 600,    level: 3,  }},computed: {  itemWidth() {    return this.appWidth / this.level  },},

实现方格块背景图

可能咱们会将大图切图而后填充,切成3x3、4x4...,能够,然而不值得,太繁琐了,前面还会有不同的图片供选择作为拼图背景,工作量是成倍的。偷懒的是将背景图片应用CSS background-position 进行定位。

<div  class="item"  :style="{    height: `${itemWidth}px`,     backgroundPosition: getBgPos(row, col)  }">

定位逻辑是,将第(row, col)地位的方格的背景图从左上方平((col - 1)*width, (row - 1)*height)间隔。比方第2行第3列的背景图,往左挪动2个方格宽度,往右挪动1个方格高度。

// 获取背景图片地位getBgPos(row, col) {  const w = this.itemWidth  const { level } = this  const offsetX = ((col - 1) % level) * w  const offsetY = ((row - 1) % level) * w  return `-${offsetX}px -${offsetY}px`},
.col .item {  background-color: #99a9bf;  background-repeat: no-repeat;  background-image: url('./issue.png');  overflow: hidden;}

实现方格替换

这里应用Vue实现,数据驱动视图:响应式数据(数组)中两个元素替换地位,对应的是视图上的两个方格地位替换。这里咱们只需替换背景图地位信息来伪装元素挪动了。
响应式数组的数据结构定义成这样

[    {    index: 0, // 索引    bgPos: "0px 0px", // 背景图偏移量    isSpace: false // 是否是空白格  },  ...  {    index: 8, // 索引    bgPos: "-400px -400px", // 背景图偏移量    isSpace: true // 是否是空白格  }]

其次,生成有序和乱序的数组,别离对应游戏闯关胜利状态,和初始未开始状态的视图。
在乱序数组的最初一项预留,作为相邻滑块替换空间,实现形式是复制一份有序数组(除去最初一项),而后插入一个空白项。其中空白项将背景图片移出可视区即可。

// 初始化init() {  this.initOrigList()  this.initMessList()},// 初始化原始列表initOrigList() {  const { level } = this  const list = []  for (let i = 0; i < level; i++) {    for (let j = 0; j < level; j++) {      list.push({        index: i * level + j,        bgPos: this.getBgPos(i + 1, j + 1),        isSpace: false      })    }  }  this.origList = list},// 初始化乱序列表initMessList() {  // 除去最初一项的列表打乱  const list = this.sufflex(this.origList.slice(0, -1))  // 最初一项设置为空白:背景图片移出可视区  this.messList = [    ...list,    {      index: Math.pow(this.level, 2) - 1,      bgPos: `-${this.appWidth}px`,      isSpace: true    }  ]},

有序数组打乱这里采纳洗牌算法,原理很简略:从开端开始往前,后面随机选取一个数与最初的数调换。

// 洗牌算法sufflex(arr) {  const len = arr.length  const cards = [...arr]  let r = len - 1  while (r >= 0) {    const i = Math.floor(Math.random() * (r + 1))    ;[cards[r], cards[i]] = [cards[i], cards[r]]    r--  }  return cards},

其次,从生成布局上,每个方格的背景图地位要与该乱序数组相关联。

<div  class="item"  @click="handleMove(row, col)"  :style="{    height: `${itemWidth}px`,     backgroundPosition: getMessBgPos(row, col),  }"></div>

最初,在点击某一项方格时,判断它能不能挪动,要看它的上下左右相邻方格是否有空白格,有则与空白格替换地位,否则,不能挪动。

// 点击单元项挪动handleMove(row, col) {  const targetPos = this.getNearbySpacePos(row - 1, col - 1)  if (!targetPos.length) return  this.changePos((row - 1) * this.level + col - 1, targetPos[0] * this.level + targetPos[1])},// 获取以后点击模块紧挨着的空白模块地位// 如果有则能够挪动:以后模块上下左右相邻模块有空白模块则能够挪动getNearbySpacePos(row, col) {  const { level, messList } = this  // 上  if (row > 0 && messList[(row - 1) * level + col].isSpace) return [row - 1, col]  // 下  if (row < level - 1 && messList[(row + 1) * level + col].isSpace) return [row + 1, col]  // 左  if (col > 0 && messList[row * level + col - 1].isSpace) return [row, col - 1]  // 右  if (col < level - 1 && messList[row * level + col + 1].isSpace) return [row, col + 1]  return []},// 替换地位changePos(index, targetIndex) {  const temp = this.messList[index]  const targetTemp = this.messList[targetIndex]  this.$set(this.messList, targetIndex, temp)  this.$set(this.messList, index, targetTemp)},

至此,就简略实现了方格与相邻空白格的替换操作。

欠缺游戏性能

作为一款残缺的游戏,还有许多细节点须要补充优化。

  • 游戏开始和游戏完结开关;
  • 游戏难度(nxn中n越大,难度越大);
  • 反对更换本人喜爱的背景图片;
  • 游戏步数和用时记录;

以上性能的实现比较简单,就不一一阐明了,感兴趣的能够将代码点这里查看代码,或者间接点这里在线体验。
感激点赞&关注,最初祝你能够胜利闯关,完~

本文参加了1024程序员节,欢送正在浏览的你也退出。