小伙用Java开发经典小游戏连连看,程序员才是最牛逼的潜力股!

“连连看”这款经典小游戏想必九零后们都玩过,无论是在电脑上还是手机上,规则十分简单,就是将相同的方块连接起来消除,要求连接线不能大于3条,小编最近编写了一下这款经典小游戏,首先展示一下游戏界面:

如果有想学习java的同学,可来我们的java技术学习QQ群:165080868,免费送整套系统的java视频教程!我每晚上8点还会在群内直播讲解Java知识,欢迎大家前来学习哦。

解析:如何连线

连连看游戏的核心在于如何判断两个方块是相连的。这其实是一般游戏中寻路算法的一种,如何寻找从一个点到另一个点的最短路径,但是实现一个寻路算法的过程比较复杂,本文就不深入展开了。这里会使用另一种思路来寻找路径。

在连连看中,能消除的情况只有以下三种。

直线相连。

一个拐角。

两个拐角。

前两种情况相对来说比较简单,但第三种情况在游戏中可能出现多种连线。

看似比较复杂,其实只需要先分别计算出两个方块在地图上 X 轴与 Y 轴上能直接联通的坐标,每个方块得到两个数组(X 轴与 Y 轴上能直接联通的点),然后对两个方块的 X 轴和 Y 轴的数组做一个比较运算,即可得到拐角所在的位置。

上面简单分析了一下连线基本的实现思路,下面开始写代码。

流程

生成地图

点击事件

获得连接线

绘制连接线

生成地图

第一件要干的事当然是随机生成一个地图。

game.vue

<template>

  <div :class="currentTheme.name">

    <table class="game" @click="handleClick">

      <tr :key="row" v-for="(cols, row) in cellData">

        <cell :key="col" v-for="(cell, col) in cols" :isSelected="cell.isSelected" :isLine="cell.isLine" :lineClass="cell.lineClass" :isBlank="cell.isBlank" :className="cell.className"></cell>

      </tr>

    </table>

  </div>

</template>

<script>

import Cell from './cell'

import Utils from '../utils'

import config from '../config'

import themes from '../themes'

export default {

  components: {

    Cell

  },

  data () {

    return {

      cellData: [], //  地图数据数组

      currentSelect: null, // 当前选中的方块

      config: Object.assign(config) // 配置信息

    }

  },

  computed: {

    currentTheme () {

      // 当前theme

      return themes.filter(e => e.name === this.config.defaultTheme)[0]

    }

  },

  mounted () {

    this.init()

  },

  methods: {

    // ...

  }

}

</script>

cell.vue

这里使用了一个空的 div 的 :before 和 :after 伪类来展示连接线

<template>

  <td :class="classNames">

    <div v-if="isLine" :class="lineClass"></div>

  </td>

</template>

<script>

  export default {

    name: 'cell',

    props: ['isSelected', 'isBlank', 'className', 'lineClass', 'isLine'],

    computed: {

      classNames () {

        return {

          'selected': this.isSelected, // 选中

          'blank': this.isBlank, // 空白

          [this.className]: true

        }

      }

    }

  }

</script>

为了更好的对连连看的方块内容进行拓展,我们使用一个数组来装不同色块的 className,然后将对应的 className 放到色块上,通过 css 来控制色块的背景图片。

init(){

console.time('initData')

this.cellData=this.initData()

console.timeEnd('initData')

},

initData(){

// classNames => ['a','b','c','d'] 每个元素代表一个方块的className

// 生成一个方块的数组,将className放到其中letcellGroup=this.currentTheme.classNames.map(e=>{

return{

isBlank:false,// 是否空白方块

className:e,// 方块的className

lineClass:'',// 连接线的className

isLine:false,// 是否显示连接线

isSelected:false

}

})

// 空白方块

letblankCell{

isBlank:true,

className:'',

lineClass:'',

isLine:false,

isSelected:false

}

// 先根据配置中的方块个数从方块数组中随机取出几条letrandomCellGroup=Utils.arrayRandom(cellGroup,this.config.cellGroupCount)

// 再根据配置中的行和列随机填充一个地图数据letcellData=Utils.arrayFillByRandomGroup(this.config.row*this.config.col,randomCellGroup)

// 将数据根据行的大小转为二维数组,然后外部包裹一层空白节点

/* 

 * Utils.dyadicArrayWrap 

 * Wrap a dyadic array by fill

* @params arr the source arr

* @params fill which to wrap source arr 

 * 

 *                               0 0 0 0 0 

 * 1 1 1                     0 1 1 1 0 

 * 1 1 1     =>            0 1 1 1 0 

 * 1 1 1                     0 1 1 1 0 

 *                              0 0 0 0 0 */letresult=Utils.dyadicArrayWrap(Utils.arrayToDyadic(cellData,this.config.col),blankCell)

// 最后把行和列的坐标设置到节点上

result.forEach((cols,row)=>{

cols.forEach((cell,col)=>{

cell.row=row

cell.col=col

})

})

returnresult

}

最后我们得到了一个地图数据的二维数组。

选取事件

接下来就是选取方块的事件了。

为了提高性能,没有将点击事件直接绑定到方块上,而是通过绑定在外层的 table 上,用事件代理来实现。这里也吐槽一下 Vue 目前是没有事件代理的,为了提高绑定的性能需要自己实现一个事件代理。

// 点击事件代理

handleClick(ev){

// 如果点击事件不是触发在方块上那么退出if(ev.target.nodeName!=='TD')return

// 获取点击方块的坐标,如果方块是空的那么退出letcol=ev.target.cellIndex

letrow=ev.target.parentNode.rowIndex

letcurrentCell=this.cellData[row[col]

if(currentCell.isBlank===true)return

this.selectCell(currentCell)

},

// 选择方块

selectCell(currCell){

if(!this.currentSelect){

// 如果没有选中任何方块那么就直接设置选中currCell.isSelected=true

this.currentSelect=currCellre

turn

}

if(this.currentSelect===currCell){

// 如果点击的方块和已选中方块是同一个,那么就取消这个方块的选中状态currCell.isSelected=false

this.currentSelect=null

return

}

letprevCell=this.currentSelect

// 通过className来判断前后两个方块的图片是否相同if(prevCell.className!==currCell.className){

// 如果两个方块的className不同,那么将点击的方块设置为选中状态prevCell.isSelected=false

currCell.isSelected=true

this.currentSelect=currCell

return

}

// 获取两个方块的连接线路径数组console.time('getLine')

letresult=this.getLine(prevCell,currCell)

console.timeEnd('getLine')

if(result.length===0){

// 如果没有获取到连接线,说明两个方块无法连接,那么将点击的方块设置为选中状态prevCell.isSelected=false

currCell.isSelected=true

this.currentSelect=currCell

}else{

// 如果获取到连接线,那么将两个方块设置为空白方块prevCell.isBlank=true

currCell.isBlank=true

prevCell.className=''

currCell.className=''

prevCell.isSelected=false

currCell.isSelected=false

this.currentSelect=null

// 最后绘制连接线

this.drawLine(result)

}

}

选择的逻辑在这里就判断完毕了,接下来也是本文的核心,如何获得连接线路径。.

获得连接线

这个内容稍微有点长,分为几块来写。

首先描述一下在下文中多次提到的可连接线。

在查找可连接线时,红色方块是需要查找的方块,黑色是其他方块,白色是空白方块,那么可以从红色方块开始向前与向后遍历得到一个可以达到的方块Set对象,也就是图中所有的白色方块。

getLine

获取连接线的入口

getLine(prev,curr){

// 连接线数组

letresult=[]

// 一条直线连通的情况

// 分别获取上一个选中方块的X轴与Y轴上的可连接线,这里getHorizontalLine与getVerticalLine返回的均为一个Set对象,使用has来快速高效地判断在可连接线上是否包含某个方块letprevH=this.getHorizontalLine(prev)

letprevH=this.getHorizontalLine(prev)

if(prevH.has(curr))returnthis.getBeeline(prev,curr)

letprevV=this.getVerticalLine(prev)

if(prevV.has(curr))returnthis.getBeeline(prev,curr)

// 如果直线连通失败了,那么获取另一个方块的可连接线

letcurrH=this.getHorizontalLine(curr)

letcurrV=this.getVerticalLine(curr)

// 做一个快速判断,如果其中一个方块在X轴和Y轴上的可连接线长度都为0,那么返回空数组

if((!prevH.size&&!prevV.size)||(!currH.size&&!currV.size))returnresult

// 一个拐角可以连通的情况

// 分别对X轴和Y轴上的可连接线做一个交点判断

etintersection=this.getIntersection(prevH,currV)||this.getIntersection(prevV,currH)

以上为部分代码,想要看完整代码的大佬请加上java编程学习QQ群:165080868

欢迎关注胖胖爱Java的简书号,可视化学习java,每天更新文章,让Java学习更加简单。

声明:本文内容来源于网络,如有侵权请联系删除

猜你喜欢

转载自blog.csdn.net/tanzhouxiaomanxi/article/details/89284427