原生JS实战:经典贪吃蛇(开局10倍速度,来看看你最高能得多少分!)
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5875523.html
该程序是本人的个人作品,写的不好,未经本人允许,请不要用于其它用途!
贪吃蛇的游戏相信80后的朋友小时候都玩过,记得我小时候还攒了二十多块钱买了个游戏机(一个礼拜2块的零花钱!),可以玩飞机、俄罗斯方块、贪吃蛇等,刚开始玩的真过瘾,无奈太费电池,玩不起,放一段时间居然屏幕不行了!哎!
点击查看演示:
苏福的作品:贪吃蛇
body{background-color: gray}
#main{
position: absolute;
left: 0;
right: 0;
bottom: 0;
margin: auto;
top: 40px;
width: 600px;
height: 400px;
background-color: #336699;
}
#game-window{
position: absolute;
left: 0;
top: 0;
width: 600px;
height: 400px;
overflow: hidden;
}
#begin-btn{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: 100px;
height: 40px;
line-height: 40px;
font-size: 20px;
background-color: powderblue;
text-align: center;
cursor: pointer;
}
.block{
position: absolute;
left: -10px;
margin: auto;
width: 10px;
height: 10px;
background-color: powderblue;
-webkit-box-shadow: inset 0 0 1px black;
-moz-box-shadow: inset 0 0 1px black;
box-shadow: inset 0 0 1px black;
}
#snake-head{
background-color: palegreen;
}
#snake-body{
background-color: palegreen;
}
#score-info{
position: absolute;
top: -25px;
width: 100%;
height: 25px;
line-height: 25px;
background-color: powderblue;
overflow: hidden;
}
.score-info-item{
float: left;
width: 145px;
height: 25px;
margin-left: 1px;
text-align: left;
white-space: nowrap;
overflow: hidden;
}
window.onload = function () {
//====公共函数
function id(id){return document.getElementById(id)}
function getRandom(max){return Math.floor((Math.random()*max)/10)*10}
function getElemXY(elem){
return{
x:parseInt(elem.style.left),
y:parseInt(elem.style.top)
}
}
function setElemXY(elem,x,y){
elem.style.left = x+'px';
elem.style.top = y+'px';
}
function clear() {
gameWindow.innerHTML = '';
scoreInfo.innerHTML = '成绩: 0';
speedInfo.innerHTML = '速度: 5';
levelInfo.innerHTML = '等级: 1';
maxScoreInfo.innerHTML = '最高成绩: '+maxScore;
}
//====公共变量
var beginBtn = id('begin-btn'),
scoreInfo = id('score'),
speedInfo = id('speed'),
levelInfo = id('level'),
maxScoreInfo = id('max-score'),
maxScore = 0,
block = document.querySelector('.block'),
timeId,
gameWindow = id('game-window');
//====游戏入口
beginBtn.onclick = function (){
clear();
beginBtn.style.display = 'none';
var s = new Snake();
s.moving();
};
//按下方向键,改变蛇头的移动方向
document.onkeydown = function (event){
var e = event||window.event;
var key = e.keyCode;
switch (key){
case 38:
if(Snake.headDirection.x === 0){return;} //判断是否是垂直移动
Snake.headDirection.y = 1;
Snake.headDirection.x = 0;
break;
case 40:
if(Snake.headDirection.x === 0){return;}
Snake.headDirection.y = -1;
Snake.headDirection.x = 0;
break;
case 37:
if(Snake.headDirection.y === 0){return;} //判断是否是水平移动
Snake.headDirection.x = 1;
Snake.headDirection.y = 0;
break;
case 39:
if(Snake.headDirection.y === 0){return;}
Snake.headDirection.x = -1;
Snake.headDirection.y = 0;
break;
}
};
//====Snake类
var Snake = function () {
this.score = 0;
this.speed = 10;
this.levle = 0;
this.snakeHead = this.createBlock('snake-head',block);
this.snake = [this.snakeHead]; //将蛇头作为蛇体的第一个元素
this.food = this.createBlock('food',block);
};
//蛇头的移动方向,x,y的取值为-1 0 1
Snake.headDirection = {x:1,y:0};
//创建蛇节
Snake.prototype.createBlock = function (id,block) {
var bk = block.cloneNode(false),x = getRandom(600),y = getRandom(400);
bk.id = id;
setElemXY(bk,x,y);
gameWindow.appendChild(bk);
return bk;
};
//移动蛇身,并判断是否吃到食物,或吃到自己,或撞墙
Snake.prototype.moving = function () {
//吃到食物
var head = getElemXY(this.snakeHead), food = getElemXY(this.food);
if(head.x === food.x && head.y === food.y){
this.eat(this.food);
this.food = this.createBlock('food',block);
}
//撞墙
var x = Snake.headDirection.x, y = Snake.headDirection.y;
if(x === 0){
y === 1 ? head.y-=10 : head.y+=10;
if(head.y400){this.gameOver();return;}
}
if(y === 0){
x === 1 ? head.x-=10 : head.x+=10;
if(head.x600){this.gameOver();return;}
}
var len = this.snake.length;
for(var i = len-1 ; i>0 ; i--){
var nowXY = getElemXY(this.snake[i]);
if(head.x === nowXY.x && head.y === nowXY.y){this.gameOver();return;} //吃到自己
var preXY = getElemXY(this.snake[i-1]);
setElemXY(this.snake[i],preXY.x,preXY.y); //将每个蛇节都移到它的前一个的位置
}
setElemXY(this.snakeHead,head.x,head.y); //舌头移动到新位置
timeId = setTimeout(function () {
this.moving();
}.bind(this),1000/this.speed);
};
//游戏结束
Snake.prototype.gameOver = function(){
beginBtn.style.display = 'block';
clearTimeout(timeId);
timeId = null;
};
//吃食物,并更新游戏成绩
Snake.prototype.eat = function (food) {
this.levle++;
this.speed+=0.4;
this.score+=5*this.speed;
levelInfo.innerHTML = '等级: '+this.levle;
speedInfo.innerHTML = '速度: '+this.speed.toFixed(2);
scoreInfo.innerHTML = '成绩: '+this.score;
maxScore = Math.max(maxScore,this.score);
maxScoreInfo.innerHTML = '最高成绩: '+maxScore;
var oldLast = getElemXY(this.snake[this.snake.length-1]);
setElemXY(food,oldLast.x,oldLast.y);
this.snake.push(food);
};
}
</script>
开始游戏
贪吃蛇没什么游戏规则,就是转转转、吃吃吃!我就定义了一个类Snake
,想用面向对象来写,不知道写的像不像!请前辈多多指点
//====Snake类
var Snake = function () {
this.score = 0;
this.speed = 10;
this.levle = 0;
this.snakeHead = this.createBlock('snake-head',block);
this.snake = [this.snakeHead]; //将蛇头作为蛇体的第一个元素
this.food = this.createBlock('food',block);
};
这个游戏的设计难点有这几个:
- 蛇的移动方向随方向键的改变
- 蛇身各个部分的移动
- 蛇身移动过程中不断的判断是否撞墙、是否吃到食物。
蛇的移动方向的改变我定义了一个静态属性Snake.headDirection = {x:1,y:0};
,用来保存当前状态,x、y的取值范围为1、0、-1,x和y不会同时为零,x为零时表示当前垂直移动,可以往左(y:1)或往右(y:-1),y为零时表示水平移动,可以往上(x:1)或往下(x:-1)。为此我把方向键的事件回调函数设计成类似状态机的函数:
document.onkeydown = function (event){
var e = event||window.event;
var key = e.keyCode;
switch (key){
case 38:
if(Snake.headDirection.x === 0){return;} //判断是否是垂直移动
Snake.headDirection.y = 1;
Snake.headDirection.x = 0;
break;
case 40:
if(Snake.headDirection.x === 0){return;}
Snake.headDirection.y = -1;
Snake.headDirection.x = 0;
break;
case 37:
if(Snake.headDirection.y === 0){return;} //判断是否是水平移动
Snake.headDirection.x = 1;
Snake.headDirection.y = 0;
break;
case 39:
if(Snake.headDirection.y === 0){return;}
Snake.headDirection.x = -1;
Snake.headDirection.y = 0;
break;
}
};
下面是创建蛇头或蛇身的共有函数,因为蛇头、蛇身都是由一样的方块组成,这里我预先在HTML文件里就创建了一个方块<span class="block"></span>
,通过css把它藏在标题栏的下面,然后以后碰到要创建蛇头、蛇身的时候就克隆一个并在给定随机位置后添加到游戏窗口,省的每次都运行创建元素的代码:具体看代码注释
Snake.prototype.createBlock = function (id,block) {
var bk = block.cloneNode(false),x = getRandom(600),y = getRandom(400);
bk.id = id;
setElemXY(bk,x,y); //该方法用来设置元素的lef、top
gameWindow.appendChild(bk);
return bk;
};
接下来是蛇的移动、吃食物、撞墙:当蛇头的位置等于食物的位置时,启动吃食物函数;当蛇头的下次移动的坐标在游戏窗口之外时,就判定撞墙,游戏结束;
//移动蛇身,并判断是否吃到食物,或吃到自己,或撞墙
Snake.prototype.moving = function () {
//吃到食物
var head = getElemXY(this.snakeHead), food = getElemXY(this.food);
if(head.x === food.x && head.y === food.y){
this.eat(this.food);
this.food = this.createBlock('food',block);
}
//移动、撞墙
var x = Snake.headDirection.x, y = Snake.headDirection.y;
//x等于0说明当前垂直移动,如果y等于1,说明在向左移动,所以head.y即元素的left-10px;y等于零时同理
if(x === 0){
y === 1 ? head.y-=10 : head.y+=10;
if(head.y<0||head.y>400){this.gameOver();return;}
}
if(y === 0){
x === 1 ? head.x-=10 : head.x+=10;
if(head.x<0||head.x>600){this.gameOver();return;}
}
var len = this.snake.length;
for(var i = len-1 ; i>0 ; i--){
var nowXY = getElemXY(this.snake[i]);
if(head.x === nowXY.x && head.y === nowXY.y){this.gameOver();return;} //吃到自己
var preXY = getElemXY(this.snake[i-1]);
setElemXY(this.snake[i],preXY.x,preXY.y); //将每个蛇节都移到它的前一个的位置
}
setElemXY(this.snakeHead,head.x,head.y); //舌头移动到新位置
timeId = setTimeout(function () {
this.moving();
//这里用bind绑定this,不然无法调用this.moving(),不支持bind的话只能外层加个匿名函数来传参了
}.bind(this),1000/this.speed);
};
下面是吃食物函数,这个函数比较简单,主要看后面几行代码,将食物的位置改为蛇身的最后一个节点的位置,并把它添加进蛇的数组,便于整体移动
//吃食物,并更新游戏成绩
Snake.prototype.eat = function (food) {
this.levle++;
this.speed+=0.4;
this.score+=5*this.speed;
levelInfo.innerHTML = '等级: '+this.levle;
speedInfo.innerHTML = '速度: '+this.speed.toFixed(2);
scoreInfo.innerHTML = '成绩: '+this.score;
maxScore = Math.max(maxScore,this.score);
maxScoreInfo.innerHTML = '最高成绩: '+maxScore;
var oldLast = getElemXY(this.snake[this.snake.length-1]);
setElemXY(food,oldLast.x,oldLast.y);
this.snake.push(food);
};
基本上就是以上这些代码了,其它还有几个简单的公共函数,就不说了,自己查看源码。
这里我要说的一点心得:给函数、变量命名的时候一定要语义化,一看就能大概知道这个函数、变量是干什么的,这样才不会自乱阵脚,更别提和别人合作了!(不要用拼音,你不觉得low的话也行,我是不懂就查翻译,多多少少也能提高点英语水平吧,呵呵,我英语水平也是很菜,惭愧!)
原生JS实战:经典贪吃蛇(开局10倍速度,来看看你最高能得多少分!)的更多相关文章
- 原生js写的贪吃蛇网页版游戏特效
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <bo ...
- canvas原生js写的贪吃蛇
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 20行JS代码实现贪吃蛇
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- JS高级---案例贪吃蛇,把封装的函数移动到js文件中
案例贪吃蛇,把封装的函数移动到js文件中 <!DOCTYPE html> <html lang="en"> <head> <meta ch ...
- 原生JS实战:分享一个首页进度加载动画!
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5871134.html 该程序是本人的个人作品,写的不好,可以参考,但未经 ...
- 用js写一个贪吃蛇小游戏
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 原生JS实战:写了个斗牛游戏,分享给大家一起玩!
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5869953.html 该程序是本人的个人作品,写的不好,未经本人允许,请 ...
- js面向对象案例 贪吃蛇
食物对象 (function () { //map:所在的父盒子,obj自身的一些属都具有默认值 function Food(map, obj) { obj = obj || {}; //没有则使用默 ...
- 原生JavaScript实现的贪吃蛇
github代码地址:https://github.com/McRayFE/snake 涉及到的知识点: 键盘事件 setInterval()定时器 javascript中数组的使用 碰撞的检测 of ...
随机推荐
- 【原创】开源.NET排列组合组件KwCombinatorics使用(一)—组合生成
本博客所有文章分类的总目录:本博客博文总目录-实时更新 本博客其他.NET开源项目文章目录:[目录]本博客其他.NET开源项目文章目录 KwCombinatorics组件文章目录: 1. ...
- Android获取内置sdcard跟外置sdcard路径
Android获取内置sdcard跟外置sdcard路径.(测试过两个手机,亲测可用) 1.先得到外置sdcard路径,这个接口是系统提供的标准接口. 2.得到上一级文件夹目录 3.得到该目录的所有文 ...
- 从零开始编写自己的C#框架(7)——需求分析
本章内容虽然叫“需求分析”,实际上关于具体的需求分析操作步骤并没有深入去写,因为细化的话那将是一本厚厚的书,而需求分析在本系列中,是帮助大家了解项目的基本要求(主要针对本项目而已).而写本章的主要目的 ...
- ES6 - Note2:解构赋值
ES6的解构赋值就是利用模式匹配从按照一定模式的数组或者对象中提取值赋值给变量. 1.数组的解构赋值 在ES6以前,变量的赋值是直接指定的,以后可以这么来写,如下所示 let [a,b,c] = [1 ...
- 使用Python和Perl绘制北京跑步地图
当你在一个城市,穿越大街小巷,跑步跑了几千公里之后,一个显而易见的想法是,如果能把在这个城市的所有路线全部画出来,会是怎样的景象呢? 文章代码比较多,为了不吊人胃口,先看看最终效果,上到北七家,下到南 ...
- iOS开发之各种动画各种页面切面效果
因工作原因,有段时间没发表博客了,今天就发表篇博客给大家带来一些干货,切勿错过哦.今天所介绍的主题是关于动画的,在之前的博客中也有用到动画的地方,今天就好好的总结一下iOS开发中常用的动画.说道动画其 ...
- C语言之通过冒泡排序浅谈编程思想
写这篇博文的目的是想起到抛砖引玉的作用,还请大牛们留下一些先进的思想,让小菜学习一下.下面入正题. 复习C语言怎么能少的了冒泡呢,记得刚学C语言那会,感觉冒泡排序真的太复杂了,理解不大了,嗯!还是当时 ...
- html规范
1 前言 HTML 作为描述网页结构的超文本标记语言,在百度一直有着广泛的应用.本文档的目标是使 HTML 代码风格保持一致,容易被理解和被维护. 2 代码风格 2.1 缩进与换行 [强制] 使用 4 ...
- EntityFramework 7 Linq Contains In 奇怪问题
这篇博文纪录一下:当使用 EF7,Linq 实现类似 where filename in('','','') SQL 代码,使用 Contains 出现报错问题. project.json 配置文件( ...
- spring源码分析之定时任务Scheduled注解
1. @Scheduled 可以将一个方法标识为可定时执行的.但必须指明cron(),fixedDelay(),或者fixedRate()属性. 注解的方法必须是无输入参数并返回空类型void的. @ ...