大家小时候都玩过诺基亚里面的贪吃蛇吧,今天也来自己写个贪吃蛇小游戏。
- 首先呢。需要创建自己的地图,一个宽为800px,高为600px,定位在浏览器的左上角。
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>贪吃蛇</title>
<style>
#map {
width: 800px;
height: 600px;
background-color: #ccc;
position: relative;
top: 0px;
left: 0px;
}
</style>
</head>
<body>
<div id="map"></div>
</body>
</html>
- 我们现在需要创建一个食物,我们发现贪吃蛇里面的每一个食物都有宽,高,背景色,位置(x,y)坐标轴,用编程中面向对象的思维,可以视为食物就是一个对象,那就需要一个创建食物对象的构造函数。
// 如果Food函数未传值,那就默认为宽为20px,高为20px,背景色为绿色,(x,y)坐标都为0就是原点。
function Food(width = 20, height = 20, bgc = 'green', x = 0, y = 0) {
this.width = width;
this.height = height;
this.bgc = bgc;
this.x = x;
this.y = y;
}
- 那如何把这个食物对象显示在地图上呢?这时候需要一个显示食物对象的一个函数。
Food.prototype.render = function (map) {
//给食物对象随机坐标x, y
//offsetWidth = border + padding + height;
//Math.random()方法返回一个大于等于0小于1的随机数
//Math.floor()方法执行向下取整
this.x = Math.floor(Math.random() * map.offsetWidth / this.width) * this.width;
this.y = Math.floor(Math.random() * map.offsetHeight / this.height) * this.height;
//创建一个div,让这个div拥有这个食物对象的所有信息 (把食物对象的xy宽高背景色都赋值给这个div)
let box = document.createElement('div');
box.style.position = "absolute";
box.style.left = this.x + 'px';
box.style.top = this.y + 'px';
box.style.backgroundColor = this.bgc;
box.style.width = this.width + 'px';
box.style.height = this.height + 'px';
//把这个div添加到map地图中
map.appendChild(box);
}
4.然后在这个构造函数暴露出去。
window.Food = Food;
- 导入到html文件中,实例化Food构造函数
let food = new Food();
food.render(map);
这个时候我们的随机食物就已经做出来了,接下来去写蛇这个对象
- 蛇有什么特点呢?蛇和食物一样是有宽,高,背景色,还会移动,移动的方向,(x,y)坐标轴,蛇也是一个对象
function Snake(width = 20, height = 20, direction = 'right') {
this.width = width;
this.height = height;
this.direction = direction;
//这个body就是描述蛇的每一节的
this.body = [
{ x: 3, y: 1, bgc: 'red' },
{ x: 2, y: 1, bgc: 'pink' },
{ x: 1, y: 1, bgc: 'green' }
]
}
2.把创建出来的蛇显示在地图上
Snake.prototype.render = function (map) {
//遍历蛇一节一节的显示
for (let i = 0; i < this.body.length; i++) {
let item = this.body[i];
let box = document.createElement('div');
box.style.position = 'absolute';
box.style.left = item.x * this.width + 'px';
box.style.top = item.y * this.height + 'px';
box.style.width = this.width + 'px';
box.style.height = this.height + 'px';
box.style.backgroundColor = item.bgc;
//把box添加到map中
map.appendChild(box);
}
}
3.让蛇动起来吃东西
Snake.prototype.move = function (food, map) {
//除了蛇头之外的蛇身边的坐标改变
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;
}
//蛇头的坐标改变
switch (this.direction) {
case 'right':
this.body[0].x++;
break;
case 'left':
this.body[0].x--;
break;
case 'top':
this.body[0].y--;
break;
case 'bottom':
this.body[0].y++;
break;
}
//判断蛇是否吃到了食物.
//如果蛇头的坐标与食物的坐标重叠,那就说明蛇已经吃到了食物
var snakeHeadX = this.body[0].x * this.width; //蛇头的x坐标.
var snakeHeadY = this.body[0].y * this.height; //蛇头的y坐标
var foodX = food.x;//食物的x坐标
var foodY = food.y;//食物的y坐标.
//先拿到蛇尾巴(方便取他的xy)
var lastSnakeUnit = this.body[this.body.length - 1];
//判断
if (snakeHeadX == foodX && snakeHeadY == foodY) {
//吃到了食物就要长身体.
this.body.push({
x: lastSnakeUnit.x,
y: lastSnakeUnit.y,
bgColor: getRandomColor()
});
//吃了食物要产生一个新的食物.
food.render(map);
}
}
4.蛇每次吃到一个食物,那么蛇就会加一个box,这个box是随机颜色,我们需要写一个随机颜色的函数
//随机生成16进制的颜色函数
function getRandomColor() {
let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; //下标0-15
let str = '#';
//循环产生 6个 0-15的数.
for (let i = 0; i < 6; i++) {
let num = Math.floor(Math.random() * 16);
//根据这个随机数,在arr数组中去取值.
str += num;
}
return str;
}
5.把这个蛇的构造函数暴露出去
window.Snake = Snake;
- 这个时候食物和蛇都有在地图上显示了,但是蛇还不能动,现在我们需要写一个让蛇不停的动起来的方法,本质就是用计时器不停的调用蛇的move和render方法。
function snakeAutoMove() {
var timerId = setInterval(function () {
//判断蛇移动后有没有出界.
//思路:蛇头的坐标小于0,或者大于宽高就出界.
var snakeHeadX = this.snake.body[0].x * this.snake.width;//蛇头x坐标
var snakeHeadY = this.snake.body[0].y * this.snake.height;//蛇头y坐标
if (snakeHeadX < 0 || snakeHeadY < 0 || snakeHeadX >= this.map.offsetWidth || snakeHeadY >= this.map.offsetHeight) {
alert('Game Over!');
clearInterval(timerId);
return;//判断蛇出界了,那就提前结束这个方法,就不要让他往下执行这个渲染.
}
this.snake.render(this.map);
}.bind(that), 400);
}
- 现在蛇能动了,现在需要控制蛇的运动方向,让蛇根据键盘按键来移动。
function bindKey() {
document.onkeydown = function (e) {
e = e || window.event;
//console.log(e.keyCode);//左37 上38 右39 下40
switch (e.keyCode) {
case 37:
if (this.snake.direction != 'right') {
this.snake.direction = 'left';
}
break;
case 38:
if (this.snake.direction != 'bottom') {
this.snake.direction = 'top';
}
break;
case 39:
if (this.snake.direction != 'left') {
this.snake.direction = 'right';
}
break;
case 40:
if (this.snake.direction != 'top') {
this.snake.direction = 'bottom';
}
break;
}
}.bind(that);
}
完整代码
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>贪吃蛇</title>
</head>
<script src="index.js"></script>
<body>
<div id="map"></div>
</body>
</html>
<style>
#map {
position: relative;
left: 0;
top: 0;
width: 800px;
height: 600px;
background-color: #ccc;
}
</style>
<script>
//获取地图对象
let map = document.querySelector("#map");
//实例化一个游戏控制器对象.
var game1 = new Game(map);
//调用游戏控制器的start方法让游戏开始.
game1.start();
</script>
index.js
;(function (window) {
//声明一个数组,用来保存食物
var list = [];
//1.经过分析,发现食物有宽/高/背景色/定位的xy坐标,所以食物是一个对象,那就有一个创建食物对象的构造函数.
function Food(width,height,bgColor,x,y){
this.width = width || 20;
this.height = height || 20;
this.bgColor = bgColor || "green";
this.x = x || 0;
this.y = y || 0;
}
Food.prototype.render = function (map) {
//渲染新食物之前删除老食物div.
remove(map);
//2.1 给食物对象随机产生xy坐标
this.x = Math.floor(Math.random()*map.offsetWidth/this.width) * this.width;
this.y = Math.floor(Math.random()*map.offsetHeight/this.height)*this.height;
//2.2 创建一个div,让这个div拥有这个食物对象的所有显示信息.(把食物对象的xy宽高背景色都赋值给div的样式)
var div1 = document.createElement('div');
div1.style.position = 'absolute';
div1.style.left = this.x + "px";
div1.style.top = this.y + 'px';
div1.style.width = this.width + "px";
div1.style.height = this.height + 'px';
div1.style.backgroundColor = this.bgColor;
//2.3 把这个div添加到map地图中.
map.appendChild(div1);
//2.4 把渲染食物的这个div用list数组存起来
list.push(div1);
}
//删除老食物div的方法.
function remove(map){
for(var i = 0 ; i < list.length; i++){
map.removeChild(list[i]);
}
list = []; //清空list
}
//3.把Food这个构造函数给暴露出去.
window.Food = Food;
}(window));
//----------------------------------------------------------------------------------
;(function (window) {
//随机产生一个十六进制的颜色的函数.
function getColorForRandom(){
var arr = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']; //下标0-15
var str = "#";
//循环产生 6个 0-15的数.
for(var i = 0 ; i < 6; i++){
var num = Math.floor(Math.random()*16);
//根据这个随机数,在arr数组中去取值.
str += arr[num];
}
return str; //"#98de00"
}
//写蛇
//声明一个数组list用来保存蛇.
var list = [];
//1.分析蛇也是有宽/高/背景色/移动的方向/定位xy坐标,蛇也是对象.
function Snake(width,height,direction){
this.width = width || 20;
this.height = height || 20;
this.direction = direction || 'right';
//这个body就是描述蛇的每一节的.
this.body = [
{x:3,y:1,bgColor:'red'},
{x:2,y:1,bgColor:'green'},
{x:1,y:1,bgColor:'pink'}
];
}
//2.把创建出来的蛇对象显示在map地图上.
Snake.prototype.render = function (map) {
//渲染新蛇的时候删除老蛇(调用remove方法)
remove(map);
//2.1 遍历蛇一节一节的显示.
for(var i = 0 ; i < this.body.length; i++){
//2.2 创建div
var div1 = document.createElement('div');
//2.3 让这个div拥有这个蛇节的每一个显示信息
div1.style.position = 'absolute';
div1.style.top = this.body[i].y * this.height + 'px';
div1.style.left = this.body[i].x * this.width + 'px';
div1.style.width = this.width + 'px';
div1.style.height = this.height + 'px';
div1.style.backgroundColor = this.body[i].bgColor;
//2.4 把div1添加到map中.
map.appendChild(div1);
//把渲染蛇用的div存进list数组中.
list.push(div1);
}
}
//4.删除老蛇的方法.
function remove(map){
//从map中移除老蛇div.
for(var i = 0 ; i < list.length; i++){
map.removeChild(list[i]);
}
//list数组清空.
list.length = 0; // list = [];
}
//3.让蛇移动的方法写在蛇的原型中.
Snake.prototype.move = function (food,map) {
//除了蛇头之外的蛇身体的坐标改变
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;
}
//蛇头的坐标改变
switch (this.direction){
case 'right':
this.body[0].x++;
break;
case 'left':
this.body[0].x--;
break;
case 'top':
this.body[0].y--;
break;
case 'bottom':
this.body[0].y++;
break;
}
//判断蛇是否吃到了食物.
//思路:蛇头的坐标和食物的坐标重合.
var snakeHeadX = this.body[0].x * this.width; //蛇头的x坐标.
var snakeHeadY = this.body[0].y * this.height; //蛇头的y坐标
var foodX = food.x;//食物的x坐标
var foodY = food.y;//食物的y坐标.
//先拿到蛇尾巴(方便取他的xy)
var lastSnakeUnit = this.body[this.body.length-1];
//判断
if(snakeHeadX == foodX && snakeHeadY == foodY){
//吃到了食物就要长身体.
this.body.push({
x:lastSnakeUnit.x,
y:lastSnakeUnit.y,
bgColor:getColorForRandom()
});
//吃了食物要产生一个新的食物.
food.render(map);
}
}
//3.把Snake这个构造函数给暴露出去
window.Snake = Snake;
}(window));
//------------------------------------------------------------------------
;(function (window) {
//声明一个变量that,一开始赋值null
var that = null;
//1.创建游戏控制器对象的构造函数.
function Game(map){
//游戏控制器对象,里面有食物对象,食物对象是调用食物的构造函数创建出来的.
this.food = new Food();
//游戏控制器对象,里面有蛇对象,蛇对象是调用蛇的构造函数创建出来的.
this.snake = new Snake();
//游戏控制对象,里面有地图,地图是传进来的.
this.map = map;
//给that赋值.
that = this;
}
//2.游戏开始的方法.
Game.prototype.start = function () {
//2.1 显示食物对象.
this.food.render(this.map);
//2.2 显示蛇对象
this.snake.render(this.map);
//2.3 让蛇动一下,调用蛇对象的move方法改坐标,调用蛇的render方法重新渲染.
//this.snake.move();
//this.snake.render(this.map);
snakeAutoMove();
//2.4 调用绑定按键方法.
bindKey();
}
//3.写一个方法让蛇不停的动起来.本质就是用一个计时器不停的调用蛇的move和render方法.
function snakeAutoMove(){
var timerId = setInterval(function () {
//需求:把此时这个this变成游戏控制器对象
this.snake.move(this.food,this.map);
//判断蛇移动后有没有出界.
//思路:蛇头的坐标小于0,或者大于宽高就出界.
var snakeHeadX = this.snake.body[0].x *this.snake.width;//蛇头x坐标
var snakeHeadY = this.snake.body[0].y *this.snake.height;//蛇头y坐标
if(snakeHeadX < 0 || snakeHeadY < 0 || snakeHeadX >= this.map.offsetWidth || snakeHeadY >= this.map.offsetHeight){
alert('Game Over!');
clearInterval(timerId);
return;//判断蛇出界了,那就提前结束这个方法,就不要让他往下执行这个渲染.
}
this.snake.render(this.map);
}.bind(that),400);
}
//4.绑定键盘按键方法,让蛇根据键盘按键来移动.
function bindKey(){
document.onkeydown = function (e) {
e = e || window.event;
//console.log(e.keyCode);//左37 上38 右39 下40
switch(e.keyCode){
case 37:
if(this.snake.direction != 'right'){
this.snake.direction = 'left';
}
break;
case 38:
if(this.snake.direction != 'bottom'){
this.snake.direction = 'top';
}
break;
case 39:
if(this.snake.direction != 'left'){
this.snake.direction = 'right';
}
break;
case 40:
if(this.snake.direction != 'top'){
this.snake.direction = 'bottom';
}
break;
}
}.bind(that);
}
//3.把Game构造函数给暴露出去.
window.Game = Game;
}(window));
网友评论