写这个小例子时在学习构造函数创建对象,所以元素都是以构造函数形式动态创建的。
1.创建地图
function map(){
//相关属性
this.className='map';
//构造地图对象
this._map=null;
this.createMap= function () {
if(this._map==null){
this._map=document.createElement('div');
this._map.className=this.className;
document.body.appendChild(this._map);
}
}
}
然后实例化对象为Map , Map=new map(); Map.createMap();
2.创建蛇
注意蛇的位置是相对于地图来绝对定位的
(1) 构造函数snack来创建Snack对象,在构造 函数中创建属性this.snackBody=[[4,1,null,'aqua'],[3,1,null,'#FF36FB'],[2,1,null,'#FF36FB']];
表示蛇的初始大小是由3个小div块构成的,二维数组中的行元素表示每个小的div块,例如[4,1,null,’aqua’]就表示在地图中这个小div块的大小相对于地图的位置是第四列第一行(行与列都是从0开始算的),颜色为aqua(蓝色);注意整个地图宽900px,高600px,每个小div都是宽30高30,
然后调用对象中的createSnack方法来为蛇创建身体
this.createSnack= function () {
for(var i=0;i<this.snackBody.length;i++){
if(this.snackBody[i][2]==null)
{//注意此处的[i][2]指的是上面数组snackBody中的null,添加这个判断是为了在定时器中调用时如果蛇身体存在即[i][2]不为null,时不再创建div
this.snackBody[i][2]=document.createElement('div');
this.snackBody[i][2].className=this.className;
this.snackBody[i][2].style.backgroundColor=this.snackBody[i][3];
Map._map.appendChild(this.snackBody[i][2]);
}
//蛇坐标移动
this.snackBody[i][2].style.left=this.snackBody[i][0]*30+"px";
this.snackBody[i][2].style.top=this.snackBody[i][1]*30+"px";
}
}
所以初始化的蛇在地图中的位置如图:
黄色部分为随机出的食物位置。
(2)创建好蛇如何使它动起来呢
timer=setInterval("Snack.move()", 1000);
在定时器中调用Snack.move()函数使得蛇动起来
在snack中的this.move()里面首先我们要让蛇为跟随蛇头的坐标走,如果蛇头在地图中的坐标为[4][1],那么蛇头后的第一个div蛇身它的下一个位置就是[4][1],以此类推,第二个div蛇身的下一位置就是第一个div蛇身的位置
//属性跟随,即后一节走前一节的位置
for(var i=this.snackBody.length-1;i>0;i--){
//刚开始蛇尾是第三节即i=2时,跟随第二节i=1的位置
this.snackBody[i][0]=this.snackBody[i-1][0];//行跟随
this.snackBody[i][1]=this.snackBody[i-1][1];//列跟随
}
现在蛇的每个小节实现了后一个走前一个的路,那么蛇头的位置怎么定
在sanck函数中写了一个新属性this.direct用来表示蛇头走的方向,
如果蛇头向左走那么this.snackBody[0][0] += 1;
即蛇头列坐标每次加1,所以蛇头的坐标改变如下:
//改变蛇头位置
switch (this.direct) {
case "right":this.snackBody[0][0] += 1;break;
case "left":this.snackBody[0][0] -= 1;break;
case "up":this.snackBody[0][1] -= 1;break;
case "down":this.snackBody[0][1] += 1;break;
}
左边改变不代表蛇头位置真正改变,还要通过改变它的left,top值来实现这个真正的位置移动,同理蛇身的坐标改变也要通过改变它的left,top值来实现这个真正的位置移动,因此我将下面代码写入了this.createSanck方法里
this.snackBody[i][2].style.left=this.snackBody[i][0]*30+"px";
this.snackBody[i][2].style.top=this.snackBody[i][1]*30+"px";
然后在this.move()方法里调用this.createSanck,createSanck完整代码见上面创建蛇(1)
在上面createSnack函数中已经写了蛇坐标移动的方法,就是每次定时器调用move函数时让蛇的left值增加30px就是一个小div的宽度,也就是使this.snackBody[i][0]*30+”px”;(注意此处i=0就是蛇头,其他为蛇身
),即如果蛇头的列数变为5了,this.snackBody[i][0]=5,那么乘以30后就是蛇头相对于地图的left值。同理可以得到top值即列数。就使蛇真正动了起来。
(3)用键盘方向键控制蛇行走的方向
在前面说过在sanck对象里创建了this.direct来记录方向,那么改变this.direct的值就可以实现了
//改变蛇头位置
switch (this.direct) {
case "right":this.snackBody[0][0] += 1;break;
case "left":this.snackBody[0][0] -= 1;break;
case "up":this.snackBody[0][1] -= 1;break;
case "down":this.snackBody[0][1] += 1;break;
}
添加keydown事件,获取方向键键码,改变this.direct
this.setdirection = function (code) {
//采用这种判断组织相反方向的掉头问题
if(code==37&&this.direct!='right'){this.direct = "left";}
if(code==38&&this.direct!='down'){this.direct = "up";}
if(code==39&&this.direct!='left'){this.direct = "right";}
if(code==40&&this.direct!='up'){this.direct = "down";}
}
这里的双重判断代表不能再两个相反的方向间掉头
3.随机食物位置及吃掉食物
(1)创建食物,做法类似于创建蛇,利用产生的随机数来随机食物的坐标
function food(){
this.className='food';
this._food=null;
this.x=0;
this.y=0;
this.createFood= function () {
//随机食物所在的行数和列数
this.x = Math.floor(Math.random() * 30);
this.y = Math.floor(Math.random() * 20);
if(this._food==null){
this._food=document.createElement('div');
this._food.className=this.className;
Map._map.appendChild(this._food);
}
//食物的位置
this._food.style.left=this.x*30+'px';
this._food.style.top=this.y*30+'px';
}
}
(2)食物创建好并且随机出现,那么蛇如何吃掉食物
在定时器中调用了sanck.move()方法,将吃食物的方法也放进move()中,吃掉食物的原理是:蛇头坐标等于食物坐标时,表示找到食物,蛇尾会走过食物的位置,让蛇尾的坐标入蛇身数组,即食物的位置入了数组,改变食物颜色为蛇身颜色,这样食物就成了新的蛇尾,视觉上就是吃掉了食物
if (this.snackBody[0][0] == Food.x && this.snackBody[0][1] == Food.y) {
this.snackBody.push(
[this.snackBody[this.snackBody.length - 1][0],
this.snackBody[this.snackBody.length - 1][1],
null,
"#FF36FB"//吃完后颜色变化
]
)
Food.createFood();
sortNumber+=10;//这是计分的变量
}
调用 Food.createFood()重新创建食物,snack.move()函数中会调用snack.createSnack()函数,因此新加入的小块的[i][2]为null,因此会创建div.
以上贪吃蛇最简单的功能已经完成,可以在下面添加如何判定游戏结束的附加功能
(1)蛇撞地图边界游戏结束
蛇头坐标越界就死,清除定时器,弹出游戏结束弹出框
if(this.snackBody[0][0] >= 30||this.snackBody[0][0] < 0||
this.snackBody[0][1] >= 20||this.snackBody[0][1] < 0) {
clearInterval(timer);
StartOver.text='游戏结束';
StartOver.alertDiv();
}
(2)蛇如何穿过地图边界继续游戏
当蛇头位置小于0时,要让蛇头出现在地图上不能写30,要写29,同理19
if (this.snackBody[0][0] >= 30) {
this.snackBody[0][0] = 0;
}
if (this.snackBody[0][0] < 0) {
this.snackBody[0][0] = 29;
}
if (this.snackBody[0][1] >= 20) {
this.snackBody[0][1] = 0;
}
if (this.snackBody[0][1] < 0) {
this.snackBody[0][1] = 19;
}
(2)开始结束按钮就是打开和清除定时器,不赘述,下面写重玩功能的实现
首先清除地图上的所有东西,包括蛇和食物,这样地图上就不显示蛇和食物了;然后删除已有蛇数组,重新创建新蛇数组,因为原来蛇数组中的[i][2]不为null,调用创建蛇的函数不能新建蛇,因此先删除再重新赋值;打开定时器;创建食物;将食物加到地图上
//重玩按钮
var again=document.createElement('div');
again.innerHTML='重玩';
again.className='again';
again.onclick= function () {
clearInterval(timer);
Map._map.innerHTML=null;
delete Snack.snackBody;
sortNumber=0;
modeTag=1;
StartOver.text='游戏开始';
StartOver.alertDiv();
Snack.snackBody=[[4,1,null,'aqua'],[3,1,null,'#FF36FB'],[2,1,null,'#FF36FB']];
timer=setInterval("Snack.move()", tim);
Food.createFood();
Map._map.appendChild(Food._food);
}
附上完整代码,写的比较繁琐
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>贪吃蛇</title>
<style>
*{
margin: 0;
padding: 0;
}
body,html{
width: 100%;
height: 100%;
background-image: url("img/12.png");
position: relative;
}
@font-face {
font-family: YiSu;
src: url('font/H.ttf');
}
.title{
font-size: 6em;
font-family: YiSu;
width: 100%;
height: 15%;
text-align: center;
color: white;
text-shadow:0px 0px 10px aqua,0px 0px 20px #52EEFF,0px 0px 30px #20DAFF,0px 0px 40px #12C9FF;
}
.map{
width: 900px;
height: 600px;
background-color: black;
position: relative;
margin: 0 auto;
box-shadow: 0px 0px 10px aqua,0px 0px 20px #52EEFF,0px 0px 30px #20DAFF,0px 0px 40px #12C9FF;
}
.snack{
width: 30px;
height: 30px;
position: absolute;
}
.food{
width: 30px;
height: 30px;
background-color: yellow;
position: absolute;
}
.sortBank{
font-family: YiSu;
font-size: 2em;
width: 200px;
height: 50px;
border: 2px solid black;
background-color:transparent;
color: #000;
border-radius: 10px;
position: absolute;
top: 20px;
left: 1px;
text-align: left;
}
.sort{
width: 50px;
height: 50px;
color: aqua;
background-color: transparent;
font-family: 微软雅黑;
font-size: 1em;
position: absolute;
top:0;
left: 120px;
}
.start,.end,.again,.box,.li1,.li2,.li3, .modeBank,.nanDiv,.nanBank,.nan1,.nan2,.nan3{
font-family: YiSu;
font-size: 2em;
width: 200px;
height: 50px;
border: 2px solid black;
background-color:transparent;
color: #000;
border-radius: 10px;
position: absolute;
text-align: left;
text-align: center;
}
.start{
top: 80px;
left: 1px;
}
.end{
top: 140px;
left: 1px;
}
.again{
top: 200px;
left: 1px;
}
.box{
top: 260px;
left: 1px;
}
.li1{
top:50px;display: none;
font-size: 32px;
}
.li2{
top:100px;display: none;font-size: 32px;
}
.li3{
top:150px;display: none;font-size: 32px;left: 1px;
}
.start:hover,.end:hover,.again:hover,.box:hover{
color: white;
text-shadow:0px 0px 10px aqua,0px 0px 20px #52EEFF,0px 0px 30px #20DAFF,0px 0px 40px #12C9FF;
}
.modeBank{
width: 630px;
height: 30px;
font-size: 20px;
top: 500px;
border: none;
text-align: left;
left: 1px;
}
.nanDiv{
right: 1px;
top:20px;
}
.nanDiv:hover, .nan1:hover, .nan2:hover, .nan3:hover{
color: aqua;
}
.nan1{
top:50px;display: none;
font-size: 32px;
}
.nan2{
top:100px;display: none;font-size: 32px;
}
.nan3{
top:150px;display: none;font-size: 32px;
}
.nanBank{
right: 1px;
top: 250px;
text-align: left;
border: none;
width: 320px;
height: 30px;
font-size: 20px;
}
.startOver{
font-size: 6em;
font-family: YiSu;
text-shadow:0px 0px 10px aqua,0px 0px 20px #52EEFF,0px 0px 30px #20DAFF,0px 0px 40px #12C9FF;
line-height: 300px;
text-align: center;
color: white;
width: 500px;
height: 300px;
background-image:url("img/12.png") ;
border-radius: 10px;
box-shadow: 0px 0px 10px aqua,0px 0px 20px #52EEFF,0px 0px 30px #20DAFF,0px 0px 40px #12C9FF;
position: absolute;
left: 50%;
top: 50%;
margin-top: -230px;
margin-left: -250px;
display: none;
}
</style>
</head>
<body>
</body>
<script>
var Map;
var Snack;
var timer;
var Food;
var sortNumber=0;/*分数值*/
var modeTag=2;/*/!*模式切换标*/
var nanTag=1;/*难度切换标志*/
var tim=300;/*定时器时间值*/
var StartOver;
var SOtext;/*开始结束提示信息*/
function map(){
//相关属性
this.className='map';
//构造地图对象
this._map=null;
this.createMap= function () {
if(this._map==null){
this._map=document.createElement('div');
this._map.className=this.className;
document.body.appendChild(this._map);
}
}
}
function snack(){
this.className='snack';
this.direct='right';
this.snackBody=[[4,1,null,'aqua'],[3,1,null,'#FF36FB'],[2,1,null,'#FF36FB']];
this.setdirection = function (code) {
//采用这种判断组织相反方向的掉头问题
if(code==37&&this.direct!='right'){this.direct = "left";}
if(code==38&&this.direct!='down'){this.direct = "up";}
if(code==39&&this.direct!='left'){this.direct = "right";}
if(code==40&&this.direct!='up'){this.direct = "down";}
}
this.move= function () {
//属性跟随,即后一节走前一节的位置
for(var i=this.snackBody.length-1;i>0;i--){
//刚开始蛇尾是第三节即i=2时,跟随第二节i=1的位置
this.snackBody[i][0]=this.snackBody[i-1][0];//行跟随
this.snackBody[i][1]=this.snackBody[i-1][1];//列跟随
}
//改变蛇头位置
switch (this.direct) {
case "right":this.snackBody[0][0] += 1;break;
case "left":this.snackBody[0][0] -= 1;break;
case "up":this.snackBody[0][1] -= 1;break;
case "down":this.snackBody[0][1] += 1;break;
}
//蛇撞自己
for (var j = 1; j < this.snackBody.length; j++) {
if (this.snackBody[0][0] == this.snackBody[j][0] && this.snackBody[0][1] == this.snackBody[j][1]) {
//计时器停止
clearInterval(timer);
StartOver.text='游戏结束';
StartOver.alertDiv();
}
}
switch(modeTag){
case 2:
//严格模式蛇撞边界死
if(this.snackBody[0][0] >= 30||this.snackBody[0][0] < 0||
this.snackBody[0][1] >= 20||this.snackBody[0][1] < 0) {
clearInterval(timer);
StartOver.text='游戏结束';
StartOver.alertDiv();
}
break;
case 1:
//宽松模式穿蛇墙走
if (this.snackBody[0][0] >= 30) {
this.snackBody[0][0] = 0;
}
if (this.snackBody[0][0] < 0) {
this.snackBody[0][0] = 29;
}
if (this.snackBody[0][1] >= 20) {
this.snackBody[0][1] = 0;
}
if (this.snackBody[0][1] < 0) {
this.snackBody[0][1] = 19;
}
break;
/* case 3://不实用存在很多bug
//自由模式自动吃
if (Food.x >= this.snackBody[0][0]) {
this.direct = "right";
if (Food.x == this.snackBody[0][0]) {
if (Food.y >= this.snackBody[0][1]) {
this.direct = "down";
}
else {
this.direct = "up";
}
}
}
else {
this.direct = "left";
if (Food.x == this.snackBody[0][0]) {
if (Food.y >= this.snackBody[0][1]) {
this.direct = "down";
}
else {
this.direct = "up";
}
}
}
break;*/
}
//蛇头找食物,如果蛇头和食物坐标相等,则蛇数组入栈一个最后走过的位置即食物位置,
if (this.snackBody[0][0] == Food.x && this.snackBody[0][1] == Food.y) {
this.snackBody.push(
[
this.snackBody[this.snackBody.length - 1][0],
this.snackBody[this.snackBody.length - 1][1],
null,
"#FF36FB"//吃完后颜色变化
]
)
Food.createFood();
sortNumber+=10;
}
//入栈后的食物位置有null值,调用createSnack()函数会给null创建div
Snack.createSnack();//注意此处由于已经有蛇所以不进方法的If,只改变left,top坐标
}
this.createSnack= function () {
for(var i=0;i<this.snackBody.length;i++){
if(this.snackBody[i][2]==null)
{//注意此处的[i][2]指的是上面数组中的null
this.snackBody[i][2]=document.createElement('div');
this.snackBody[i][2].className=this.className;
this.snackBody[i][2].style.backgroundColor=this.snackBody[i][3];
Map._map.appendChild(this.snackBody[i][2]);
}
//坐标移动
this.snackBody[i][2].style.left=this.snackBody[i][0]*30+"px";
this.snackBody[i][2].style.top=this.snackBody[i][1]*30+"px";
}
}
}
function food(){
this.className='food';
this._food=null;
this.x=0;
this.y=0;
this.createFood= function () {
//随机食物所在的行数和列数
this.x = Math.floor(Math.random() * 30);
this.y = Math.floor(Math.random() * 20);
if(this._food==null){
this._food=document.createElement('div');
this._food.className=this.className;
Map._map.appendChild(this._food);
}
//食物的位置
this._food.style.left=this.x*30+'px';
this._food.style.top=this.y*30+'px';
}
}
function startOver(){
this.className='startOver';
this._div=null;
this.text='游戏开始';
this.createAlert= function () {
if(this._div==null){
//游戏开始结束弹出框
startOver=document.createElement('div');
startOver.className=this.className;
document.body.appendChild(startOver);
}
}
this.alertDiv= function () {
startOver.innerHTML=this.text;//由于要更改弹出的text值,因此赋值放在这里
startOver.style.display='block';
setTimeout(function(){
startOver.style.display='none';
},500);
}
}
window.onload= function () {
Map=new map();
Map.createMap();
Snack=new snack();
Snack.createSnack();
Food=new food();
Food.createFood();
StartOver=new startOver();
StartOver.createAlert();
/* timer=setInterval("Snack.move()", 300);*/
document.onkeydown = function (event) {
Snack.setdirection(event.keyCode);
}
//游戏名称
var title=document.createElement('div');
title.innerHTML='小小贪吃蛇';
title.className='title';
document.body.appendChild(title);
//得分板
var sortBank=document.createElement('div');
sortBank.innerHTML='得分:';
sortBank.className='sortBank';
document.body.appendChild(sortBank);
//分数
var sort=document.createElement('div');
sort.innerHTML=sortNumber;
sort.className='sort';
sortBank.appendChild(sort);
//更新分数
setInterval(function () {
sort.innerHTML=sortNumber;
},1000);
//开始按钮
var start=document.createElement('div');
start.innerHTML='开始';
start.className='start';
start.onclick= function () {
clearInterval(timer);
StartOver.text='游戏开始';
StartOver.alertDiv();
timer=setInterval("Snack.move()", tim);
}
document.body.appendChild(start);
//结束按钮
var end=document.createElement('div');
end.innerHTML='结束';
end.className='end';
end.onclick= function () {
StartOver.text='游戏结束';
StartOver.alertDiv();
clearInterval(timer);
}
document.body.appendChild(end);
//重玩按钮
var again=document.createElement('div');
again.innerHTML='重玩';
again.className='again';
again.onclick= function () {
clearInterval(timer);
Map._map.innerHTML=null;
delete Snack.snackBody;
sortNumber=0;
modeTag=1;
StartOver.text='游戏开始';
StartOver.alertDiv();
Snack.snackBody=[[4,1,null,'aqua'],[3,1,null,'#FF36FB'],[2,1,null,'#FF36FB']];
timer=setInterval("Snack.move()", tim);
Food.createFood();
Map._map.appendChild(Food._food);
}
document.body.appendChild(again);
//模式切换
var box=document.createElement('div');
box.className='box';
box.innerHTML='模式切换';
document.body.appendChild(box);
var li1=document.createElement('li');
li1.className='li1';
li1.innerHTML='宽松模式';
box.appendChild(li1);
var li2=document.createElement('li');
li2.className='li2';
li2.innerHTML='严格模式';
box.appendChild(li2);
var li3=document.createElement('li');
li3.className='li3';
li3.innerHTML='自由模式';
box.appendChild(li3);
//模式切换的点击事件
box.onmouseenter= function () {
li1.style.display='block';
li2.style.display='block';
li3.style.display='block';
}
box.onmouseleave= function () {
li1.style.display='none';
li2.style.display='none';
li3.style.display='none';
}
//模式切换通知
var modeBank=document.createElement('div');
modeBank.innerHTML='默认宽松模式,现处于:';
modeBank.className='modeBank';
document.body.appendChild(modeBank);
//模式的对应事件
li1.onclick= function () {
modeBank.innerHTML='';
modeTag=1;//宽松模式
modeBank.innerHTML+='默认严格模式,现处于:宽松模式';
}
li2.onclick= function () {
modeBank.innerHTML='';
modeTag=2;//严格模式
modeBank.innerHTML+='默认严格模式,现处于:严格模式';
}
li3.onclick= function () {
modeBank.innerHTML='';
modeTag=3;//自由模式
modeBank.innerHTML+='默认严格模式,现处于:自由模式';
}
//难度选择
var nanDiv=document.createElement('div');
nanDiv.className='nanDiv';
nanDiv.innerHTML='难度切换';
document.body.appendChild(nanDiv);
var nan1=document.createElement('li');
nan1.className='nan1';
nan1.innerHTML='简单模式';
nanDiv.appendChild(nan1);
var nan2=document.createElement('li');
nan2.className='nan2';
nan2.innerHTML='中等模式';
nanDiv.appendChild(nan2);
var nan3=document.createElement('li');
nan3.className='nan3';
nan3.innerHTML='困难模式';
nanDiv.appendChild(nan3);
//难度切换的点击事件
nanDiv.onmouseenter= function () {
nan1.style.display='block';
nan2.style.display='block';
nan3.style.display='block';
}
nanDiv.onmouseleave= function () {
nan1.style.display='none';
nan2.style.display='none';
nan3.style.display='none';
}
//难度切换通知
var nanBank=document.createElement('div');
nanBank.innerHTML='难度:默认简单,现处于';
nanBank.className='nanBank';
document.body.appendChild(nanBank);
//难度的对应事件
nan1.onclick= function () {
nanBank.innerHTML='';
tim=300;
nanBank.innerHTML+='难度:默认简单,现处于:简单';
}
nan2.onclick= function () {
nanBank.innerHTML='';
tim=200;
nanBank.innerHTML+='难度:默认简单,现处于:中等';
}
nan3.onclick= function () {
nanBank.innerHTML='';
tim=100;
nanBank.innerHTML+='难度:默认简单,现处于:困难';
}
}
</script>
</html>