(文末提供原码)
贪吃蛇游戏主要由蛇、食物这两部分组成。再加上游戏的控制部分。因此采用面向对象的方法可以很好的解决问题。
目录
主页部分:
完成一些基本背景的布局和游戏初始化的设定。
通过链接引入蛇类、食物类、游戏类:
<script src="./Food.js"></script>
<script src="./Snake.js"></script>
<script src="./Game.js"></script>
css样式:
<style>
* {
margin: 0;
padding: 0;
}
.map {
width: 1350px;
height: 600px;
/* background-color: #ccc; */
position: relative;
left: 0px;
top: 0px;
}
.btn{
position: absolute;
top: 270px;
left: 620px;
width: 80px;
height: 60px;
}
</style>
页面基本功能:
<script>
// 1.获取地图
var map = document.getElementById('map')
// 2.创建一个Game控制对象
var game1 = new Game(map);
game1.start();
// 点击button,重新开始
function reload(){
window.location.reload();
}
</script>
游戏类:
游戏类负责控制渲染页面、蛇和食物、控制蛇的移动等功能
1.声明一个Game构造函数,生成这个对象
function Game (map) {
this.food1 = new Food()
this.snake1 = new Snake()
this.map = map
that = this
}
2.准备开始的方法
Game.prototype.start = function () {
// 2.1渲染食物和蛇
this.food1.render(this.map)
this.snake1.render(this.map)
// 2.2让蛇自动动起来,并且渲染
snakeAutoMove()
bindKey()
}
3.定时器让蛇动起来
function snakeAutoMove () {
var timeId = setInterval(function () {
this.snake1.move(this.food1, this.map)
// 判断蛇移动是否出界,如果蛇头坐标小于0,或者大于宽/高,就出界了
var snakeHeadX = this.snake1.body[0].x * this.snake1.width;
var snakeHeadY = this.snake1.body[0].y * this.snake1.height;
if (snakeHeadX < 0 || snakeHeadY < 0 || snakeHeadX >= this.map.offsetHeight || snakeHeadY >= this.map.offsetWidth) {
// 出界就结束游戏
alert('Game Over')
clearInterval(timeId)
return;
}
// 吃到自己身体也死
if (this.snake1.body.length > 0) {
for (var i = 1; i < this.snake1.body.length; i++) {
if (this.snake1.body[0].x == this.snake1.body[i].x && this.snake1.body[0].y == this.snake1.body[i].y){
alert('咬蛇自尽')
clearInterval(timeId)
}
}
}
this.snake1.render(this.map);
}.bind(that), 100);
}
4.让蛇根据那键盘按键来移动
function bindKey () {
document.onkeydown = function (e) {
e = e || window.event;
switch (e.keyCode) {
case 37:
if (this.snake1.direction != 'right') {
this.snake1.direction = 'left';
}
break;
case 38:
if (this.snake1.direction != 'bottom') {
this.snake1.direction = 'top';
}
break;
case 39:
if (this.snake1.direction != 'left') {
this.snake1.direction = 'right';
}
break;
case 40:
if (this.snake1.direction != 'top') {
this.snake1.direction = 'bottom';
}
break;
}
}.bind(that);
}
蛇类:
1.创建一条蛇
// 声明一个list保存蛇
var list = [];
// 1.声明一个构造函数,创建蛇对象
function Snake (width, height, direction) {
this.width = width || 30;
this.height = height || 30;
// 蛇开始默认向右走
this.direction = direction || 'right';
// 初始化创建出来的蛇,用数组来表示蛇身体,吃到了就添加一个
this.body = [
// 蛇的每一节身体都有不同的x,y
{ x: 1, y: 2, bgColor: '#3EBFA0'},
{ x: 1, y: 1, bgColor: '#8DE2C9'}
];
}
2.把创建出来的蛇对象渲染到地图上
Snake.prototype.render = function (map) {
// 渲染新蛇之前删除老蛇
remove(map)
// 把蛇的每一节遍历出来
for (var i = 0; i < this.body.length; i++) {
// 创建div
var div1 = document.createElement('div');
// 把当前蛇这一节身体所有的显示信息赋值给这个div
div1.style.width = this.width + 'px';
div1.style.height = this.height + 'px';
div1.style.backgroundColor = this.body[i].bgColor;
div1.style.position = 'absolute';
div1.style.borderRadius = 20 + 'px';
div1.style.top = this.body[i].x * this.width + 'px';
div1.style.left = this.body[i].y * this.height + 'px';
// 把div添加到map中
map.appendChild(div1);
list.push(div1)
}
}
3.蛇的移动
Snake.prototype.move = function (food, map) {
// 3.1蛇身的移动,从蛇尾开始改变,比较简单
for (var i = this.body.length - 1; i > 0; i--) {
this.body[i].x = this.body[i - 1].x;
this.body[i].y = this.body[i - 1].y;
}
// 3.2根据方向改变蛇头
switch (this.direction) {
case 'right':
this.body[0].y++;
break;
case 'left':
this.body[0].y--;
break;
case 'top':
this.body[0].x--;
break;
case 'bottom':
this.body[0].x++;
break;
}
// 判断蛇有没有吃到食物,蛇头是否与食物重叠
var snakeHeadX = this.body[0].x * this.width
var snakeHeadY = this.body[0].y * this.width
var foodX = food.x
var foodY = food.y
// 去除蛇尾的坐标备用
var lastSnakeUnit = this.body[this.body.length - 1];
// 判断
if (snakeHeadX == foodX & snakeHeadY == foodY) {
// 吃到了食物,长身体
this.body.push({
x: lastSnakeUnit.x,
y: lastSnakeUnit.y,
bgColor: '#7CDEC1'
})
// 产生新食物,即重新渲染
food.render(map)
}
}
4.声明一个方法,删除老蛇
function remove (map) {
for (var i = 0; i < list.length; i++) {
map.removeChild(list[i]);
}
// 删除list里真正的蛇
list = []
}
食物类
1.创建一个食物
// 声明一个list存放食物
var list = []
// 1.创建食物对象的构造函数
function Food (width, height, bgColor, x, y) {
this.width = width || 30
this.height = height || 30
this.bgColor = bgColor || '#0061BC'
this.x = x || 0
this.y = y || 0
}
2.根据Food构造函数创建出来的食物对象,渲染到地图上
Food.prototype.render = function (map) {
removeFood(map);
// 2.1给食物对象随机生成xy坐标
// 生成0-1的随机数,乘上地图宽/小方块宽,向下取整。就是对于整个地图来说的随机数,再乘上div宽,就可以随机生成在地图内的x
this.x = Math.floor((Math.random() * map.offsetHeight) / this.width) * this.width
this.y = Math.floor((Math.random() * map.offsetWidth) / this.height) * this.height
// 2.2创建一个div,让这个div拥有这个食物对象的所有显示信息
var div1 = document.createElement('div')
div1.style.width = this.width + 'px'
div1.style.height = this.height + 'px'
div1.style.backgroundColor = this.bgColor
div1.style.position = 'absolute'
div1.style.borderRadius = 20 + 'px'
div1.style.top = this.x + 'px'
div1.style.left = this.y + 'px'
// 2.3把这个div追加到地图中去
map.appendChild(div1)
// 把div保存起来
list.push(div1)
}
3.删除老食物div的方法
function removeFood(map){
for(var i = 0; i<list.length; i++){
map.removeChild(list[i]);
}
list = []
}
最终成果:
死亡界面:
1.撞墙而死
2.咬到自己身体而死
实验总结:
通过本次开发,熟悉了面向对象多类的开发、以及对于定时器的控制。由于缺乏开发经验,并且时间紧张。因此开发的不够完善,只能实现基本的游戏功能。对于样式以及玩法的多样性没有丰富起来
实验原码:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./Food.js"></script>
<script src="./Snake.js"></script>
<script src="./Game.js"></script>
</head>
<style>
* {
margin: 0;
padding: 0;
}
.map {
width: 1920px;
height: 930px;
/* background-color: #ccc; */
position: relative;
left: 0px;
top: 0px;
}
.btn{
position: absolute;
top: 400px;
left: 850px;
width: 80px;
height: 60px;
}
</style>
<body>
<div class="map" id="map"></div>
<button class="btn" onclick="reload()">重新开始</button>
<script>
// 1.获取地图
var map = document.getElementById('map')
// 2.创建一个Game控制对象
var game1 = new Game(map);
game1.start();
// 点击button,重新开始
function reload(){
window.location.reload();
}
</script>
</body>
</html>
Game.js:
// Ganme控制蛇和食物的生成,引入地图等等
(function (window) {
var that = null;
// 1.声明一个Game构造函数,生成这个对象
function Game (map) {
this.food1 = new Food()
this.snake1 = new Snake()
this.map = map
that = this
}
// 2.准备开始的方法
Game.prototype.start = function () {
// 2.1渲染食物和蛇
this.food1.render(this.map)
this.snake1.render(this.map)
// 2.2让蛇自动动起来,并且渲染
snakeAutoMove()
bindKey()
}
// 3.定时器让蛇动起来,定时器的this是window,因此需要用that引用一下
function snakeAutoMove () {
var timeId = setInterval(function () {
this.snake1.move(this.food1, this.map)
// 判断蛇移动是否出界,如果蛇头坐标小于0,或者大于宽/高,就出界了
var snakeHeadX = this.snake1.body[0].x * this.snake1.width;
var snakeHeadY = this.snake1.body[0].y * this.snake1.height;
if (snakeHeadX < 0 || snakeHeadY < 0 || snakeHeadX >= this.map.offsetHeight || snakeHeadY >= this.map.offsetWidth) {
// 出界就结束游戏
alert('Game Over')
clearInterval(timeId)
return;
}
// 吃到自己身体也死
if (this.snake1.body.length > 0) {
for (var i = 1; i < this.snake1.body.length; i++) {
if (this.snake1.body[0].x == this.snake1.body[i].x && this.snake1.body[0].y == this.snake1.body[i].y){
alert('咬蛇自尽')
clearInterval(timeId)
}
}
}
this.snake1.render(this.map);
}.bind(that), 100);
}
// 4.让蛇根据那键盘按键来移动
function bindKey () {
document.onkeydown = function (e) {
e = e || window.event;
switch (e.keyCode) {
case 37:
if (this.snake1.direction != 'right') {
this.snake1.direction = 'left';
}
break;
case 38:
if (this.snake1.direction != 'bottom') {
this.snake1.direction = 'top';
}
break;
case 39:
if (this.snake1.direction != 'left') {
this.snake1.direction = 'right';
}
break;
case 40:
if (this.snake1.direction != 'top') {
this.snake1.direction = 'bottom';
}
break;
}
}.bind(that);
}
// 暴露这个方法
window.Game = Game
}(window))
Food.js:
// Food类
(function (window) {
// 声明一个list存放食物
var list = []
// 1.创建食物对象的构造函数
function Food (width, height, bgColor, x, y) {
this.width = width || 30
this.height = height || 30
this.bgColor = bgColor || '#0061BC'
this.x = x || 0
this.y = y || 0
}
// 2.根据Food构造函数创建出来的食物对象,渲染到地图上
// 渲染方法写原型链中
Food.prototype.render = function (map) {
removeFood(map);
// 2.1给食物对象随机生成xy坐标
// 生成0-1的随机数,乘上地图宽/小方块宽,向下取整。就是对于整个地图来说的随机数,再乘上div宽,就可以随机生成在地图内的x
this.x = Math.floor((Math.random() * map.offsetHeight) / this.width) * this.width
this.y = Math.floor((Math.random() * map.offsetWidth) / this.height) * this.height
// 2.2创建一个div,让这个div拥有这个食物对象的所有显示信息
var div1 = document.createElement('div')
div1.style.width = this.width + 'px'
div1.style.height = this.height + 'px'
div1.style.backgroundColor = this.bgColor
div1.style.position = 'absolute'
div1.style.borderRadius = 20 + 'px'
div1.style.top = this.x + 'px'
div1.style.left = this.y + 'px'
// 2.3把这个div追加到地图中去
map.appendChild(div1)
// 把div保存起来
list.push(div1)
}
// 删除老食物div的方法
function removeFood(map){
for(var i = 0; i<list.length; i++){
map.removeChild(list[i]);
}
list = []
}
// 把Food构造函数暴露出去
window.Food = Food;
}(window));
Snake.js:
// 蛇类
(function (window) {
// 声明一个list保存蛇
var list = [];
// 1.声明一个构造函数,创建蛇对象
function Snake (width, height, direction) {
this.width = width || 30;
this.height = height || 30;
// 蛇开始默认向右走
this.direction = direction || 'right';
// 初始化创建出来的蛇,用数组来表示蛇身体,吃到了就添加一个
this.body = [
// 蛇的每一节身体都有不同的x,y
{ x: 1, y: 2, bgColor: '#3EBFA0'},
{ x: 1, y: 1, bgColor: '#8DE2C9'}
];
}
// 2.把创建出来的蛇对象渲染到地图上
Snake.prototype.render = function (map) {
// 渲染新蛇之前删除老蛇
remove(map)
// 把蛇的每一节遍历出来
for (var i = 0; i < this.body.length; i++) {
// 创建div
var div1 = document.createElement('div');
// 把当前蛇这一节身体所有的显示信息赋值给这个div
div1.style.width = this.width + 'px';
div1.style.height = this.height + 'px';
div1.style.backgroundColor = this.body[i].bgColor;
div1.style.position = 'absolute';
div1.style.borderRadius = 20 + 'px';
div1.style.top = this.body[i].x * this.width + 'px';
div1.style.left = this.body[i].y * this.height + 'px';
// 把div添加到map中
map.appendChild(div1);
list.push(div1)
}
}
// 3.蛇的移动
Snake.prototype.move = function (food, map) {
// 3.1蛇身的移动,从蛇尾开始改变,比较简单
for (var i = this.body.length - 1; i > 0; i--) {
this.body[i].x = this.body[i - 1].x;
this.body[i].y = this.body[i - 1].y;
}
// 3.2根据方向改变蛇头
switch (this.direction) {
case 'right':
this.body[0].y++;
break;
case 'left':
this.body[0].y--;
break;
case 'top':
this.body[0].x--;
break;
case 'bottom':
this.body[0].x++;
break;
}
// 判断蛇有没有吃到食物,蛇头是否与食物重叠
var snakeHeadX = this.body[0].x * this.width
var snakeHeadY = this.body[0].y * this.width
var foodX = food.x
var foodY = food.y
// 去除蛇尾的坐标备用
var lastSnakeUnit = this.body[this.body.length - 1];
// 判断
if (snakeHeadX == foodX & snakeHeadY == foodY) {
// 吃到了食物,长身体
this.body.push({
x: lastSnakeUnit.x,
y: lastSnakeUnit.y,
bgColor: '#7CDEC1'
})
// 产生新食物,即重新渲染
food.render(map)
}
}
// 4.声明一个方法,删除老蛇
function remove (map) {
for (var i = 0; i < list.length; i++) {
map.removeChild(list[i]);
}
// 删除list里真正的蛇
list = []
}
// 暴露蛇函数
window.Snake = Snake;
}(window));