基于JS写了个贪吃蛇,记录一下其中的设计思路等等内容
背景
- 地图:就是一个二维数组,二维数组的下标对就相当于X、Y轴,无论是身体上的元素,还是要被吃掉的点,都可以转化为二维数组的一个坐标
- 运动:使用时间间隔,每隔一段时间就重新根据方向计算蛇的位置,保持蛇的移动
基础信息
- 目标点:随机生成在地图上,且不能生成在蛇身上
- 蛇:一个坐标数组
- 方向:根据键盘输入获得,如果没有则保持当前方向
总体思路
- 初始化地图:打印整张地图
- 初始化蛇:给定一个默认的蛇身和当前运动方向
- 初始化目标点
- 异步获取键盘输入,将其赋值给当前运动方向
- 根据当前方向预计算下一个坐标点
- 判断下一个坐标点是否在蛇身体中,或超出边界
- 如果在身体中或超出边界,不符合游戏规则,游戏结束,跳出时间间隔函数(这里面有一个小坑,下文填)
- 满足游戏规则,判断预坐标点是否为目标点
- 如果不是,则将预坐标点加入数组第一个,并删除最后一个
- 如果是,则将预坐标点加入数组第一个
具体实现
这里只写出思路中的方法和其作用,必要时写点伪代码
属性
- 蛇身数组
- 随机点坐标
- 当前运动方向
方法
- 展示函数(需要被改色的数据):用来改变需要变色的表格
功能说明- 需要判断传入参数为单个点还是数组
- 如果是单个点,则直接改色
- 如果是数组,则数组头和身分别改色
- 随机点坐标生成函数(蛇数组):用来根据蛇的身体计算随机点坐标
功能说明- 生成一个随机点
- 判断该点是否在蛇数组中
- 如果不在,返回该随机点坐标
- 如果在,递归调用自身,再次生成
- 初识化地图函数
功能说明- 根据地图尺寸,生成对应行、列的表格
- 对其中的每个格(td),赋一个id值,这个id值是根据当前行列拼接成的
- id转坐标函数(id)
功能说明- 传入一个字符串的id值,返回对应的坐标数组
- 坐标转id函数(坐标)
功能说明- 传入一个坐标数组,返回对应的id
写不动了,懒得写了,直接传代码吧,有问题评论区沟通。。。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>贪吃蛇练习</title>
<style type="text/css">
table {
margin: auto;
border: 1px solid white;
}
td {
border: 1px solid white;
border-right-color: 1px solid black;
border-bottom-color: 1px solid black;
background-color: gainsboro;
height: 20px;
width: 20px;
}
</style>
</head>
<body onkeydown="changeSnakeDirection(event);">
<table border="1" id="mapTable" cellspacing="1" cellpadding="">
</table>
</body>
<script type="text/javascript">
//函数起始区
var MAP_ROWS = 20;
var MAP_COLS = 20;
var runTime = 200;
//定义默认蛇数组
var snake = ['0_3', '0_2', '0_1', '0_0'];
//定义随机点的ID
var randomCellId;
//定义默认方向
var nowDirection = 40;
var inputDirection;
window.onload = function() {
//初始化地图表格
//声明图像区 //定义地图的行数和列数
initMapTable();
//1.显示内容
//显示随机点和蛇数组
randomCellId = createRandomCell(snake);
changeTableColor(snake);
alert("点击确定开始游戏");
//开始间隔执行
var gameStartId = window.setInterval(function() {
//显示蛇数组
//changeTableColor(snake);
//2.获取方向,生成预期坐标点
//定义初始方向为下,根据键盘事件改变方向,且同方向的正反值无效
nowDirection = calcSnakeDirection(nowDirection, inputDirection);
// 获取蛇头坐标
var snakeHeadCoordinate = changeCellIdToCoordinate(snake[0]);
var nextHeadCoordinate = calcNextHeadCoordinate(snakeHeadCoordinate, nowDirection);
//3.判断坐标点是否出界或撞到自身
var gameResult = jundgeGameRule(nextHeadCoordinate);
if(gameResult) { //4.满足游戏规则,进行蛇数组运动计算
calcSnakeMove(nextHeadCoordinate, randomCellId);
} else { //5.游戏结束,提示,重启
clearInterval(gameStartId);
userResult = window.confirm("游戏结束,点击确定重新开始");
if(userResult) {
location.reload();
}
}
}, runTime);
}
//计算蛇数组的运动,根据预落点将其写入蛇数组
//如果吃掉随机点,将随机点增加到蛇数组
function calcSnakeMove(nextHeadCoordinate, randomCellId) {
var nextHeadId = changeRandNumToId(nextHeadCoordinate[0], nextHeadCoordinate[1]);
var snakeLength = snake.length;
var snakeLastCell = snake[(snakeLength - 1)];
snake.unshift(nextHeadId);
if(nextHeadId != randomCellId) {
snake.length = snakeLength;
changeTableColor(snakeLastCell, "gainsboro");
} else {
randomCellId = createRandomCell(snake);
}
changeTableColor(snake);
return null;
}
//判断游戏规则,满足规则返回True,失败返回False
function jundgeGameRule(nextHeadCoordinate) {
//判断是否出界
nextHeadId = changeRandNumToId(nextHeadCoordinate[0], nextHeadCoordinate[1]);
if(nextHeadCoordinate[0] < 0 ||
nextHeadCoordinate[1] < 0 ||
nextHeadCoordinate[0] >= MAP_COLS ||
nextHeadCoordinate[1] >= MAP_ROWS
) {
return false;
} else if((!jundgeRandomCellInSnake(nextHeadId)) && !(jundgeRandomCellInSnakeLast(nextHeadId))) { //判断是否撞到自身
return false;
} else {
return true;
}
}
//根据当前蛇头坐标、当前方向计算下一步的坐标
function calcNextHeadCoordinate(snakeHeadCoordinate, nowDirection) {
switch(nowDirection) {
case 37:
snakeHeadCoordinate[0] = parseInt(snakeHeadCoordinate[0]) - 1;
break;
case 39:
snakeHeadCoordinate[0] = parseInt(snakeHeadCoordinate[0]) + 1;
break;
case 38:
snakeHeadCoordinate[1] = parseInt(snakeHeadCoordinate[1]) - 1;
break;
case 40:
snakeHeadCoordinate[1] = parseInt(snakeHeadCoordinate[1]) + 1;
break;
default:
break;
}
return snakeHeadCoordinate;
}
//根据键盘按键获取方向
function changeSnakeDirection(event) {
//获取按键对象
var eve = window.event || event;
//获取用户按键值
inputDirection = eve.keyCode;
if(typeof(inputDirection) == "number") {
return inputDirection;
} else {
return null;
}
}
function calcSnakeDirection(nowDirection, inputDirection) {
//判断有效按键
if(inputDirection != null &&
inputDirection >= 37 &&
inputDirection <= 40) {
//判断反方向按键失效
if(Math.abs(nowDirection - inputDirection) != 2) {
nowDirection = inputDirection;
}
}
return nowDirection;
}
//生成随机点
function createRandomCell(snake) {
var flagIndex = false;
while(!flagIndex) {
//生成map范围内的随机点
var randomNumX = createRandomNum(MAP_COLS);
var randomNumY = createRandomNum(MAP_ROWS);
//将随机点转成id
randomCellId = changeRandNumToId(randomNumX, randomNumY);
//判断是否在蛇数组中
flagIndex = jundgeRandomCellInSnake(randomCellId, flagIndex);
}
changeTableColor(randomCellId, "red");
return randomCellId;
}
//判断目标点是否在蛇数组中,如果不存在返回true
function jundgeRandomCellInSnake(targetCellId, flagIndex) {
targetCellIdIndex = snake.indexOf(targetCellId);
if(targetCellIdIndex < 0) {
flagIndex = true;
}
return flagIndex;
}
//判断目标点是否在蛇数组的最后一位,如果存在返回true
function jundgeRandomCellInSnakeLast(targetCellId) {
targetCellIdIndex = snake.indexOf(targetCellId);
snakeLastIndex = snake.length - 1;
if(targetCellIdIndex == snakeLastIndex) {
return true;
} else {
return false;
}
}
//将随机点坐标转成id,返回字符串的id
function changeRandNumToId(randomNumX, randomNumY) {
var randCellId = randomNumX + "_" + randomNumY;
return randCellId;
}
//将id转成坐标,返回坐标数组
function changeCellIdToCoordinate(cellId) {
cellCoordinate = cellId.split("_");
return cellCoordinate;
}
//生成0到最大值范围内的随机整数
function createRandomNum(max) {
var randomNum = parseInt(Math.random() * (max));
return randomNum;
}
//给定坐标或数组,改变对应坐标的颜色
function changeTableColor(targetCell, col) {
targetCellType = typeof(targetCell);
//判断是单个点,还是蛇数组
if(targetCellType == null) {
alert('打印图形参数异常!');
return null;
} else if(targetCellType == "string") { //判断为单个点
changeColor(targetCell, col);
} else { //蛇数组
changeColor(targetCell[0], "red");
for(var i = 1; i < targetCell.length; i++) {
changeColor(targetCell[i], "pink");
}
}
}
//改变某个坐标的颜色
function changeColor(targetCell, color) {
document.getElementById(targetCell).style.backgroundColor = color;
}
/**
* 初始化地图表格
*/
function initMapTable() {
//获取到table对象
var mapTable = document.getElementById("mapTable");
//循环创建行和列
for(var i = 0; i < MAP_ROWS; i++) {
//创建行
var row = mapTable.insertRow(mapTable.rows.length);
for(var j = 0; j < MAP_COLS; j++) {
//创建列
var td = row.insertCell();
//为每一个td设计一个id
td.id = j + "_" + i;
}
}
}
function getElementStyle(obj, attr) {
if(obj.currentStyle) {
return obj.currentStyle[attr];
} else {
return getComputedStyle(obj, false)[attr];
}
}
/**
* 根据给定方向计算下一个预落点是否撞身
* @param {Object} nowDirection
*/
function jundgeCollision(nowDirection) {
//获取蛇头坐标
var snakeHeadCoordinate = changeCellIdToCoordinate(snake[0]);
var nextHeadCoordinate = calcNextHeadCoordinate(snakeHeadCoordinate, nowDirection);
//3.判断坐标点是否出界或撞到自身
var collisionResult = jundgeGameRule(nextHeadCoordinate);
return collisionResult;
}
</script>
</html>