用JavaScript寫的貪喫蛇遊戲(很簡單,很詳細)

效果

編寫外部框架

<!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();
	}

 

資源下載,不需要積分的

給個三連吧兄弟們!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章