百玩不厌的消除类小游戏-水果连连看

311 阅读4分钟

我正在参加掘金社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛

前言

第一次尝试写小游戏,就挑最简单开始练手吧。有以下几点思路:

  • 首先提供偶数个水果图片;
  • 其次定义需要显示水果图片的格子数,为了方便最好是水果图片的整数倍;
  • 游戏开始时将水果图片随机渲染到每个格子中并绑定上x,y坐标等信息;
  • 通过点击格子去判断是否能互相连通;
  • 如果没有通路,允许重新排列;
  • 允许提示一个通路及所有通路。

实现

图片集

可以自己设计,也可以到各大网站下载。 image.png

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;
		}

页面渲染效果

image.png

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)//所有通路

游戏效果

game.gif