Snake game made with canvas+javascript

This is a small snake game made with html5 canvas (canvas) combined with javascript. It completes the basic game functions, but there are some areas that are not yet perfect. The javascript resources and codes used can be downloaded by clicking the following link: Snake game code resources made with canvas .

general idea

Although Snake is a simple game, it still needs to consider a lot of things to realize on the canvas. I divided the whole game into several parts to solve them separately, and finally completed the game. The specific functional modules are as follows.

required function

(1) Regarding snakes:

  1. The drawing of the greedy snake: I drew the color of the greedy snake as blue (#0000ff), and drew a snake eye as black (#000000) at the head of the snake, then created an object snake, and put in the snake Data related to greedy snakes, such as the coordinates of the snake head, the coordinates of the snake tail, the turning point of the snake body (the snake will generate a turning point when turning), the current moving direction of the snake head/snake tail, and the direction of the previous frame of the snake head.
  2. Snake's movement: Using the canvas to make a small snake game naturally does not really let the snake move forward, but redraws every frame. According to the snake.fangxiang1 (moving direction of the snake head) attribute of the object, the coordinates of the snake head are modified every frame. Similarly, the coordinates of the snake tail are modified according to the snake.fangxiang2 attribute.
  3. When the snake eats itself: After each movement of the snake, it will explore the pixel colors in front of the snake head and at the left and right positions. If the pixel in a certain direction is predicted to be blue, it means that the next frame will go to this direction Going in the direction will eat yourself, that is, the game fails. So I created an aa array to store the direction in which the snake will be eaten. When entering the next frame, the current direction will be compared with the aa array. If the current direction is stored in the aa array, it will be determined that you have eaten yourself, the game is over, a confirmation box will pop up, and the page will be refreshed.
  4. The greedy snake eats food: this is actually similar to eating itself, just a different color (the food is red), but eating itself is a game failure, and eating food is clearing the food, and in a The new location spawns food, and the snake grows longer.
  5. How the greedy snake grows longer: here I set a variable gogo, if gogo==false, it means that no food is eaten, and the snake moves forward as usual, when gogo=false, the snake head moves forward, the snake tail does not move forward, then the overall snake The length is elongated.
  6. The function of the turning point: when the user presses the arrow keys on the keyboard, the current coordinates of the snake head will be stored in the turning point array, the snake head will continue to move forward according to snake.fangxiang1, and the snake tail will be based on the difference between the snake tail and the first turning point To calculate which direction should be moved, when the tail of the snake reaches the position of the first turning point, delete the turning point, at this time the second turning point represents the "first" turning point.
    (2) About the canvas:
    the canvas is the overall game interface, mainly composed of a green background (grass), a red circle (food), and a blue polyline (snake). The size is 300x300, and there is a button in the center of the canvas for start Game button, the button disappears after clicking the button, and the game starts.
    (3) About food:
    The food will disappear after being touched by the snake head. The specific method is to draw a green circle at the coordinates of the current food with the same radius as the food, so that the food can be covered, and then two random Integer (multiple of 5), as the new coordinates of the food, the food will be redrawn in the next frame.

all codes

The idea has been explained, and the following is the complete version of the code. The comments are already complete, so I won’t break the code apart and talk about it part by part. Of course, copying and pasting directly will definitely not work, because there is a lack of a modernizr.js file, and this file and the code file must be placed in the same folder to run. The link at the beginning of the article can download the code file and modernizr.js file.

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>画布练习</title>
<script src="modernizr.js"></script>
<script type="text/javascript">
	window.addEventListener("load", eventWindowLoaded, false);	
	function eventWindowLoaded() {
    
    
		canvasApp();
	}
	function canvasSupport () {
    
    
		return Modernizr.canvas;
	}
	function canvasApp(){
    
    
		if (!canvasSupport()) {
    
    
			return;
		}	
		var theCanvas=document.getElementById("canvas1");	
		var cxt=theCanvas.getContext("2d");
		var food={
    
    
			foodx:100,		//食物的x坐标
			foody:100,		//食物的y坐标
		};
		var snake={
    
    
			headx:150,		//蛇头x坐标
			heady:50,		//蛇头y坐标
			woundx:[],		//转折点x坐标
			woundy:[],		//转折点y坐标
			endx:100,		//蛇尾x坐标
			endy:50,		//蛇尾y坐标
			fangxiang1:"right",	//蛇头方向
			fangxiang2:"right",	//蛇尾方向
		};
		var gameover=false;			//游戏结束的标志
		var aa=[];		//记录下一帧会吃到自己的方向
		var bb;		//记录下一帧会吃到食物的方向
		var gogo=false;		//若gogo=false,则代表没吃到食物,蛇尾需要前进。若gogo=true,则代表吃到食物,蛇尾不需要前进
		drawScreen();
		function drawScreen(){
    
    
			//背景绘制
			cxt.fillStyle="#00ff00";		//设置背景颜色为绿色
			cxt.fillRect(0,0,300,300);		//绘制背景,一个左上角在(0,0),右下角在(600,600)的矩形
			
			//绘制食物
			cxt.beginPath();			//开始绘制
			cxt.fillStyle="#ff0000";		//设置食物颜色为红色
			cxt.arc(food.foodx,food.foody,9,(Math.PI/180)*0,(Math.PI/180)*360,false);		//在(100,100)的位置画一个半径为5的圆
			cxt.fill();			//立即以填充颜色的方式绘制
			cxt.closePath();	//结束绘制
			
			//绘制贪食蛇
			cxt.beginPath();	//开始绘制
			cxt.strokeStyle="#0000ff";		//设置贪食蛇颜色为蓝色
			cxt.lineCap="round";		//设置线段两端为圆角
			cxt.lineWidth=10;		//设置线段宽度为10
			cxt.moveTo(snake.endx,snake.endy);		//设置线段的开始点为蛇尾(snake.endx,snake.endy)
			for(var i=0;i<snake.woundx.length;i++){
    
    
				cxt.lineTo(snake.woundx[i],snake.woundy[i]);		//根据蛇的转折点数量,来连接蛇身和蛇尾
			}
			cxt.lineTo(snake.headx,snake.heady);			//再将蛇头连接
			cxt.stroke();			//立即以描边的方式绘制
			cxt.closePath();		//结束绘制
			
			//绘制贪食蛇的眼睛
			cxt.beginPath();		//开始绘制
			cxt.fillStyle="#000000";		//设置蛇眼为黑色
			cxt.arc(snake.headx,snake.heady,3,(Math.PI/180)*0,(Math.PI/180)*360,false);		//在(snake.headx,snake.heady)的位置画一个半径为3的圆
			cxt.fill();			//以填充颜色的方式立即绘制
			cxt.closePath();		//结束绘制	
			
			//判断游戏是否结束
			if(snake.headx>297||snake.headx<0||snake.heady>297||snake.heady<0){
    
    
				gameover=true;
			}
			for(var i=0;i<aa.length;i++){
    
    
				if(aa[i]==snake.fangxiang1){
    
    
					gameover=true;
					break;
				}
			}
			if(gameover==true){
    
    
				var t=confirm("你输了");		//弹出确认框,并返回一个值t,t代表用户点了确认键/取消键
				if(t==true){
    
    
					clearInterval(tt);	//清除动画
					location = location;		//刷新页面
				}else{
    
    
					clearInterval(tt);
					location = location;
				}
			}
			aa.length=0;		//清空数组
			//判断是否吃到了食物
			if(bb==snake.fangxiang1){
    
    
				cxt.beginPath();
				fillStyle="#00ff00";		//设置颜色为背景色
				cxt.arc(food.foodx,food.foody,9,(Math.PI/180)*0,(Math.PI/180)*360,false);		//清除该食物
				cxt.fill();
				cxt.closePath();
				food.foodx=Math.floor(Math.random()*56)*5+10;		//重新设置食物x坐标
				food.foody=Math.floor(Math.random()*56)*5+10;		//重新设置食物y坐标
				gogo=true;			//表示吃到了食物
			}
			bb="";		//清除bb
			
			//根据snake.fangxiang1来判断蛇头的前进方向,同时根据前进后的蛇头位置,来预判下一步是否会吃到自己或者吃到食物
			switch(snake.fangxiang1){
    
    
				case "right":
					snake.headx+=5;
					var cc=cxt.getImageData(snake.headx+5,snake.heady,1,1);
					setAB(cc,"right");
					cc=cxt.getImageData(snake.headx,snake.heady-5,1,1);
					setAB(cc,"top");
					cc=cxt.getImageData(snake.headx,snake.heady+5,1,1);
					setAB(cc,"bottom");
					break;
				case "left":
					snake.headx-=5;
					var cc=cxt.getImageData(snake.headx-5,snake.heady,1,1);
					setAB(cc,"left");
					cc=cxt.getImageData(snake.headx,snake.heady-5,1,1);
					setAB(cc,"top");
					cc=cxt.getImageData(snake.headx,snake.heady+5,1,1);
					setAB(cc,"bottom");
					break;
				case "top":
					snake.heady-=5;
					var cc=cxt.getImageData(snake.headx+5,snake.heady,1,1);
					setAB(cc,"right");
					cc=cxt.getImageData(snake.headx-5,snake.heady,1,1);
					setAB(cc,"left");
					cc=cxt.getImageData(snake.headx,snake.heady-5,1,1);
					setAB(cc,"top");
					break;
				case "bottom":
					snake.heady+=5;
					var cc=cxt.getImageData(snake.headx+5,snake.heady,1,1);
					setAB(cc,"right");
					cc=cxt.getImageData(snake.headx-5,snake.heady,1,1);
					setAB(cc,"left");
					cc=cxt.getImageData(snake.headx,snake.heady+5,1,1);
					setAB(cc,"bottom");
					break;
			}
			function setAB(e,f){
    
    
				if(e.data[2]==255){
    
    
					aa.push(f);
				}
				if(e.data[0]==255){
    
    
					bb=f;
				}
			}
			
			//根据gogo的值判断蛇尾是否需要前进,用来实现蛇身变长
			if(gogo==false){
    
    
				//以下是蛇尾前进的方法
				if(snake.woundx.length!=0){
    
    
					//若转折点数组不为空,则根据蛇尾坐标和转折点数组中的第一个坐标的差值,判断蛇尾的前进方向
					if(snake.woundx[0]-snake.endx>0){
    
    
						snake.fangxiang2="right";
					}
					if(snake.woundx[0]-snake.endx<0){
    
    
						snake.fangxiang2="left";
					}
					if(snake.woundy[0]-snake.endy>0){
    
    
						snake.fangxiang2="bottom";
					}
					if(snake.woundy[0]-snake.endy<0){
    
    
						snake.fangxiang2="top";
					}
				}else{
    
    
					//若转折点数组为空,则蛇尾与蛇头进行比较
					if(snake.headx-snake.endx>0){
    
    
						snake.fangxiang2="right";
					}
					if(snake.headx-snake.endx<0){
    
    
						snake.fangxiang2="left";
					}
					if(snake.heady-snake.endy>0){
    
    
						snake.fangxiang2="bottom";
					}
					if(snake.heady-snake.endy<0){
    
    
						snake.fangxiang2="top";
					}
				}
			
				//根据蛇尾的前进方向(snake.fanxgiang2)来决定蛇尾的坐标值变化
				switch(snake.fangxiang2){
    
    
					case "right":
						snake.endx+=5;
						break;
					case "left":
						snake.endx-=5;
						break;
					case "top":
						snake.endy-=5;
						break;
					case "bottom":
						snake.endy+=5;
						break;
				}
			}else{
    
    
				gogo=false;
			}
			//当蛇尾到达第一个转折点时,移除该转折点
			if(snake.endx==snake.woundx[0]&&snake.endy==snake.woundy[0]){
    
    
				snake.woundx.shift();		//shift()函数时移除数组中第一个值,并将数组中剩余的值重新设置下标
				snake.woundy.shift();
			}
		}
		
		//键盘监听事件
		function eventKeyDown(event){
    
    
			switch(event.keyCode){
    
    
				case 37:		//37代表了键盘的向左键,同理38为上,39为右,40为下。
					if(snake.fangxiang1!="right"){
    
    
						snake.fangxiang1="left";
						snake.woundx.push(snake.headx);		//将蛇头x坐标存入转折点x坐标数组里
						snake.woundy.push(snake.heady);		//将蛇头y坐标存入转折点y坐标数组里
						break;
					}else{
    
    
						break;
					}
				case 38:
					if(snake.fangxiang1!="bottom"){
    
    
						snake.fangxiang1="top";
						snake.woundx.push(snake.headx);
						snake.woundy.push(snake.heady);
						break;
					}else{
    
    
						break;
					}
				case 39:
					if(snake.fangxiang1!="left"){
    
    
						snake.fangxiang1="right";
						snake.woundx.push(snake.headx);
						snake.woundy.push(snake.heady);
						break;
					}else{
    
    
						break;
					}
				case 40:
					if(snake.fangxiang1!="top"){
    
    
						snake.fangxiang1="bottom";
						snake.woundx.push(snake.headx);
						snake.woundy.push(snake.heady);
						break;
					}else{
    
    
						break;
					}
			}
		}
		
		//开始游戏按钮点击事件
		var button=document.getElementById("start");		//将参数button与id="start"的按钮绑定
		button.addEventListener("click",startgame);			//按钮点击事件
		
		//开始游戏
		var tt;				//用于保存播放动画函数,在游戏结束时需要删除动画(即clearInterval(tt)),才能刷新页面
		function startgame(){
    
    
			button.style.visibility="hidden";		//将开始游戏按钮隐藏
			window.addEventListener("keyup",eventKeyDown,true);	//添加键盘监听事件
			tt=setInterval(drawScreen,70);			//每40毫秒运行一次drawScreen()函数
		}
	}
</script>
</head>
<body>
	<div>
		<canvas id="canvas1" width="300" height="300"></canvas>
		<button id="start" style="position:absolute;left:80px;top:150px;width:150px;height:40px;">开始游戏</button>
		<br>
		<span>使用键盘的上下左右键来控制蛇的移动方向</span>
	</div>
</body>
</html>

Guess you like

Origin blog.csdn.net/zhulong16/article/details/102727334