效果
编写外部框架
<!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();
}
给个三连吧兄弟们!
来源:oschina
链接:https://my.oschina.net/u/4394131/blog/4949991