搭建页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="map" id="map"></div>
<!-- 引入多个js文件 -->
<script src="js/tools.js"></script>
<script src="js/food.js"></script>
<script src="js/snake.js"></script>
<script src="js/game.js"></script>
<script src="js/main.js"></script>
</body>
</html>
index.css
* {
margin:0;
padding:0;
}
.map {
position:relative;
width:800px;
height:600px;
background-color:lightgray;
}
分析对象
- 游戏对象
- 蛇对象
- 食物对象
食物对象
1.创建构造函数Food,设置属性
- x
- y
- width
- height
- color
- 通过原型设置方法
- render 随机创建一个食物对象,并且输出到map上
3.通过自调用函数,进行封装,通过window暴露Food对象
// 缩小定义的构造函数的作用域
// 匿名函数,自调用函数.IIFE,关住作用域
(function(){
var ps = "absolute";
// 创建食物的构造函数
function Food(option){
// 避免传入的参数的数据类型不对,或者没有传参
option = option instanceof Object ? option : {};
// 传入的数据可能是类似于数组等对象,需要进一步进行判断
this.width = option.width || 20;
this.height = option.height || 20;
this.x = option.x || 20;
this.y = option.y || 20;
this.color = option.color || "green";
// 增加一个属性,存储将来这个对象渲染出来的所有的div元素
this.elements = [];
}
// 渲染一个元素到页面之上,需要添加到原型对象的方法中
// 全局变量设置不变的属性,更加优化
Food.prototype.render = function(map){
// 创建一个新的div元素
var ele = document.createElement("div");
// 每次设置样式之前,随机获取一个x和y
this.x = Tools.getRandom(0,map.clientWidth / this.width - 1) * this.width;
this.y = Tools.getRandom(0,map.clientHeight / this.height - 1) * this.height;
// 给生成的元素添加对应的样式
ele.style.width = this.width + "px";
ele.style.height = this.height + "px";
ele.style.left = this.x + "px";
ele.style.top = this.y + "px";
ele.style.background = this.color;
ele.style.position = ps;
// 让新元素添加到指定的父级中
map.appendChild(ele);
// 将新元素添加到数组中,方便后期调用删除
this.elements.push(ele);
};
// 删除一个食物div元素的方法
Food.prototype.remove = function(map,i){
// 通过一些方法,获取删除食物的下标
//将元素从html结构中删除
map.removeChild(this.elements[i]);
// 将元素从数组中删除
this.elements.splice(i,1);
};
// 利用windows对象,暴露Food函数,可以给外部使用
window.Food = Food;
})();
// 测试
// 获取父级地图元素
var map = document.getElementById("map");
var food = new Food();
food.render(map);
// 在外面调用到这个Food构造函数
蛇对象
- 创建Snake构造函数,设置属性
1.width:蛇节宽度,默认20
2.height:蛇节高度,默认20
3.body:数组,蛇的头部和身体,第一个位置是蛇头
4.direction:蛇运动的方向,默认right,可以是left,bottom,top - 通过原型设置方法
- render:随机创建一个蛇对象,输出到map
- 通过自调用函数,封装,通过window暴露Snake
// 使用自调用函数关住作用域
(function(){
// 全局变量
ps = "absolute";
// 创建蛇的构造函数
function Snake(option){
// 避免传入的参数的数据类型不对,或者没有传参
option = option instanceof Object ? option : {};
// 给对象添加属性
//蛇节的宽高
this.width = option.width || 20;
this.height = option.height || 20;
// 设置蛇身数据
this.body = [
{x : 3,y: 2,color:"red"},
{x : 2,y: 2,color:"blue"},
{x : 1,y: 2,color:"blue"},
];
// 设置蛇的移动方向
this.direction = "right";
// 添加一个元素的数组,存储所有的渲染的元素
this.elements = [];
}
// 将元素渲染到页面上的方法
Snake.prototype.render = function(map){
// 生成对应个数的div元素
// 遍历body数组
for(var i =0,length = this.body.length; i < length;i++){
// 根据数组的每一项的数据生成一个新的div元素
var piece = this.body[i];
// 创建元素
var ele = document.createElement("div");
// 添加样式
ele.style.width = this.width + "px";
ele.style.height = this.height + "px";
ele.style.left = piece.x * this.width + "px";
ele.style.top = piece.y * this.height + "px";
ele.style.position = ps;
ele.style.background = piece.color;
// 渲染到指定的父级内部
map.appendChild(ele);
// 添加的新元素存到数组里
this.elements.push(ele);
}
};
// 添加蛇的运动方法
Snake.prototype.move = function(){
// 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;
}
// 存储蛇头的数据
var head = this.body[0];
// 2.蛇头要根据方向发生位置变化
switch(this.direction){
case "right":
head.x += 1;
break;
case "left":
head.x -= 1;
break;
case "top":
head.y -= 1;
break;
case "bottom":
head.y += 1;
}
};
// 删除上一次渲染的蛇的所有div元素
Snake.prototype.remove = function (map) {
// 遍历删除所有的元素
for(var i = this.elements.length-1; i >=0 ;i--){
map.removeChild(this.elements[i]);
}
// 数组也要清空
this.elements = [];
};
window.Snake = Snake;
})();
// 测试
// var map = document.getElementById("map");
// var snake = new Snake();
// snake.render(map);
游戏对象
- 创建 Game 的构造函数,并设置属性
1.food
2.snake
3.map - 通过原型设置方法
- start 开始游戏(绘制所有游戏对象,渲染食物对象和蛇对象)
- 通过自调用函数,进行封装,通过 window 暴露 Game 对象
// 自调用函数封闭作用域
(function(){
// 定义一个全局变量,存储this
var that;
function Game(map){
// 设置三个属性,蛇,食物,地图
this.food = new Food();
this.snake = new Snake();
this.map = map;
that = this;
}
// 添加一个游戏开始方法,方法内初始化蛇和食物
Game.prototype.start = function(){
// 添加蛇和食物到地图上
this.food.render(this.map);
this.food.render(this.map);
this.food.render(this.map);
this.snake.render(this.map);
// 让游戏逻辑开始
// 让蛇动起来
runSnake();
// 上下左右箭头控制蛇的运动方向
bindKey();
}
// 封装一个私有函数,这个函数只能在模块内部进行调用
function runSnake(){
// 开启一个定时器,让蛇连续运动
var timer = setInterval(function(){
// 定时器函数内部的this指向的是window,不能直接使用this。
that.snake.move();
// 删掉上一次的蛇
that.snake.remove(that.map);
// 渲染新的蛇
that.snake.render(that.map);
// 记录一下最大的位置
var maxX = that.map.offsetWidth / that.snake.width;
var maxY = that.map.offsetHeight / that.snake.height;
// 找到当前蛇头的位置
var headX = that.snake.body[0].x;
var headY = that.snake.body[0].y;
// 每一次这个蛇走到新的位置,都要判断一下是否吃到食物了,让自己增加一节
// 记录一下食物的坐标
// var foodX = that.food.x;
// var foodY = that.food.y;
// 获取一下蛇头的具体坐标值(px)
var hX = headX * that.snake.width;
var hY = headY * that.snake.height;
// 判断蛇头和食物是否重叠
// 将食物数组的每一个都要进行对比,谁被吃掉,删除自己,渲染一个新的元素
for(var i = 0;i<that.food.elements.length;i++){
if(that.food.elements[i].offsetLeft === hX && that.food.elements[i].offsetTop === hY){
// 吃到食物
// 让食物删除,渲染一个新的食物
that.food.remove(that.map,i);
that.food.render(that.map);
// 添加一个新的蛇节
var last = that.snake.body[that.snake.body.length - 1];
that.snake.body.push({
x: last.x,
y: last.y,
color: last.color
});
}
}
// 每次移动都要判断是否出了地图,游戏是否结束
// 进行判断
if(headX < 0 || headX >= maxX || headY < 0 || headY >= maxY){
// 停止定时器,弹出提醒,游戏结束
clearInterval(timer);
alert("Game Over");
}
},150)
};
// 封装一个私有函数控制上下左右按键更改的方向
function bindKey() {
// 给整个文档绑定键盘按下事件
document.onkeydown = function(e){
// console.log(e.keyCode);
// 键盘的编码
// 37是left,38是top,39是right,40是bottom
switch(e.keyCode){
case 37:
that.snake.direction = "left";
break;
case 38:
that.snake.direction = "top";
break;
case 39:
that.snake.direction = "right";
break;
case 40:
that.snake.direction = "bottom";
break;
}
};
}
// 将构造函数通过window暴露
window.Game = Game;
})();
// // 测试
// var map = document.getElementById("map");
// var game = new Game(map);
// game.start();
main主执行代码
项目结构main主执行代码,该文件引用必须要放在所有js文件引用之后
// 使用自调用函数,关住作用域
(function(){
var map = document.getElementById("map");
var game = new Game(map);
game.start();
})();
减少浏览器http请求
将所有的js代码整合到一个js文件里,然后就只发送一次请求,进行优化
自调用函数之后一定要加分号,不然会有程序错误
JS代码压缩
压缩之后代码运行速度会有提高,同时,压缩后的文件名一般为xxx.min.js
自调用函数的参数
- window
- undefined
两个参数
作用:1.window,将全局变为局部变量,避免了跳出本层作用域,将来进行代码压缩的时候,如果没有进行传参,速度会稍微减慢一点
作用:2.undefined,IE8及以下浏览器的undefined是可变的,会被更改
网友评论