我正在参加掘金社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛
前言
第一次尝试写小游戏,就挑最简单开始练手吧。有以下几点思路:
- 首先提供偶数个水果图片;
- 其次定义需要显示水果图片的格子数,为了方便最好是水果图片的整数倍;
- 游戏开始时将水果图片随机渲染到每个格子中并绑定上x,y坐标等信息;
- 通过点击格子去判断是否能互相连通;
- 如果没有通路,允许重新排列;
- 允许提示一个通路及所有通路。
实现
图片集
可以自己设计,也可以到各大网站下载。
HTML代码
主要是确定好水果图片渲染的格子数、默认初始化图片及按钮集,代码如下:
<div class="game-title">Qiuの水果连连看</div>
<div class="game-container">
<div id="game-content">
<div id="row1" class="row">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
</div>
<div id="row2" class="row">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
</div>
<div id="row3" class="row">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
</div>
<div id="row4" class="row">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
</div>
<div id="row5" class="row">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
</div>
<div id="row6" class="row">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
</div>
<div id="row7" class="row">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
</div>
<div id="row8" class="row">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
<img class="col" src="./images/9.jpg">
</div>
</div>
<div class="controller">
<button id="restartBtn" class="btnStyle">开始</button>
<button id="resetBtn" class="btnStyle">重新排列</button>
<button id="tips" class="btnStyle">提示</button>
<button id="allRoads" class="btnStyle">所有通路</button>
<div id="showInfo"></div>
</div>
</div>
CSS代码
主要是对整个棋盘进行样式调整,使其更加美观,代码如下:
.game-title {
text-align: center;
line-height: 80px;
font-size: 30px;
color: #03a9f4;
font-weight: bold;
}
/* 游戏整体 */
.game-container {
width: 600px;
height: 450px;
margin: 0 auto;
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
background-color: #ffeb3b;
}
/* 内容展示 */
#game-content {
width: auto;
height: auto;
}
.row {
display: flex;
}
.row img {
width: 50px;
height: 50px;
flex: 1;
border: solid 1px gray;
box-sizing: border-box;
cursor: pointer;
}
/* 按钮 */
.controller {
width: 150px;
height: 400px;
margin-left: 20px;
padding: 10px;
box-sizing: border-box;
background-color: #feffb1;
}
.btnStyle {
width: 100%;
height: 30px;
margin-top: 20px;
text-align: center;
background-color: aquamarine;
}
/* 消息提示框 */
#showInfo {
font-size: 12px;
margin-top: 20px;
padding: 10px;
height: 120px;
overflow: auto;
border: 2px solid black;
background-color: aquamarine;
}
页面渲染效果
JS代码
整体样子有了,我们还需要加上一些事件按钮对其进行控制,这样才算完美,代码如下:
// 各节点参数
var gameContainer = document.getElementById('game-content');//游戏容器
var rows = document.getElementsByClassName('row');//行
var cols = document.getElementsByClassName('col');//列
var restartBtn = document.getElementById('restartBtn');//重新开始按钮
var resetBtn = document.getElementById('resetBtn');//重新排列按钮
var tips = document.getElementById('tips');//提示按钮
var allRoad = document.getElementById('allRoads')//所有通路按钮
var showInfo = document.getElementById('showInfo')//提示面板
var message = ''//存放提示语
var targetArr = [];//存放被选中的水果图片
var len = gameContainer.children.length;
var childLen = gameContainer.firstElementChild.children.length;
// 水果图片集
var fruits = ['./images/1.jpg', './images/2.jpg', './images/3.jpg', './images/4.jpg', './images/5.jpg', './images/6.jpg', './images/7.jpg', './images/8.jpg'];
// 初始化加载
window.onload = function () {
// 一进来就可以开始游戏了
reStart()
}
//游戏画面渲染
var setData = function (arr) {
var pic_value;
// 二维数组转一维数组
arr = [].concat.apply([], arr);//64个
// 渲染(水果图片随机)
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
r = Math.floor(Math.random() * (arr.length));//获取随机数
pic_value = arr[r];
arr.splice(r, 1)
// 设置标签属性
cols[i * 8 + j].setAttribute('x', i);
cols[i * 8 + j].setAttribute('y', j);
cols[i * 8 + j].setAttribute('isSelected', false);
cols[i * 8 + j].setAttribute('pic', pic_value);
cols[i * 8 + j].setAttribute('src', pic_value);
}
}
}
//获取当前画面上水果图片的分布情况
var getCellFruitImage = function () {
//用来保存当前游戏画面的所有img标签的src对象
var arrs = new Array();
for (var i = 0; i < len; i++) {
arrs[i] = new Array();
for (var j = 0; j < childLen; j++) {
arrs[i][j] = rows[i].children[j].getAttribute('src');
}
}
return arrs;
}
//开始及重新开始
var reStart = function () {
// 根据所给的一维数组初始化一个棋盘所需的二维数组
var fruit_arr = []//64个
for (var i = 0; i < 8; i++) {
fruit_arr[i] = []
fruit_arr[i] = fruit_arr[i].concat(fruits)
}
// 游戏画面渲染
setData(fruit_arr);
}
//重新排列
var reSet = function () {
// 获取当前游戏画面并重新渲染
setData(getCellFruitImage())
}
//所有通路:获取当前游戏画面上所有水果图片相互之间的有效连接情况
function getAllRoads() {
var roads = [];
var result = [];
var result_roads = []
var arr = getCellFruitImage();//获取当前游戏画面分布情况
//找到水果图片相同的点
for (var i = 0; i < len; i++) {
for (var j = 0; j < childLen; j++) {
if (arr[i][j] == './images/9.jpg') {//剔除被消除掉的点
continue;
} else {
for (var m = 0; m < len; m++) {
for (var n = 0; n < childLen; n++) {
if (arr[m][n] == arr[i][j] && !(m == i && n == j)) {
result = getAllRoad(m, n, i, j, arr);
if (result.length != 0)
roads.push(result);
}
}
}
}
}
}
// 去重及提示
for (var index in roads) {
var item = [...new Set(roads[index][0])]
// 返回结果
result_roads.push(item)
// 提示
if (index === '0') {
message = '所有可消除的坐标如下:<br>' + item + '<br>'
}
message += item + '<br>'
}
showInfo.innerHTML = message
return result_roads;
}
//提示:获取其中一条通路
function getTips() {
var roads = getAllRoads();
var arrs = roads.shift();
message = '可消除的坐标(x,y)分别为:' + arrs[0] + ' 和 ' + arrs[1]
showInfo.innerText = message
}
//游戏画面点击事件
gameContainer.onclick = function (e) {
var tag = e.target;//img
var aTag;
var arr;
var x0, y0, x1, y1;//坐标参数
for (var i = 0; i < len; i++) {
for (var j = 0; j < childLen; j++) {
aTag = gameContainer.children[i].children[j]
if (aTag === tag) {//如果选取的是图片标签则继续执行
targetArr.push(aTag);
targetArr[0].style.border = '2px solid red';//被点到的图片加红色边框
if (targetArr.length == 2) {//如果选取了2张图片
arr = getCellFruitImage();//获取当前游戏画面分布情况
// 消除红色边框
targetArr[0].style.border = null;
targetArr[1].style.border = null;
// 拿到被选取图片的坐标
x0 = targetArr[0].getAttribute('x');
y0 = targetArr[0].getAttribute('y');
x1 = targetArr[1].getAttribute('x');
y1 = targetArr[1].getAttribute('y');
// 如果被选取的图片一样,且不都为本身,那么寻找可连接的路径
if (targetArr[0].getAttribute('src') === targetArr[1].getAttribute('src') && targetArr[0] !== targetArr[1]) {
var arrTest = getAllRoad(x0, y0, x1, y1, arr);//根据坐标和当前游戏画面分布情况寻找有效路径
//判断点击的两图片是否在有效路径中
if (arrTest.length) {//有则消除
targetArr[0].setAttribute('src', './images/9.jpg');
targetArr[1].setAttribute('src', './images/9.jpg');
} else {//没有则提示
message = '没有可达的路径'
showInfo.innerText = message
}
} else {
message = '水果不相同'
showInfo.innerText = message
}
targetArr = [];
arr = null;
}
return;
}
}
}
}
//寻路(参数为两个点位置(坐标)和当前游戏画面分布情况)
function getAllRoad(x0, y0, x1, y1, arr) {
var t1 = parseInt(y0), t2 = parseInt(y1), t3 = parseInt(x0), t4 = parseInt(x1);
var flag;
var result = new Array();
var arr1 = [];
//取两点值比较大小
var getMinX = t3 < t4 ? t3 : t4;
var getMaxX = t3 > t4 ? t3 : t4;
var getMinY = t1 < t2 ? t1 : t2;
var getMaxY = t1 > t2 ? t1 : t2;
//存储节点间无阻挡区间端点
var startY, endY, startX, endX;
//横向遍历
//向左遍历
for (var i = y0 - 1; i >= 0; i--) {
if (arr[x0][i] == './images/9.jpg') {
t1--;
} else {
break;
}
}
for (var i = y1 - 1; i >= 0; i--) {
if (arr[x1][i] == './images/9.jpg') {
t2--;
} else {
break;
}
}
startY = t1 > t2 ? t1 : t2;
console.log('左侧区间分别为', t1, t2)
console.log('左侧区间端点', startY)
//向右遍历
t1 = parseInt(y0);
t2 = parseInt(y1);
for (var i = y0 - 0 + 1; i < childLen; i++) {
if (arr[x0][i] == './images/9.jpg') {
t1++;
} else {
break;
}
}
for (var i = y1 - 0 + 1; i < childLen; i++) {
if (arr[x1][i] == './images/9.jpg') {
t2++;
} else {
break;
}
}
endY = t1 < t2 ? t1 : t2;
console.log('右侧区间端点分别为', t1, t2)
console.log('右侧区间端点', endY)
if (startY == 0 || endY == childLen - 1) {
console.log('两点边界相连')
if (startY == 0) {
arr1.push(x0 + ',' + y0)
arr1.push(x0 + ',' + startY)
arr1.push(x1 + ',' + startY)
arr1.push(x1 + ',' + y1)
result.push(arr1);
arr1 = [];
console.log('通')
console.log('startY==0')
}
if (endY == childLen - 1) {
arr1.push(x0 + ',' + y0)
arr1.push(x0 + ',' + endY)
arr1.push(x1 + ',' + endY)
arr1.push(x1 + ',' + y1)
result.push(arr1);
arr1 = [];
console.log('通')
console.log('startY==len-1')
}
}
if (endY - startY < 0 && endY - startY != -1) {
console.log('两点构成的矩形,横向方向无法连接')
} else if ((x0 == x1) && (y0 - y1 == -1 || y0 - y1 == 1)) {
console.log('两点横向相邻');
arr1.push(x0 + ',' + y0)
arr1.push(x1 + ',' + y1)
result.push(arr1);
console.log('通')
arr1 = [];
} else {
for (var i = startY; i <= endY; i++) {
flag = 0;
for (var j = getMinX + 1; j < getMaxX; j++) {
if (arr[j][i] == './images/9.jpg') {
flag++;
} else {
break;
}
}
if (flag == (getMaxX - getMinX - 1)) {
if (flag == 0) {
console.log('两点处于相邻行')
console.log('通')
arr1.push(x0 + ',' + y0);
arr1.push(x0 + ',' + i);
arr1.push(x1 + ',' + i);
arr1.push(x1 + ',' + y1);
result.push(arr1);
arr1 = [];
} else {
console.log('一般情况')
console.log('通')
arr1.push(x0 + ',' + y0);
arr1.push(x0 + ',' + i);
arr1.push(x1 + ',' + i);
arr1.push(x1 + ',' + y1);
result.push(arr1);
arr1 = [];
}
}
}
}
console.log('横向遍历,所有通路', result.length)
console.log('横向所有通路分别为', result)
//纵向遍历
//向上遍历
for (var i = x0 - 1; i >= 0; i--) {
if (arr[i][y0] == './images/9.jpg') {
t3--;
} else {
break;
}
}
for (var i = x1 - 1; i >= 0; i--) {
if (arr[i][y1] == './images/9.jpg') {
t4--;
} else {
break;
}
}
startX = t3 > t4 ? t3 : t4;
console.log('上侧区间端点分别为', t3, t4);
console.log('上侧区间端点', startX);
t3 = parseInt(x0);
t4 = parseInt(x1);
//向下遍历
for (var i = x0 - 0 + 1; i < len; i++) {
if (arr[i][y0] == './images/9.jpg') {
t3++;
} else {
break;
}
}
for (var i = x1 - 0 + 1; i < len; i++) {
if (arr[i][y1] == './images/9.jpg') {
t4++;
} else {
break;
}
}
endX = t3 < t4 ? t3 : t4;
console.log('下侧区间端点分别为', t3, t4)
console.log('下侧区间端点', endX)
if (startX == 0 || endX == len - 1) {
console.log('两点边界相连')
if (startX == 0) {
arr1.push(x0 + ',' + y0)
arr1.push(startX + ',' + y0)
arr1.push(startX + ',' + y1)
arr1.push(x1 + ',' + y1)
result.push(arr1);
arr1 = [];
console.log('通')
console.log('startX==0')
}
if (endX == len - 1) {
arr1.push(x0 + ',' + y0)
arr1.push(endX + ',' + y0)
arr1.push(endX + ',' + y1)
arr1.push(x1 + ',' + y1)
result.push(arr1);
arr1 = [];
console.log('通')
console.log('startX==len-1')
}
}
if (endX - startX < 0 && endX - startX != -1) {
console.log('endX', endX, 'startX', startX)
console.log('两节点构成的矩形,纵向方向无法连接')
} else if ((y0 == y1) && (x0 - x1 == -1 || x0 - x1 == 1)) {
console.log('两点纵向相邻');
arr1.push(x0 + ',' + y0)
arr1.push(x1 + ',' + y1)
result.push(arr1);
console.log('通')
arr1 = [];
} else {
for (var i = startX; i <= endX; i++) {
flag = 0;
for (var j = getMinY + 1; j < getMaxY; j++) {
if (arr[i][j] == './images/9.jpg') {
flag++;
} else {
break;
}
}
if (flag == (getMaxY - getMinY - 1)) {
if (flag == 0) {
console.log('两点处于相邻列')
console.log('通')
arr1.push(x0 + ',' + y0);
arr1.push(i + ',' + y0);
arr1.push(i + ',' + y1);
arr1.push(x1 + ',' + y1);
result.push(arr1);
arr1 = [];
} else {
console.log('一般情况')
console.log('通')
arr1.push(x0 + ',' + y0);
arr1.push(i + ',' + y0);
arr1.push(i + ',' + y1);
arr1.push(x1 + ',' + y1);
result.push(arr1);
arr1 = [];
}
}
}
}
console.log('所有通条数', result.length)
console.log('所有通路分别为', result)
return result;
}
// 点击事件
restartBtn.addEventListener('click', reStart);//重新开始
resetBtn.addEventListener('click', reSet);//重新排列
tips.addEventListener('click', getTips)//提示
allRoads.addEventListener('click', getAllRoads)//所有通路