最近前端以及JavaScript很火,再加上上课web的需要,感觉是时候系统的学学HTML5了,之前和同学合作用html5的canvas开发了一个贪吃蛇改进版的小游戏,很有趣,当时的核心代码是同学敲得,而我也在忙别的事情一直没有回过头来研究一下。好在快一年的时间了,自己也学了很多种UI的皮毛,见得多了,再看canvas感觉就是只要有文档就能直接用了。
这里分享一下开发的过程吧。
效果图。


还有穿墙功能。

前置技能
使用canvas。
所需工具以及前置技能:支持HTML5的浏览器,html+css, javascript,Jquery
canva是HTML5中新特性的一种,直接可以进行绘图,和其他UI差别不大。
布局
首先,我们写个index.html,并进行简单的布局及样式。
设置整个画布为720 x 480 的大小。每个蛇身、食物的半径为12,所以一行能放720 / 24 = 30个,每列能放 480 / 24 = 20个元素。
//简单的布局
Score //得分 Level //关卡 //引入jquery
主逻辑
然后我们编写主逻辑。
首先获取Canvas对象,然后设置网页的键盘监听器(接受上下左右的按键),接着通过Jquery获取StartGame这个按钮的事件。
在引入Jquery的代码下面插入一段JS脚本。
<script type="text/javascript"> var canvas = document.getElementById('canvas1'); //获取canvas画布 var ctxt = canvas.getContext('2d'); //以后就用ctxt来在canvas画布上绘画 document.addEventListener('keydown',keyDownCheck,false); //设置键盘按下的监听事件,通过keyDownCheck这个函数处理, $("#startGame").click(function(){ beginGame(1); // 通过beginGame(1)这个函数来开始游戏,1代表了开始的关卡 }); </script>
好,这样我们游戏的外部框架出来了,接着我们往里深入,来实现贪吃蛇的主体逻辑。
Snake类
首先,我们先创建一个Snake的类,javascript的类用function就可以实现。
贪吃蛇需要有身子,身子由什么组成呢? 因为是2D游戏,所以由一个个相连的二位坐标点组成。
到这里我们想到,蛇身由点组成,那么食物也可由点组成,于是这里我们把点写成一个类,num属性代表食物的得分。
function Pos(x,y,num = 2){ this.x = x; this.y = y; this.num = num; }
this.body = [new Pos(20,15), new Pos(21,15),new Pos(22,15), new Pos(23,15), new Pos(24,15),new Pos(25,15)]; //定义蛇身,从20 ~ 25相邻的一串点
当然食物(障碍物)obstacle,我们也用一样的方法定义了。
function Obstacle(){ this.body = [new Pos(0,0),new Pos(0,1),new Pos(1,0) ,new Pos(1,1),new Pos(29,19),new Pos(10,10),new Pos(11,11)]; }
有了蛇身,还要定义蛇当前行进的方向。
这里我们规定 1 ,2 ,3 ,4 分别为上下左右
this.dir = 3; // 初始向左
有了身子方向,接着我们要想怎么去移动move。
这里提供一个思路。
首先,我们通过蛇头,和方向判断出蛇下一步要走的那个点的坐标。我们记做head,记录下这一坐标。然后从蛇尾遍历到蛇头,每一个的坐标赋值为其前一个位置的坐标,而蛇头就用head进行赋值,这样就能实现移动了。
当然,有了head我们还能做更多的事,判断head所在位置是不是墙,是不是蛇身,是不是食物,我们就能实现判断是否游戏结束,以及得分情况了。
this.move = function(level){ var isEat = false; var head; if(this.dir == 1) head = new Pos(this.body[0].x,this.body[0].y-1); else if(this.dir == 2) head = new Pos(this.body[0].x,this.body[0].y+1); else if(this.dir == 3) head = new Pos(this.body[0].x-1,this.body[0].y); else if(this.dir == 4) head = new Pos(this.body[0].x+1,this.body[0].y); //判定是否死亡 if(level == 1){ //可以穿墙 ,这里设置第一关可以穿越围墙到另一面 if(head.x < 0) head.x = 29; if(head.x > 29) head.x = 0; if(head.y < 0) head.y = 19; if(head.y > 19) head.y = 0; } else if (head.x <0 || head.x > 29 || head.y < 0 || head.y > 20) //碰到边界 this.dead = true; if(!this.dead){ //吃到自身 for(i=0;i=0;i--){ if(i == 0) this.body[i].x = head.x , this.body[i].y = head.y; else this.body[i].x = this.body[i-1].x,this.body[i].y = this.body[i-1].y; } /* 另一种移动方法 this.body.splice(0,0,head); // 吃掉头 this.body.pop(); // 删除尾部 ,这样就移动了!*/ }
生成食物
吃到食物之后,为了让游戏进行下去,我们还要不断地生成食物。
直接用Math.Random()函数生成待生成食物的x,y坐标,然后判断是否当前坐标已经被占用,如果占用就再生成一次。
if(isEat){ // 添加食物 var isOcupy = false; var tmp; do{ isOcupy = false; tmp = createObstacle(); for(i=0;i function createObstacle(){ var tmp = new Pos(Math.floor(Math.random()*30), Math.floor(Math.random()*20)); tmp.num = Math.random() < 0.1 ? 4: 2; // 出现4和2的比例 1 : 9 return tmp; }
这样,整体的游戏逻辑算是基本实现了。
接下啦完成剩余的工作。
接受键盘数据
主要是上下左右,除了方向键,还增添了wasd,同时保证了按相反的方向无效,因为贪吃蛇不会直接回头走。
function keyDownCheck(){ var e=event||window.event||arguments.callee.caller.arguments[0]; //各种兼容下获取event // alert(e.keyCode); if(e.keyCode == 87 || e.keyCode == 38){ // up if(snake.dir == 2) return; snake.dir = 1; } else if(e.keyCode == 83 || e.keyCode == 40){ // down if(snake.dir == 1) return; snake.dir = 2; } else if(e.keyCode == 65 || e.keyCode == 37){ // left if(snake.dir == 4) return; snake.dir = 3; } else if(e.keyCode == 68 || e.keyCode == 39){ //right if(snake.dir == 3) return; snake.dir = 4; } }
主体循环以及绘图
2D游戏应该都是这样,游戏数据不停的的变化,界面也在不停的刷新。我们用setInterval来设立一个定时器,每隔一段时间刷新一下屏幕。再刷新的同时执行snake的move函数,这样就能让贪吃蛇不停的往前走了。当然最重要的,是要让这个后台数据变化在屏幕上显示出来。这里就用到了canvas的绘图技术了。
首先是开始游戏的函数。
function beginGame(level){ if(gameTimer) clearInterval(gameTimer); //清空计时器 snake = new Snake(); //为贪吃蛇申请对象 obstacle = new Obstacle(); //为障碍物申请对象 gameTimer = setInterval(function(){ //设置定时器 snake.move(level); draw(level); //绘图函数。 $("#score").val(snake.score); //更新成绩 $("#level").val(level); },gameSpeed[level-1]); } var gameSpeed = [240,210,180,150]; // 游戏速度
接着draw函数。
function draw(level){ // 画布大小 720 x 480, 每个元素半径12 // X = 0 ~ 29 , Y = 0 ~ 19 ctxt.clearRect(0,0,canvas.width,canvas.height); //清空我们的画布 ctxt.beginPath(); //开始画 ctxt.rect(0,0,canvas.width,canvas.height); ctxt.fillStyle = "black"; ctxt.closePath(); ctxt.fill(); //填充 for(i=0;iGameOver函数。
function GameOver(){ alert('GameOver!'); clearInterval(gameTimer); //为了保险,清空一下计数器 $("#score").val(0); //得分置为0 $("#level").val(1); //关卡置为1 snake.body = null; //蛇身置为空 }
接下来大家还可以为这个游戏加上音效,背景音乐,或者改造成其他版本的贪吃蛇,例如2048版贪吃蛇,相信如果你仔细学习了这个代码,扩充下去不难做到。
这个项目我已经放到了github上,欢迎fork
https://github.com/chaiwenjun000/webSnake