有没有java开发千行代码小游戏(小伙用Java开发经典小游戏连连看)
有没有java开发千行代码小游戏(小伙用Java开发经典小游戏连连看)前两种情况相对来说比较简单,但第三种情况在游戏中可能出现多种连线。两个拐角。在连连看中,能消除的情况只有以下三种。直线相连。一个拐角。
“连连看”这款经典小游戏想必九零后们都玩过,无论是在电脑上还是手机上,规则十分简单,就是将相同的方块连接起来消除,要求连接线不能大于3条,小编最近编写了一下这款经典小游戏,首先展示一下游戏界面:
解析:如何连线
连连看游戏的核心在于如何判断两个方块是相连的。这其实是一般游戏中寻路算法的一种,如何寻找从一个点到另一个点的最短路径,但是实现一个寻路算法的过程比较复杂,本文就不深入展开了。这里会使用另一种思路来寻找路径。
小编整理了一份java学习资料,私信回复【01】,获取源码。
在连连看中,能消除的情况只有以下三种。
直线相连。
一个拐角。
两个拐角。
前两种情况相对来说比较简单,但第三种情况在游戏中可能出现多种连线。
看似比较复杂,其实只需要先分别计算出两个方块在地图上 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)
以上为部分代码
声明:本文内容来源于网络,如有侵权请联系删除