您的位置:首页 > Web前端 > JavaScript

基于JS、canvas的小游戏--贪吃蛇

2017-10-10 09:33 288 查看
    几个月前学习JS的时候,就想着写几款小游戏,当时在网上找了一个JS贪吃蛇的代码,代码只有20行,却实现了基本的贪吃蛇玩法。那贪吃蛇是由canvas技术实现的,由于我还没接触过canvas,我花了大量时间去学习研究。并在之后扩充了其玩法,添加了速度选择,障碍物生成,游戏暂停与恢复,以及电脑演示的功能。通过那次的尝试,我发现写小游戏可以很好的锻炼我的思维和关于数据跟踪方面的逻辑。

几个月后,我的JS能力相对与之前有了不小的进步,我想把之前的贪吃蛇代码拿出来重构一下,并增加一些道具玩法。之前的代码逻辑是一根线程下来,想到哪里就写在哪里,所有游戏功能都在一个函数里面,这样虽然也能实现游戏玩法,但是很不方面维护和添加新的功能。这次的重构我准备把每个功能模块分离出来,想用哪个模块随时即可。下面开始正题,介绍本次开发的思路:(贪吃蛇链接(点击打开链接),除了排行榜相关的php代码,其他的源代码都可以从链接处得到。)

一、首先把整个游戏的界面大致排版弄出来,:

左面为功能框,有着许多功能按钮。中间为canvas画布,每个蛇身体我设置的为20x20px,整个画布总共有40x30个这样的点,这也是蛇的活动空间。右侧为信息显示框。

背景的彩色雪花的功能由snow.js实现。

canvas代码:

HTML中:

<canvas id="can1" width="800" height="600"></canvas>
JS中:
var c = document.getElementById("can1"),
n, ctx = c.getContext("2d");


二、整个游戏玩法是由main.js实现的。

点击开始按钮之后,倒计时从3->2->1,门右侧播放加油gif图,中间蛇处于静止状态,门由闭合开始慢慢向外打开,其中的gif是由js添加与隐藏的,其他的构成都是canvas技术。下面开始贴代码:

region();//生成开场区域
var t1=setInterval(function(){door(a,b)},100);	//调用开门函数
tt(time);//生成开始倒计时初始数字
var t2=setInterval(function(){tt(time);},1000);	//调用倒计时函数
jyimg();//生成加油gif
var t3=setInterval(function(){jyimg();},3000);//清除加油gif
setTimeout(function(){clearregion()},3100);//清除开场区域
//  **生成入场区域
function region(){
ctx.beginPath();
ctx.moveTo(260,0);
ctx.lineTo(260,100);
ctx.lineTo(360,100);
ctx.lineWidth='2';
ctx.strokeStyle="white";
ctx.stroke();
ctx.closePath();
//	**左右分割
ctx.beginPath();
ctx.moveTo(420,100);
ctx.lineTo(520,100);
ctx.lineTo(520,0);
ctx.lineWidth='2';
ctx.strokeStyle="white";
ctx.stroke();
ctx.closePath();
}
//	**

//	**开门效果
function door(x,y){
ctx.clearRect(360,97,60,70);
ctx.clearRect(300,101,60,70);
ctx.beginPath();
ctx.moveTo(360,100);
ctx.arc(360,100,60,a,a,false);//绘制弧形轨迹线,当这里的两个a参数不同时,有弧线,不然只有半径
ctx.lineJoin="round";
ctx.lineWidth="5";
ctx.strokeStyle="rgb(170,140,110)";
ctx.stroke();
ctx.closePath();
a=x+y;
if(a>2){
clearInterval(t1);
}
}

//	**

//	**开始倒计时
function t
4000
t(a){
ctx.clearRect(275,0,90,90);
ctx.beginPath();
ctx.font="50px 宋体";//样式同css的font
ctx.fillStyle="rgb(40,140,170)";//这里可以设置字体颜色
ctx.fillText(a,305,59);//设置字体和位置
ctx.closePath();
//		**圆形框
ctx.beginPath();
ctx.arc(318,42,40,0,360*Math.PI);
ctx.lineJoin="round";
ctx.lineWidth="3";
ctx.strokeStyle="rgb(40,140,170)";
ctx.stroke();
ctx.closePath();
time=a-1;
if(time<0){
ctx.clearRect(275,0,90,90);
clearInterval(t2);
}
}
//	**

//	**开场gif
function jyimg(){
var jiayou=$('.jiayou');
jiayou.show();
jyi++;
if(jyi==2){
jiayou.hide();
clearInterval(t3);
}
}
//  **

//	**清除开始门区域
function clearregion(){
ctx.clearRect(255,0,110,160);
ctx.clearRect(420,0,110,160);
}
//	**

三、核心到了,生成蛇身体、障碍物、食物以及道具的函数:这里的t参数是蛇的位置参数,画布可以容纳30x40=1200个方块元素,第一行的t参数从左至右依次为0->39。c参数是生成方块的颜色。

//	**生成蛇身体
function draw(t, c) { //画布中生成元素
ctx.fillStyle = c;
var x= (t % 40)*20;
var y= ~~(t / 40)*20;
ctx.fillRect( x , y , 18, 18); //生成画布元素,包括位置(前2个参数)和大小(后两个参数)
}

//	**
1、定义一个蛇的初始身体数组和颜色:

var she=[180,140,100,60];//蛇的身体元素
var shec="rgb(43,141,169)";//蛇的颜色
2、启用循环在画布中生成蛇:
//	**生成方块函数
function is(a){
if(a==0){
for(var shei=0;shei<she.length;shei++){
draw(she[shei],shec);
}
}else{
draw(she[0],shec);
}
}
//	**

四、蛇有了,就要开始考虑它的移动,由于蛇的位置坐标已经被0->1199个位置参数代替,考虑它的移动只需想到4个位置参数,上:-40,下:40,左:-1,右:1。设置一个时钟,每次调用时钟,就在蛇的身体数组开始处加入一个新坐标代替蛇头的位置,新蛇头的位置=原蛇头位置+当前的位置参数。并移除蛇身体数组的最后一位,这样就能实现蛇的移动了。
she.unshift( n=she[0]+shef);
she.pop();
is(1);
is2();
function is2(){//尾巴位置变黑
draw(she[she.length-1],'black');
}


五、食物功能,每当蛇吃到食物的时候,蛇的身体长度加一,其实实现这个功能很简单,当蛇走到食物位置前,把食物的位置加入到蛇数组开头。这时不移除蛇的尾巴,就实现了蛇的增长。同时再调用食物生成函数scfood()。

//**食物判定
if(food.indexOf(n=she[0]+shef) != -1){
she.unshift(n);
scfood(n);
is(1);
}
//  **生成食物点
function scfood(a){
var fn=0;
while (fn==0){
foodi=Math.floor(Math.random()*1200);
while( zaw.indexOf(foodi)==-1 && she.indexOf(foodi)==-1 && food.indexOf(foodi)==-1 && fn==0 ){
fn=1;
food.unshift(foodi);
}
}
var fwz=food.indexOf(a);
if(a!=-1)food.splice(fwz,1);
for(var i=0;i<food.length;i++){
draw(food[i],'#99CC33');
}
}
//	**

六、生成障碍物以及死亡判定:障碍物数量是由每次开局就由选择的难度决定了,随机生成到画布中。
//	**生成障碍物
function sczaw(){
for(var zawi=0;zawi<zawn;zawi++){
var fn=0;
while(fn==0){
zawi2=Math.floor(Math.random()*1200);
while( fn==0 && anquan.indexOf(zawi2)==-1){
fn=1;
zaw.unshift(zawi2);
}
}
}
for(var i=0;i<zaw.length;i++){
draw(zaw[i],'#999999');
}
}
//	**
//**死亡判定,要在移动之前进行判定
if(  zaw.indexOf(she[0]+shef) != -1 || she.indexOf(n, 1)!=-1 || (shef==1 && she[0]%40==39) || (shef==-1 && she[0]%40==0) || (she[0]<40 && shef==-40)  || (she[0]>1160 && shef==40) ){
go=0;
clearInterval(movet);
$('.start').css({'background-color':'red','border-radius':'0px'});
if($('.fs').eq(9).html()=='' || fenshu>eval($('.fs').eq(9).html())){
$('.lsnd2').html(level);
$(".lsfenshu2").html(fenshu);
$('.liuming').show();
}else{
alert('gg思密达!!');
}
}

七、速度选择,这个很简单,速度是由调用移动函数时钟的快慢决定的。
八、六种道具玩法,这次本次重构的重头戏,六种道具分别为①:食物数量增加;②:蛇身体减少;③:高分食物;④:随机将少数障碍物变成可食用方块;⑤:无敌时间,可以撞坏障碍物;⑥:弹簧,吃了加高分,蛇身体反向运动,蛇尾变蛇头。

1、食物增加:吃了此道具,在食物数组里加一随机数,就实现了食物的增加。

2、蛇身体减少:吃了此道具,将蛇数组的后面几位参数移除。

3、高分食物:没有逻辑成分,吃了分数多加一些。

4、变障碍物:吃了此道具,将障碍物数组里的几位随机位置的参数移除。

5、无敌时间:在此期间,碰到障碍物不死亡,将碰到的障碍物参数移除。

6、弹簧:反转蛇身体数组,有蛇尾巴和蛇尾巴倒数第二位判定蛇尾方向。

总结:以上介绍了基本思路,逻辑部分从贴出来的代码处不能完全表达,更详细的思路还是要从源代码中获取。第一次较详细的介绍自己的开发作品,有很多地方表达不是很清楚请见谅。最近在准备纯JS的消消乐游戏的开发,目前有了基本思路,等完成之后在写出来分享。


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: