用JavaScript写的贪吃蛇游戏(很简单,很详细)

我们两清 提交于 2021-02-08 09:37:17

效果

编写外部框架

<!DOCTYPE html>
<html>
	<head lang="en">
		<meta charset="UTF-8">
		<title></title>
		<style>
*{
    margin: 0;
    padding: 0;
}
#box{
	width:620px;
	height:450px;
	position:absolute;
	margin:0 auto;
	left:0;
	top:20px;
	right:0;
	bottom:0;
	background:gray;
	border-radius:10px;
}
#snakeDiv{
	width:600px;
	height:400px;
	position:absolute;
	margin:0 auto;
	left:0;
	top:10px;
	right:0;
	bottom:0;
}
.bottom{
	width:600px;
    height:30px;
	position:absolute;
	bottom:1px;
	right:1px;
}
.bottom .button1{
	position: absolute;
    right: 90px;
    width: 50px;
    font-size: 14px;
}
.bottom .button2{
	position: absolute;
    right: 30px;
    width: 50px;
    font-size: 14px;
}
.bottom .span1{
	position: absolute;
    left: 10px;
    color: white;
}
.bottom .span2{
	position: absolute;
    left: 200px;
    color: white;
}
</style>
	</head>
	<body>
		<div id='box'>
			<div id='snakeDiv'>
			</div>
			<div class='bottom'>
				<span id='score' class='span1'>分数:0</span>
				<span id='time' class='span2'>时间:0</span>
				<button onclick='start()' class='button1'>开始</button>
				<button onclick='stop()' class='button2'>结束</button>
			</div>
		</div>
		
	<script type="text/javascript" src='snake.js'></script>
	
	</body>
</html>

效果:

添加内部画布,以及绘制地图

首先创建线的构造函数Line

function Line(ctx,o){
		this.x=0,//x坐标
		this.y=0,//y坐标
		this.startX=0,//开始点x位置
		this.startY=0, //开始点y位置
		this.endX=0,//结束点x位置
		this.endY=0;//结束点y位置
		this.thin=false;//设置变细系数
		this.ctx=ctx;
		
		this.init(o);
	}
	Line.prototype.init=function(o){
		for(var key in o){
			this[key]=o[key];
		}
	}
	Line.prototype.render=function(){
		innerRender(this);
		
		function innerRender(obj){
			var ctx=obj.ctx;
			ctx.save()
			ctx.beginPath();
			ctx.translate(obj.x,obj.y);
			if(obj.thin){
				ctx.translate(0.5,0.5);
			}
			if(obj.lineWidth){//设定线宽
				ctx.lineWidth=obj.lineWidth;
			}
			if(obj.strokeStyle){
				ctx.strokeStyle=obj.strokeStyle;
			}
			//划线
		  	ctx.moveTo(obj.startX, obj.startY);
		  	ctx.lineTo(obj.endX, obj.endY);
		  	ctx.stroke();
		  	ctx.restore();
		}
	  	
	  	return this;
	}

设定参数、执行绘制等相关方法

function SnakeProxy(el){
		this.renderArr=[];//待渲染对象存储数组
		this.snakeDir=4;//蛇行走方向
		this.snakeArr=[];//用来存蛇头、蛇身、蛇尾的数组
		this.snakePosArr=[];//存蛇头、蛇身、蛇尾分别对应的位置
		this.score=0;//分数
		this.time=0;//时间
		this.moveCount=1;//计时控制器
	}
	
	SnakeProxy.prototype.init=function(el,score,time){
		if(!el) return ;
		this.el=el;
		this.scoreEL=score;
		this.timeEL=time;
		var canvas = document.createElement('canvas');//创建画布
		canvas.style.cssText="background:darkgrey;border:1px solid grey;border-radius:10px;";//设置样式
		var W = canvas.width = 600; //设置宽度
		var H = canvas.height = 400;//设置高度
		
		el.appendChild(canvas);//添加到指定的dom对象中
		
		this.ctx = canvas.getContext('2d');
		this.canvas=canvas;
		this.w=W;
		this.h=H;
		
		this.disX=20;//每个格子的x方向大小
		this.disY=20;//每个格子的y方向大小
		this.maxX=30;//x方向格子总数
		this.maxY=20;//y方向格子总数
		this.draw();//绘制
	}
	
	SnakeProxy.prototype.createMap=function(){
		var renderArr = this.renderArr;
		var disX = this.disX;
		var disY = this.disY;
		var maxX=this.maxX;
		var maxY=this.maxY;
		var rectW = this.w;
		var rectH = this.h;
		var rect=null;
		var color;
		
		for(var i=1;i<maxY;i++){//20行
			var line = new Line(this.ctx,{
				x:0,
				y:0,
			 	startX:0,
			 	startY:i*disY,
			 	endX:this.w,
			 	endY:i*disY,
			 	thin:true,
			 	strokeStyle:'white',
			 	lineWidth:0.2
			})
			renderArr.push(line);
		}
		
		for(var i=1;i<maxX;i++){//30列
			var line = new Line(this.ctx,{
				x:0,
				y:0,
			 	startX:i*disX,
			 	startY:0,
			 	endX:i*disX,
			 	endY:this.h,
			 	thin:true,
			 	strokeStyle:'white',
			 	lineWidth:0.2
			})
			renderArr.push(line);
		}
	}
	
	SnakeProxy.prototype.draw=function(){
		this.createMap();//绘制地图

		this.render();//渲染
	}
	

此时游戏区域的格子以及绘制如下:

再来绘制蛇和食物(这里都是以圆形来做的)

创建Ball构造函数

//构造函数
	function Ball(o){
		this.x=0,//圆心X坐标
		this.y=0,//圆心Y坐标
		this.r=0,//半径
		this.startAngle=0,//开始角度
		this.endAngle=0,//结束角度
		this.anticlockwise=false;//顺时针,逆时针方向指定
		this.stroke=false;//是否描边
		this.fill=false;//是否填充
		this.scaleX=1;//缩放X比例
		this.scaleY=1;//缩放Y比例
		
		this.init(o);
	}
	//初始化
	Ball.prototype.init=function(o){
		for(var key in o){
			this[key]=o[key];
		}
	}
	//绘制
	Ball.prototype.render=function(context){
		var ctx=context;
		ctx.save();
		ctx.beginPath();
		ctx.translate(this.x,this.y);
		ctx.scale(this.scaleX,this.scaleY);//设定缩放
		ctx.arc(0,0,this.r,this.startAngle,this.endAngle);//画圆
		if(this.lineWidth){//线宽
			ctx.lineWidth=this.lineWidth;
		}
		if(this.fill){//是否填充
			this.fillStyle?(ctx.fillStyle=this.fillStyle):null;
			ctx.fill();
		}
		if(this.stroke){//是否描边
			this.strokeStyle?(ctx.strokeStyle=this.strokeStyle):null;
			ctx.stroke();
		}	
		ctx.restore();
		
		return this;
	}

绘制蛇头

SnakeProxy.prototype.createHead=function(){
		var renderArr = this.renderArr;
		var disX = this.disX;
		var disY = this.disY;
		var x=1,y=0;
		var head = new Ball({
		 	x:x*disX+disX/2,
			y:y*disY+disY/2,
			r:disX/2-2,
			startAngle:0,
			endAngle:2*Math.PI,
			fill:true,
			fillStyle:'#F5DC10',
			lineWidth:1.2
		 })
		 renderArr.push(head);
		 this.snakeArr.push(head);
		 this.snakePosArr.push({x:x,y:y});
	}

蛇身(先不绘制出来,先放代码)

	
	SnakeProxy.prototype.createBody=function(){
		var renderArr = this.renderArr;
		var disX = this.disX;
		var disY = this.disY;
		var x=1,y=0;
		var body = new Ball({
		 	x:x*disX+disX/2,
			y:y*disY+disY/2,
			r:disX/3,
			startAngle:0,
			endAngle:2*Math.PI,
			fill:true,
			fillStyle:'#F5DC10',
			lineWidth:1.2
		 })
		 renderArr.push(body);
		 this.snakeArr.splice(1,0,body);//在头部后面添加
	}
	

绘制蛇尾

SnakeProxy.prototype.createEnd=function(){
		var renderArr = this.renderArr;
		var disX = this.disX;
		var disY = this.disY;
		
		var x=0,y=0;
		var end = new Ball({
		 	x:x*disX+disX/2,
			y:y*disY+disY/2,
			r:disX/4,
			startAngle:0,
			endAngle:2*Math.PI,
			fill:true,
			fillStyle:'#F5DC10',
			lineWidth:1.2
		 })
		 renderArr.push(end);
		 this.snakeArr.push(end);
		 this.snakePosArr.push({x:x,y:y});
	}

绘制食物(蛋)

SnakeProxy.prototype.createEgg=function(){
		var renderArr = this.renderArr;
		var disX = this.disX;
		var disY = this.disY;
		
		var x=_.getRandom(0,30),y=_.getRandom(0,20);
		
		var egg = new Ball({
		 	x:x*disX+disX/2,
			y:y*disY+disY/2,
			r:disX/2-2,
			startAngle:0,
			endAngle:2*Math.PI,
			scaleY:0.8,
			fill:true,
			fillStyle:'#FCF6DB',
			lineWidth:1.2
		 })
		 renderArr.push(egg);
		 this.egg=egg;
		 this.eggPos={x:x,y:y};
		 
		 var that=this;
		 egg.update=function(){//update方法,蛋被吃以后,新随机出现在地图中
		 	var x=_.getRandom(0,30),y=_.getRandom(0,20);
		 	this.x=x*disX+disX/2,this.y=y*disY+disY/2;
		 	that.eggPos={x:x,y:y};
		 }
	}

蛋加多了一个方法,用来更新位置(当被蛇吃了后)

给开始、结束按钮加入事件

var snakeDiv = document.getElementById('snakeDiv');
	var score= document.getElementById('score');
	var time= document.getElementById('time');
	
	snake.init(snakeDiv,score,time);
	
	function start(){
		snake.start()
	}
	
	function stop(){
		snake.stop()
	}
	SnakeProxy.prototype.start=function(){
		if(this.timmer) return ;
		if(this.hasEnd){//如果是结束则需要重新开始,暂停的话就继续游戏
			this.restart();
		}
		this.hasEnd=false;
		this.timmer = setInterval(this.move.bind(this),250);//开始定时任务
	}
		
	SnakeProxy.prototype.stop=function(){
		if(!this.timmer) return ;
		clearInterval(this.timmer);//清除定时任务
		this.timmer=null;
	}

此时的效果如下:

加入方向控制

SnakeProxy.prototype.control=function(){
		var that=this;
		global.addEventListener('keydown',function(e){
			switch (e.keyCode){
				case 38:
					if(that.snakeDir==1||that.snakeDir==2){
						break;
					}
					that.snakeDir=1;//上
					break;
				case 40:
					if(that.snakeDir==1||that.snakeDir==2){
						break;
					}
					that.snakeDir=2;//下
					break;
				case 37:
					if(that.snakeDir==3||that.snakeDir==4){
						break;
					}
					that.snakeDir=3;//左
					break;
				case 39:
					if(that.snakeDir==3||that.snakeDir==4){
						break;
					}
					that.snakeDir=4;//右
					break;
			
			}
			console.log(that.snakeDir)
		});
	}

加入蛇的下一个坐标更新

SnakeProxy.prototype.update=function(){
		var posArr = this.snakePosArr,pos;
		var disX=this.disX,disY=this.disY;
		_.each(this.snakeArr,function(s,i){
			pos = posArr[i];
			s.x=pos.x*disX+disX/2;
			s.y=pos.y*disY+disY/2;
		})
	}
	
	SnakeProxy.prototype.computedNext=function(obj){
		var x =obj.x, y = obj.y;
		switch (this.snakeDir){
			case 1:
				y=obj.y-1;
				break;
			case 2:
				y=obj.y+1;
				break;
			case 3:
				x=obj.x-1;
				break;
			case 4:
				x=obj.x+1;
				break;
		}
		return {x:x,y:y}
	}

再加上吃食物、咬到自己、计算时间、计算分数、定时move函数就完成了

SnakeProxy.prototype.eat=function(head){
		var pos = this.eggPos;
		if(head.x===pos.x && head.y===pos.y){//如果头部与蛋整体重合的话就吃
			this.egg.update();//重新随机蛋的位置
			//添加蛇的身体
			this.createBody();
			
			this.calcuScore();//计算分数
			return true;
		}
		return false;
	}
	SnakeProxy.prototype.eatSelf=function(head){
		var snakePosArr=this.snakePosArr;
		for(var i=1;i<snakePosArr.length;i++){
			var s = snakePosArr[i];
			if(head.x==s.x && head.y==s.y){
				return true;
			}
		}
		return false;
	}
	SnakeProxy.prototype.end=function(head,oldHead){
		if(head.x>=this.maxX || head.x<0 || head.y>=this.maxY || head.y<0 ){//超出边界结束
			alert('结束了')
			return true;
		}
		if(this.eatSelf(oldHead)){//触碰到自己也结束
			alert('结束了1')
			return true;
		}
		
		return false
	}
	
	SnakeProxy.prototype.calcuScore=function(){
		this.score+=100;
		this.scoreEL.innerText='分数:'+this.score;
	}
	
	SnakeProxy.prototype.calcuTime=function(){
		if(this.moveCount%4==0){
			this.time++;
			this.time_flag=false;
			this.timeEL.innerText='时间:'+this.time;
		}
		
		this.moveCount++;
	}
	
	SnakeProxy.prototype.move=function(){
		this.calcuTime();
		
		var headPos = this.snakePosArr[0];
		//执行吃动作
		var eatFlag = this.eat(headPos);
		//计算下一个坐标
		var nextPos = this.computedNext(headPos);
		
		var endFlag = this.end(nextPos,headPos);
		if(endFlag) {
			this.stop();
			this.hasEnd=true;
			return ;
		} 
		
		if(!eatFlag){//如果吃了食物,则不行弹出最后一个
			this.snakePosArr.pop();
		}
		this.snakePosArr.unshift(nextPos);
		this.update();
		
		this.render();
	}

 

资源下载,不需要积分的

给个三连吧兄弟们!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!