效果
編寫外部框架
<!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();
}