canvas实现贪吃蛇小游戏
2019-03-07 22:50

canvas实现贪吃蛇小游戏

贪吃蛇小游戏体验点击体验通过画布实现的贪吃蛇小游戏。

游戏效果如下:

image.pngimage.png


小游戏的源码如下:

<!DOCTYPE HTML>
<html>
	<head>
		<meta charset='utf-8'/>
		<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
		<title>贪吃蛇</title>
		<style>
			body{margin:0;background:#000;}
			#mycanvas{position:fixed;z-Index:2;top:0;bottom:0;background:transparent;}
			#canvasbg{position:fixed;z-Index:1;top:0;bottom:0;}
			.disbtn{position:fixed;z-Index:5;right:5px;top:5px;}
			.open_div{position:fixed;z-Index:5;left:0;top:0;bottom:35px;right:0;background:rgba(255,255,255,0.5);white-space:nowrap;text-align:center;}
			.open_div:after{content:"";display:inline-block;height:100%;vertical-align:middle;width:0;font-size:0;}
			.open_tip{display:inline-block;vertical-align:middle;}
			.tip{line-height:25px;font-size:13px;}
			.start-cont{text-align:center;margin-top:25px;}
			.startbtn{display:inline-block;padding:3px 8px;font-size:14px;color:#fff;background:blue;cursor:pointer;}
			.loadresource{position:fixed;z-Index:15;left:0;top:0;width:100%;height:100%;background:#F9A73D;}
			</style>
	
		</head>
		<body>
				<div class='loadresource' id='loadimg'>loading</div>
				<div class='open_div' id='opentip'>
					<div class='open_tip'>
						<div class='tip'>左拐弯:向左滑动</div>
						<div class='tip'>右拐弯:向右滑动</div>
						<div class='tip'>上拐弯:向上滑动</div>
						<div class='tip'>下拐弯:向下滑动</div>
						<div class='start-cont' ><span class='startbtn ' id='start'>开始游戏</span></div>	
					</div>
				</div>
				<canvas  id='mycanvas'>
					
				</canvas>
				<canvas id='canvasbg'>
				</canvas>
				<div class='open_div' id='gameover' style='display:none;'>
					<div class='open_tip'>
					<div class='tip'>是否重新开始游戏?</div>
					<div class='start-cont'><span class='startbtn' id='restart'>重新开始</span></div>
				</div>
				</div>
			</body>
				<script>
				function start_snake(){
					document.getElementById("opentip").style.display='none';
					flag=true;
					//paint();
				}
				function restart(){
				document.getElementById("gameover").style.display='none';
			   	a=-1;b=-1;score=0;
			   	fangxiang=2;last=2;
			   	len=5;
			   	clearMap();
				count=0;
				 flag=true;
				 gameover=false;
				//paint();
			   }
				 var canvas=document.getElementById("mycanvas");
				 var canvasbg=document.getElementById("canvasbg");
				 var ctx=canvas.getContext("2d");
				 var ctxbg=canvasbg.getContext("2d");
				 //变量初始化
				 //缓冲画布变量
				 var buffer,buffer_ctx;
				 var a=-1,b=-1,score=0,speed=14,minspeed=5,initspeed=14,speednum=8,
					 fangxiang=2,last=2,
					 //画布格子对应的二维数组,1表示蛇身,2表示食物。0表示空白。
					 map=[],
					 //蛇身位置数组,
					 row,col,
					 orow,ocol,
					 //蛇身初始化长度
					 len=5;
				//游戏是否进行中。
			    var flag=true;
				var gameover=false;
			    var ttop=35;
			    var tbottom=35;
			    var _ww=document.documentElement.clientWidth;
			    var _hh=document.documentElement.clientHeight-tbottom-ttop;
			    var _thh=document.documentElement.clientHeight;
				//蛇每一格的大小
			    var swd=32;
				//画布分的格子数目
			    var hsum=Math.floor(_ww/swd);
			    var vsum=Math.floor(_hh/swd);
			  //  console.log("hsum:"+hsum+",vsum:"+vsum)
			    var hleft=(_ww-(hsum*swd))/2;
			    var vtop=(_hh-(vsum*swd))/2;
				for(var i=0;i<hsum;i++){
					map[i]=new Array();
					for(var j=0;j<vsum;j++){
					 map[i][j]=0;
					}
				}
				var basepath="demo/";
				//需要加载的图片数组。
				var imgs=["food1.png","food2.png","food3.png","food4.png","coupon.png","doller.png","bg.png","bg2.png","left.png","right.png","blank.png"];
				var foods=["food1.png","food2.png","food3.png","food4.png"];
				var foodsimg=new Array();
				var bg=["bg.png","bg2.png"];
				var blank;
				var prizearr=[];
				var prize=0;
				var dollerarr=[];
				var doller=0;
				var pa=-1,pb=-1;
				var pflag=false;
				var prize_doller=1;
				var prizeimg=new Image();
				var dollerimg=new Image();
				var prize_score=3;
				var lastscore=0;
				//var sbody=["food1.png","food2.png","food3.png","food4.png"];
				var lenarr=[];
				var imgcount=0;
				for(var i=0;i<imgs.length;i++){
					var img=new Image();
					img.onload=loadoneImg;
					img.onerror=loadoneImg;
					img.src=basepath+imgs[i];
				}
				//图片加载完成后进行初始化调用。
				function loadoneImg(){
					imgcount++;
					if(imgcount==imgs.length){
						for(var i=0;i<shead.length;i++){
							var img=new Image();
							img.src=basepath+shead[i];
							sheadimg.push(img);
						}
						for(var i=0;i<foods.length;i++){
							var img=new Image();
							img.src=basepath+foods[i];
							foodsimg.push(img);
						}
						blank=new Image();
						blank.src=basepath+"blank.png";
						prizeimg.src=basepath+"coupon.png";
						dollerimg.src=basepath+"doller.png";
						init();
						var load=document.getElementById("loadimg");
						load.parentNode.removeChild(load);
					}
				}
				 //初始化画布信息
				 function init(){
					var _w=document.documentElement.clientWidth;
					var _h=document.documentElement.clientHeight;
					canvas.width=_w;
					canvas.height=_h;
					canvasbg.width=_w;
					canvasbg.height=_h;
					buffer=document.createElement("canvas");
					//document.body.appendChild(buffer);
					buffer.width=_w;
					buffer.height=_h;
					buffer_ctx=buffer.getContext("2d");
					ctx.clearRect(0,0,canvas.width,canvas.height);
					buffer_ctx.clearRect(0,0,buffer.width,buffer.height);
					initEvent();
					adjustwin();
					newSnake();
					snake_action();
					//绘制背景画布,背景画布不用刷新,可以避免大面积刷新引起的闪烁。
					var bgimg2=new Image();
					bgimg2.src=basepath+bg[1];
					ctxbg.drawImage(bgimg2,hleft,vtop+ttop,(hsum*swd),(vsum*swd));
				 	ctx.clearRect(0,0,canvas.width,canvas.height);
				 	ctx.drawImage(buffer,0,0);
					flag=false;
					paint();
					
				 }
				
				 var count=0;
				 //绘制动画画布
				 function paint(){
					requestAnimationFrame(paint);
					
					//count++;
					//if(count==10){
					//count=0;
					snake_action();
				 	ctx.clearRect(0,0,canvas.width,canvas.height);
				 	ctx.drawImage(buffer,0,0);
					//}
					
				 }
				 var istouch=!!(("ontouchstart" in window) || window.DocumentTouch && document instanceof DocumentTouch);
				 var touchstart=istouch?'touchstart':'mousedown',
					 touchmove=istouch?'touchmove':'mousemove',
					 touchend=istouch?'touchend':'mouseup';
				 function adjustwin(){
					document.body.addEventListener(touchstart,function(evt){
						evt.preventDefault();
					},false);
					document.body.addEventListener(touchmove,function(evt){
						evt.preventDefault();
					},false);
					document.body.addEventListener(touchend,function(evt){
						evt.preventDefault();
					},false);
				 }
				 function paint_snake(){
				 	
				 }
				 //绑定事件
				 function initEvent(){
					canvas.addEventListener(touchstart,recordstart,false);
					canvas.addEventListener(touchmove,recordmove,false);
					canvas.addEventListener(touchend,recordend,false);
					window.addEventListener("keydown",keyrecord,false);
					document.getElementById("start").addEventListener(touchend,start_snake,false);
					document.getElementById("restart").addEventListener(touchend,restart,false);
				 }
				 var startX,startY,endX,endY;
				 //根据滑动方向确定蛇运动的方向
				 function recordstart(evt){
					evt.preventDefault();
					evt.stopPropagation();
					var ee=('ontouchend' in document)?evt.touches[0]:evt;
					startX=endX=(ee.clientX==undefined)?ee.screenX:ee.clientX;
					startY=endY=(ee.clientY==undefined)?ee.screenY:ee.clientY;
				 }
				 function recordmove(evt){
					evt.preventDefault();
					evt.stopPropagation();
					var ee=('ontouchend' in document)?evt.touches[0]:evt;
					endX=(ee.clientX==undefined)?ee.screenX:ee.clientX;
					endY=(ee.clientY==undefined)?ee.screenY:ee.clientY;
				 }
				 function recordend(evt){
					 evt.stopPropagation();
					if(Math.abs(endX-startX)>Math.abs(endY-startY)){
						if(endX>startX){
							fangxiang=4;
						}else{
							fangxiang=3;
						}
					}else{
						if(endY>startY){
							fangxiang=2;
						}else{
							fangxiang=1;
						}
					}
				 }
				function keyrecord(evt){
					if(flag){
				  	var keycode=evt.keyCode;
				  	switch(keycode){
				  		case 37:fangxiang=3;break;
				  		case 38:fangxiang=1;break;
				  		case 39:fangxiang=4;break;
				  		case 40:fangxiang=2;break;
				  	}
				  }
				 }
				//初始化画布清空数组
				function clearMap()
				{
					score=0;
					fangxiang=2;
					last=2;
					len=5;
					a=-1;
					b=-1;
					lenarr=[];
					prize=0;
					doller=0;
					pa=-1,pb=-1;
					pflag=false;
					speed=20;
					for(var i=0;i<hsum;i++)
					{
						for(var j=0;j<vsum;j++)
						{
							map[i][j]=0;
						}
					}
					row=[hsum*vsum];
					col=[hsum*vsum];
					orow=[hsum*vsum];
					ocol=[hsum*vsum];
					for(var i=0;i<len;i++)
					{
						row[i]=Math.floor(hsum/3);
						orow[i]=Math.floor(hsum/3);
						col[i]=len-i;
						ocol[i]=len-i;
					}
					for(var i=0;i<len;i++)
					{
						map[row[i]][col[i]]=1;
						
					}
					
				}
				 //新建蛇
				 function newSnake()
					{
						a=-1;
						b=-1;
						row=[hsum*vsum];
						col=[hsum*vsum];
						orow=[hsum*vsum];
						ocol=[hsum*vsum];
						for(var i=0;i<len;i++)
						{
							row[i]=Math.floor(hsum/3);
							orow[i]=Math.floor(hsum/3);
							col[i]=len-i;
							ocol[i]=len-i;
						}
						for(var i=0;i<len;i++)
						{
							map[row[i]][col[i]]=1;
						}
					}
				var foodindex=0;
				//得到随机食物,并将食物序列保存变量。
				function getFoodimg(){
					var rd=foodsimg[Math.floor(Math.random()*foodsimg.length)];
					foodindex=rd;
					return rd;
				}
				var foodimg;
				//蛇头对应的图片
				var shead=["left.png","right.png","left.png","right.png"];
				var sheadimg=new Array();
				var olda=-1,oldb=-1;
				//绘制画布
				var wordleft;
				function paintimage()
				{
					
					if(flag)
					{
					buffer_ctx.clearRect(0, 0,buffer.width,buffer.height);
					//buffer_ctx.strokeStyle='#F06128';
					//buffer_ctx.strokeRect(hleft,vtop+ttop,(hsum*swd),(vsum*swd));
					/*var bgimg2=new Image();
					bgimg2.src=basepath+bg[1];
					buffer_ctx.drawImage(bgimg2,hleft,vtop+ttop,(hsum*swd),(vsum*swd));*/
					//绘制得分信息。
					buffer_ctx.fillStyle='#FFFFFF';
					buffer_ctx.font="16px 微软雅黑";
					var scorestr="得分:"+(score*20);
					var fontw=buffer_ctx.measureText(scorestr);
					buffer_ctx.fillText(scorestr,hleft+15,30);
					buffer_ctx.drawImage(prizeimg,hleft+25+fontw.width,8,32,32);
					var prizestr=prize;
					var prizew=buffer_ctx.measureText(prizestr);
					buffer_ctx.fillText(prizestr,hleft+30+fontw.width+32,30);
					buffer_ctx.drawImage(dollerimg,hleft+45+fontw.width+32+prizew.width,8,32,32);
					buffer_ctx.fillText(doller,hleft+45+fontw.width+69+prizew.width,30);
					buffer_ctx.beginPath();
					//var bgimg=new Image();
					//bgimg.src=basepath+bg[0];
					/*for(var i=0;i<hsum;i+=2)
						for(var j=0;j<vsum;j+=2)
						{
								buffer_ctx.strokeStyle='#D7CE3A';
								buffer_ctx.strokeRect(i*swd+hleft+1,j*swd+vtop+ttop+1,swd-2,swd-2);
								buffer_ctx.fillStyle="#dddddd";
								buffer_ctx.fillRect(i*swd+hleft+3,j*swd+vtop+ttop+3,swd-6,swd-6);
								buffer_ctx.drawImage(bgimg,i*swd+hleft,j*swd+vtop+ttop,2*swd,2*swd);

						}*/
					for(var i=0;i<hsum;i++)
						for(var j=0;j<vsum;j++)
						{
							if(map[i][j]==1)
								{
								/*if(row[0]==i&&col[0]==j){
								 buffer_ctx.fillStyle='#d4f2e8';
								}else{
								buffer_ctx.fillStyle='#d4f2e8';
								}
								buffer_ctx.fillRect(i*swd+hleft+1, j*swd+vtop+1, swd-2, swd-2);*/
								}
							else if(map[i][j]==2)
							{
								/*buffer_ctx.fillStyle='#158B05';
								buffer_ctx.arc(i*swd+swd/2+hleft, j*swd+swd/2+vtop+ttop, swd/2, 0,2*Math.PI);
								buffer_ctx.fill();*/
								//绘制可以吃的食物。
								buffer_ctx.drawImage(foodimg,i*swd+hleft-2,j*swd+vtop+ttop-2,swd+4,swd+4);
							}else if(map[i][j]==3){
								var img=prize_doller==1?prizeimg:dollerimg;
								buffer_ctx.drawImage(img,i*swd+hleft-2,j*swd+vtop+ttop-2,swd+4,swd+4);
							}
							/*else{
								buffer_ctx.strokeStyle='#D7CE3A';
								buffer_ctx.strokeRect(i*swd+hleft+1,j*swd+vtop+ttop+1,swd-2,swd-2);
								buffer_ctx.fillStyle="#dddddd";
								buffer_ctx.fillRect(i*swd+hleft+3,j*swd+vtop+ttop+3,swd-6,swd-6);
							}*/
						}
					 for(var k=0;k<len;k++){
						var lec=0,lecv=0;
						//根据旧位置和新位置将每一步分成speed步进行流畅动画。
						if(!(row[k]==orow[k]&&col[k]==ocol[k])){
							if(orow[k]==undefined||ocol[k]==undefined){
								orow[k]=row[k];ocol[k]=col[k];
								lec=(row[k-1]-orow[k])*swd*(count/speed);
								lecv=(col[k-1]-ocol[k])*swd*(count/speed);
							}else{
								lec=(row[k]-orow[k])*swd*(count/speed);
								lecv=(col[k]-ocol[k])*swd*(count/speed);
							}
							
						}
						if(k==0){
							//绘制蛇头,根据方向改变蛇头的图片
							
							var img=sheadimg[fangxiang-1];
							buffer_ctx.drawImage(img,orow[k]*swd+hleft+lec,ocol[k]*swd+vtop+ttop+lecv,swd,swd);
						}else{
							//绘制蛇身,如果没有吃食物的身体用blank图片绘制。
							
							var img=lenarr[k-1]==undefined?blank:lenarr[k-1];
							
							buffer_ctx.drawImage(img,orow[k]*swd+hleft+lec,ocol[k]*swd+vtop+ttop+lecv,swd,swd);
						}
					 }
					}
					else
					{   
						if(gameover){
						buffer_ctx.fillStyle='#267322';
						buffer_ctx.font="40px 微软雅黑";
						var txt="游戏结束"
						var textw=buffer_ctx.measureText(txt).width;
						buffer_ctx.fillText(txt,buffer.width/2-textw/2,buffer.height/2);
						document.getElementById("gameover").style.display='block';
					}
					}
					var prizeuser="恭喜小明中得了一个小苹果奖品,恭喜小王中得了小香蕉奖品";
					buffer_ctx.fillStyle='#BE5454';
					buffer_ctx.font="16px 微软雅黑";
					var userstr=buffer_ctx.measureText(prizeuser);
					if(wordleft==undefined){
						wordleft=_ww;
					}
					wordleft--;
					if(wordleft<=-userstr.width){
						wordleft=_ww;
					}
					//console.log(userstr.);
					var wordtop=_hh+20+ttop;
				 //  console.log(wordleft+"--");
				 //console.log(wordtop);
				   //console.log(((_thh-_hh)/2-userstr.height));
				   buffer_ctx.clearRect(0, _hh+ttop,buffer.width,(_thh-_hh));
					buffer_ctx.fillText(prizeuser,wordleft,wordtop);
				}
				function getpflag(){
					//pflag=Math.random()>0.8;
					prize_doller=Math.random()>0.5?1:2;
					//return pflag;
				}
				var prizelast=0;
				//蛇的运动
				function snake_action(){
					count++;
					if(count==speed){
						count=0;

					if(flag==true)
					 {  
						 for(var i=0;i<len;i++){//保存蛇的旧位置。
							 orow[i]=row[i];
							 ocol[i]=col[i];
						}
						
					 	 var oldlen=len;
						 if(a==-1&&b==-1)
							{  //创建蛇吃的食物
								a=Math.floor(Math.random()*hsum);
								b=Math.floor(Math.random()*vsum);
								while(map[a][b]==1)
								{
									a=Math.floor(Math.random()*hsum);
									b=Math.floor(Math.random()*vsum);
								}
								map[a][b]=2;
								olda=a;oldb=b;
								foodimg=getFoodimg();
								
							}
							else
							{	//蛇吃到食物时的处理
								if(row[0]==a&&col[0]==b)
								{ 
									map[a][b]=0;
									//如果是空气泡时不增加长度。
									if(lenarr.length+1>=len){
										len++;
									}
									a=-1;
									b=-1;
									score++;
									lastscore++;
									speed=initspeed-Math.floor(score/speednum);
									if(speed<minspeed){
										speed=minspeed;
									}
									//将对应的食物图片名称放到数组中
									lenarr.push(foodindex);
									
									
								}
							}
							if(pa==-1&&pb==-1&&lastscore>=prize_score){
								lastscore=lastscore-prize_score;
								prize_score+=1;
								getpflag();
								pa=Math.floor(Math.random()*hsum);
								pb=Math.floor(Math.random()*vsum);
								while(map[pa][pb]==1||map[pa][pb]==2)
								{
									pa=Math.floor(Math.random()*hsum);
									pb=Math.floor(Math.random()*vsum);
								}
								map[pa][pb]=3;
								prizelast=0;
							}else{
								
								if(row[0]==pa&&col[0]==pb)
								{ 
									map[pa][pb]=0;
									pa=-1;
									pb=-1;
									prize_doller==1?prize++:doller++;
								}else{
									prizelast++;
									if(prizelast>=120&&pa!=-1&&pb!=-1){
									  map[pa][pb]=0;
									  pa=-1;
									  pb=-1;
									}
								}
							}
					//先请清空最后一个,再将前一个位置赋值给后一个位置。最后再改变第一个的位置。
					map[row[oldlen-1]][col[oldlen-1]]=0;
					  for(var i=len-1;i>0;i--)
					 {
						 row[i]=row[i-1];
						 col[i]=col[i-1];
					 }
					 
					 switch(fangxiang)
					 {
					 case 1:
						 if(last==2)
						 {
							 col[0]+=1;
						 }
						 else
						 {
							 col[0]-=1;
							 last=1;
							 
						 }
						 break;
					 case 2:
						 if(last==1)
						 {
							 col[0]-=1;
						 }
						 else
						 {
							 col[0]+=1;
							 last=2;
							
						 }
						 break;
					 case 3:
						 if(last==4)
						 {
							 row[0]+=1; 
						 }
						 else
						 {
							 row[0]-=1;
							 last=3;
							 
						 }
						 break;
					 case 4:
						 if(last==3)
						 {
							 row[0]-=1;
						 }
						 else
						 {
							 row[0]+=1;
							 last=4;
							
						 }
						 break;	 	
					 }
					 //蛇撞墙或者咬到自己的尾巴时游戏结束
					 if(row[0]>=hsum||row[0]<0||col[0]>=vsum||col[0]<0||map[row[0]][col[0]]==1)
					 {
						 flag=false; 
						 gameover=true;
						 paintimage();
					 }else{
					
					 
					 for(var i=0;i<len;i++)
					 {
						 map[row[i]][col[i]]=1;
					 }
					//paintimage();
				}	
			} 
		 }
		 paintimage();
		}
		//更新得分
		
	</script>
	</html>

此小游戏使用canvas的双缓存,以及requestAnimationFrame刷新,移动端事件以及键盘事件操作,蛇身移动的运算。

代码中已有详细的注释,接下来的几篇文章,我们将逐点讲解此小游戏中使用的canvas的知识点。

原创文章,转载请注明来自:妹纸前端-www.webfront-js.com.
阅读(740)
辛苦了,打赏喝个咖啡
微信
支付宝
妹纸前端
妹纸前端工作室 | 文章不断更新中
京ICP备16005385号-1