写一个贪吃蛇小游戏
上下左右键盘移动贪吃蛇吃葡萄
结果 gif 图 ,吃了5个葡萄,游戏结束时左上角为总得分。
界面和css代码这里就不加赘述了,主要贴js代码(加了注释):
var config = {
width: 20, //一个格子的宽度
height: 20, //一个格子的高度
tr: 30, //行数
td: 30 //列数
}
var snake = null, //Snake的实例
food = null, //Food的实例
game = null; //游戏的实例
//我们把蛇移动的整个区域设置成一个具有30列30行的网格坐标
//方块(格子)坐标位置
function Square(x, y, className) {
this.x = x*config.width;
this.y = y*config.height;
this.className = className;
this.contentDom = document.createElement('div');//该位置的方块对应的DOM元素
this.contentDom.className = this.className;
this.parent = document.getElementsByClassName("innerSnake")[0];
}
Square.prototype.create = function() { //创建方块并添加到页面
this.contentDom.style.position = 'absolute';
this.contentDom.style.width = config.width + 'px';
this.contentDom.style.height = config.height + 'px';
this.contentDom.style.left = this.x + 'px';
this.contentDom.style.top = this.y + 'px';
this.parent.appendChild(this.contentDom);
};
Square.prototype.remove = function() { //移除方块
this.parent.removeChild(this.contentDom);
};
//蛇
function Snake() {
this.head = null; //蛇头
this.tail = null; //蛇尾
this.pos = []; //二维数组,存储蛇身上每个节点(方块)
this.directionKey = { //存储蛇走的方向
left: { //往左走
x: -1, //横坐标减1,一个坐标表示一个格子
y: 0, //纵坐标不变
rotate: 90
},
right: { //往右走
x: 1,
y: 0,
rotate: -90
},
up: { //往上走
x: 0,
y: -1,
rotate: 180
},
down: { //往下走
x: 0,
y: 1,
rotate: 0 //蛇头图片方向,顺时针为正
}
}
}
Snake.prototype.init = function() { //初始化蛇
//蛇头
var snakeHead = new Square(2,0,"head");
snakeHead.create();
this.head = snakeHead; //存储蛇头信息
this.pos.push([2,0]); //存储蛇头坐标
//蛇的第1节身体
var snakeBody1 = new Square(1,0,"body");
snakeBody1.create();
this.pos.push([1,0]);
//蛇的尾巴
var snakeTail = new Square(0,0,"body");
snakeTail.create();
this.tail = snakeTail;
this.pos.push([0,0]);
//形成链表关系
snakeHead.prev = null;
snakeHead.next = snakeBody1;
snakeBody1.prev = snakeHead;
snakeBody1.next = snakeTail;
snakeTail.prev = snakeBody1;
snakeTail.next = null;
//初始蛇的走向,后面想改变蛇的走向即改变this.direction
this.direction = this.directionKey.right; //默认向右走
};
//获取蛇头下一个位置对应的元素,根据元素做下一个动作
Snake.prototype.getNextPos = function() {
var nextPos = [ //获取蛇头走的下一个点的坐标
this.head.x / config.width + this.direction.x,
this.head.y / config.height + this.direction.y
];
//判断下一个点是自己or食物or围墙or无障碍?
var self = false; //设置下一个点是否是自己
this.pos.forEach(function(val) { //val即二位数组中的一个坐标
if(val.toString() === nextPos.toString()) { //下一个坐标等于蛇全部身体的一个,即下一个点是自己
self = true;
}
});
if(self) {
// console.log('撞到自己了!');
this.collide.end.call(this); //game over
return;
} else if(nextPos[0] < 0 || nextPos[1] < 0 || nextPos[0] > config.td-1 || nextPos[1] > config.tr-1) {
// console.log('撞到墙壁了!');
this.collide.end.call(this); //game over
return;
} else if (food && food.pos[0] === nextPos[0] && food.pos[1] === nextPos[1]) {
console.log('撞到食物了!');
this.collide.eat.call(this);
} else {
// console.log('啥都没遇到!');
this.collide.move.call(this, false); //注意:.call(this)重新设置this指向,使其指向当前实例对象Snake
}
};
//处理碰撞后的事件
Snake.prototype.collide = {
move: function(isEat) { //isEat 是否吃了食物,不是则删除蛇尾
var x = this.head.x / config.width + this.direction.x,
y = this.head.y / config.height + this.direction.y;
//声明一个新身体
var newBody = new Square(this.head.x/config.width, this.head.y/config.height, "body");
//更新链表关系
newBody.next = this.head.next;
newBody.next.prev = newBody;
newBody.prev = null;
this.head.remove(); //删除旧蛇头
newBody.create(); //添加蛇身体,替代在旧蛇头位置
//声明一个新蛇头(下一个走的点)
var newHead = new Square(x, y, "head");
//更新链表关系
newHead.prev = null;
newHead.next = newBody;
newBody.prev = newHead;
this.pos.unshift([x, y]); //更新蛇节点的坐标this.pos
this.head = newHead; //更新this.head的信息
newHead.contentDom.style.transform = `rotate(${this.direction.rotate}deg)`
newHead.create(); //添加蛇头
//删除蛇尾:吃食物则不删
if(!isEat) { //没有吃食物,删除蛇尾
this.tail.remove();
this.tail = this.tail.prev;
this.pos.pop(); //更新蛇节点坐标
}
// console.log(this.pos); //打印数组,验证
},
eat: function() {
this.collide.move.call(this, true); //传参true,表示此时为吃操作
food.remove();
game.score ++; //记录分数
createFood();
},
end: function() {
console.log('end');
game.gameOver();
}
}
snake = new Snake();
//创建食物
function createFood() {
var x = null, y = null;
var include = true; //表示食物的位置是否在蛇身上
var random = function(max, min) { //产生一个随机数
return Math.floor(Math.random()*(max - min + 1))
};
while(include) {
x = random(config.tr - 1, 0);
y = random(config.td - 1, 0);
snake.pos.forEach(function(val) {
if(x != val[0] && y != val[1]) {
include = false;
}
});
}
//生成食物
food = new Square(x, y, "food");
food.pos = [x, y]; //记录食物坐标
food.create();
}
//游戏逻辑
function Game() {
this.score = 0; //分数
this.timer = null; //计时器
}
Game.prototype.init = function() {
snake.init();
// snake.getNextPos(); //获取下一个点坐标
createFood();
document.onkeydown = function(event) {
if(event.which == 37 && snake.direction != snake.directionKey.right) {
//鼠标左键,蛇不能是正在往右走
snake.direction = snake.directionKey.left;
} else if (event.which == 38 && snake.direction != snake.directionKey.down) {
//鼠标上键
snake.direction = snake.directionKey.up;
} else if (event.which == 39 && snake.direction != snake.directionKey.left) {
//鼠标右键
snake.direction = snake.directionKey.right;
} else if (event.which == 40 && snake.direction != snake.directionKey.up) {
//鼠标下键
snake.direction = snake.directionKey.down;
}
}
this.start();
};
game = new Game();
//开始游戏
Game.prototype.start = function() {
this.timer = setInterval(function() {
snake.getNextPos();
}, 200);
};
//游戏结束
Game.prototype.gameOver = function() {
console.log("gameOver");
clearInterval(this.timer);
var gameOver = document.querySelector('.gameOver');
var gameScore = document.querySelector('.gameOver .score');
gameOver.style.display = 'block';
gameScore.innerHTML = `${this.score}`;
};
//开启游戏
function startGame() {
var startBtn = document.querySelector('.btn button');
var snakeWrap = document.querySelector('.snakeWrap');
startBtn.onclick = function() {
startBtn.parentNode.style.display = 'none';
snakeWrap.style.display = 'block';
game.init();
}
}
startGame();
主要用到链表数据结构,后续项目代码会上传到gitee上~