300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 微信小游戏——贪吃蛇

微信小游戏——贪吃蛇

时间:2018-09-01 00:43:34

相关推荐

微信小游戏——贪吃蛇

博客简介

本篇博客介绍的是微信小游戏贪吃蛇的案例,详细的开发过程,并且提供代码压缩包下载。

案例简介布局构建对象响应事件蛇头对象的移动蛇身的移动食物刷新绘制得分碰撞检验开始界面和结束界面前后台的切换添加音频源码下载/download/weixin_44307065/12151658

微信小游戏贪吃蛇

案例简介

作者学习微信小程序时偶然想到贪吃蛇游戏,画布绘制了一番,突发了做贪吃蛇的想法。由于还没有正式开始学习小游戏,并且只是想要简单的做出这个程序来,就没有去用游戏引擎,深究小游戏中的细节。游戏基于canvas以及响应事件,效果如下。(由于微信审核小城序名称的限制,将其命名为贪吃毛毛虫)

布局

整个小游戏界面的布局很简单,包括小蛇,食物,以及纯色背景,插入的文字得分组成。

构建对象

按照布局中的元素,我们构建蛇头对象,蛇身对象,食物三个对象,并且添加相应的位置,颜色,大小属性:

//蛇头对象var snakeHead = {x: 100,y: 100,r: 15,deg: 0,snakeDirection: "right",color: '#CD5C5C',drawHead: function () {//画出嘴脸this.ctx.save();ctx.translate(this.x, this.y);ctx.rotate(this.deg);//旋转ctx.beginPath();ctx.moveTo(0, 0);ctx.arc(0, 0, this.r, Math.PI / 7, -Math.PI / 7, false);ctx.fillStyle = this.color;ctx.fill();ctx.beginPath();ctx.arc(0, -this.r / 2, 2, Math.PI * 2, 0, true);ctx.fillStyle = 'black';ctx.fill();ctx.restore();}}//蛇身数组 var snakeBody = [];//食物数组var foods = [];//创建食物对象//food构造函数function Food() {this.x = parseInt(Math.random() * windowWidth);this.y = parseInt(Math.random() * windowHeight);this.r = parseInt(Math.random() * 10 + 10);this.color = "rgb(" + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + ")";//重新随机位置和颜色this.reset = function () {this.x = parseInt(Math.random() * windowWidth);this.y = parseInt(Math.random() * windowHeight);this.r = parseInt(Math.random() * 10 + 10);this.color = "rgb(" + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + ")";}}//创建食物对象数组function makeFoods() {for (var i = 0; i < 15; i++) {//push20个食物对象foods.push(new Food());}}

事件监听

在正式开始绘制之前,我们要确定snake的移动方向,这就需要我们根据玩家手指滑动的方向来确定当前是上滑动还是下滑动还是左滑动或是右滑动?我们可以这样来设置监听:

onTouchStart获取当前触摸点x,y坐标onTouchMove获取当前触摸点x坐标onTouchEnd发生时将方向改为相应的上下左右

wx.onTouchEnd(function (res) {snakeHead.snakeDirection = direction;})wx.onTouchMove(function (res) {moveX = res.changedTouches[0].clientX // 重新判断当前触摸点x坐标moveY = res.changedTouches[0].clientY // 重新判断当前触摸点y坐标dx = moveX - startX;dy = moveY - startY;if (Math.abs(dx) > Math.abs(dy)) {if (dx > 0) direction = "right";else if (dx < 0) direction = "left";}else if (Math.abs(dx) < Math.abs(dy)) {if (dy > 0) direction = "buttom";else if (dy < 0) direction = "top";}})wx.onTouchStart(function (res) {startX = res.changedTouches[0].clientX // 重新判断当前触摸点x坐标startY = res.changedTouches[0].clientY // 重新判断当前触摸点y坐标})

蛇头对象的移动

整个snake对象想要动起来我们就必须给snake定义一个animation函数,一帧一帧的移动位置。

蛇头对象在每一帧开始时根据方向来移动一个距离

function animation() {//开始绘制//判定方向switch (snakeHead.snakeDirection) {case 'top': snakeHead.y -= 1.8 * snakeHead.r; snakeHead.deg = -Math.PI / 2; break;case 'buttom': snakeHead.y += 1.8 * snakeHead.r; snakeHead.deg = Math.PI / 2; break;case 'left': snakeHead.x -= 1.8 * snakeHead.r; snakeHead.deg = Math.PI; break;case 'right': snakeHead.x += 1.8 * snakeHead.r; snakeHead.deg = 0; break;}ctx.save();ctx.clearRect(0, 0, windowWidth, windowHeight);ctx.fillStyle ="#8A2BE2";ctx.fillRect(0, 0, windowWidth, windowHeight);//绘制蛇头snakeHead.drawHead();}

蛇身的移动

蛇身对象的移动我们可以这样考虑:

蛇身是一个对象数组整个蛇第i节下一帧的位置就是他的上一个节点(i-1)的位置添加新的一节在蛇头的位置移除最后一节蛇身

这样一来蛇头向前移动,就像蛇头的位置添加一个新的蛇身节点,移除最后一节

function animation() {//添加新的身体snakeBody.push({x: snakeHead.x,y: snakeHead.y,r: snakeHead.r,color: "#708090"});//判定方向switch (snakeHead.snakeDirection) {case 'top': snakeHead.y -= 1.8 * snakeHead.r; snakeHead.deg = -Math.PI / 2; break;case 'buttom': snakeHead.y += 1.8 * snakeHead.r; snakeHead.deg = Math.PI / 2; break;case 'left': snakeHead.x -= 1.8 * snakeHead.r; snakeHead.deg = Math.PI; break;case 'right': snakeHead.x += 1.8 * snakeHead.r; snakeHead.deg = 0; break;}//绘制身体//没有碰撞食物则移除最后一节身体(下标为0,cover掉)//否则不做移除,长度增加,改isCollision为false表示未碰撞//绘制身体数组for(var i=0;i<snakeBody.length;i++){draw(snakeBody[i]);}//绘制蛇头snakeHead.drawHead();//绘制得分ctx.save();}

食物刷新

食物的刷新很简单,食物对象是一个数组,我们在每一帧发生时将食物根据当前位置绘制在画布上:

初始化时生成15个食物对象,随机位置,大小和颜色在每一帧发生时根据食物当前位置绘制在画布上

//创建食物对象//food构造函数function Food() {this.x = parseInt(Math.random() * windowWidth);this.y = parseInt(Math.random() * windowHeight);this.r = parseInt(Math.random() * 10 + 10);this.color = "rgb(" + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + ")";//重新随机位置和颜色this.reset = function () {this.x = parseInt(Math.random() * windowWidth);this.y = parseInt(Math.random() * windowHeight);this.r = parseInt(Math.random() * 10 + 10);this.color = "rgb(" + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + ")";}}//创建食物对象数组function makeFoods() {for (var i = 0; i < 15; i++) {//push20个食物对象foods.push(new Food());}}//获取屏幕宽高并且初始化食物对象,蛇身对象function window() {wx.getSystemInfo({success(res) {windowWidth = res.windowWidth;windowHeight = res.windowHeight;canvas.width = windowWidth;canvas.height = windowHeight;}});makeFoods();//初始化20个食物//绘制食物for (var i = 0; i < foods.length; i++) {draw(foods[i]);}snakeBody.push({x: snakeHead.x,y: snakeHead.y,r: snakeHead.r,color: "#708090"});}function animation() {//添加新的身体snakeBody.push({x: snakeHead.x,y: snakeHead.y,r: snakeHead.r,color: "#708090"});//判定方向switch (snakeHead.snakeDirection) {case 'top': snakeHead.y -= 1.8 * snakeHead.r; snakeHead.deg = -Math.PI / 2; break;case 'buttom': snakeHead.y += 1.8 * snakeHead.r; snakeHead.deg = Math.PI / 2; break;case 'left': snakeHead.x -= 1.8 * snakeHead.r; snakeHead.deg = Math.PI; break;case 'right': snakeHead.x += 1.8 * snakeHead.r; snakeHead.deg = 0; break;}//开始绘制//绘制食物ctx.save();ctx.clearRect(0, 0, windowWidth, windowHeight);ctx.fillStyle ="#8A2BE2";ctx.fillRect(0, 0, windowWidth, windowHeight);for (var i = 0; i < foods.length; i++) {draw(foods[i]);}//绘制身体//绘制身体数组for(var i=0;i<snakeBody.length;i++){draw(snakeBody[i]);}//绘制蛇头snakeHead.drawHead();}

碰撞检验

在蛇和食物可以顺利的刷新移动后,我们要给蛇设置碰撞检验,包括蛇和墙壁的检验,蛇和食物的检验

(1)检验触壁

检验小蛇和墙壁的触碰很简单,只要在animation函数中判断当前蛇头的位置是否在屏幕范围内,如果不在范围内说明触壁,游戏结束

//判定是否触壁function JudgeStared(){if(snakeHead.x<0||snakeHead.x>windowWidth||snakeHead.y<0||snakeHead.y>windowHeight) started=false;}

(2)检验碰撞食物

检验碰撞食物很简单,遍历整个食物数组,判断蛇头圆心和食物圆心的位置关系,如果距离大于半径和说明碰撞。需要注意的是碰撞后的连锁反应,蛇身增长,食物要随机刷新到另外一个位置,要点如下:

判断碰撞:(R+r)^2 <= (x1-x2)^2 + (y1-y2)^2蛇身增长:如果碰撞,那么我们不移除蛇尾碰撞随机刷新到另外一个位置分数point++设置变量iscollison来记录当前的碰撞状态

//动画函数function animation() {//添加新的身体snakeBody.push({x: snakeHead.x,y: snakeHead.y,r: snakeHead.r,color: "#708090"});//边界if (snakeHead.x > windowWidth) snakeHead.snakeDirection = "left";if (snakeHead.x <= 0) snakeHead.snakeDirection = "right";if (snakeHead.y <= 0) snakeHead.snakeDirection = "buttom";if (snakeHead.y > windowHeight) snakeHead.snakeDirection = "top";//判定方向switch (snakeHead.snakeDirection) {case 'top': snakeHead.y -= 1.8 * snakeHead.r; snakeHead.deg = -Math.PI / 2; break;case 'buttom': snakeHead.y += 1.8 * snakeHead.r; snakeHead.deg = Math.PI / 2; break;case 'left': snakeHead.x -= 1.8 * snakeHead.r; snakeHead.deg = Math.PI; break;case 'right': snakeHead.x += 1.8 * snakeHead.r; snakeHead.deg = 0; break;}//判定是否触壁JudgeStared();//检验碰撞,移动食物for (var i = 0; i < foods.length; i++) {isCollision = collision(snakeHead, foods[i]);//碰撞则重新绘制//发生碰撞if (isCollision) {foods[i].reset();break;}}//开始绘制//绘制食物ctx.save();ctx.clearRect(0, 0, windowWidth, windowHeight);ctx.fillStyle ="#8A2BE2";ctx.fillRect(0, 0, windowWidth, windowHeight);for (var i = 0; i < foods.length; i++) {draw(foods[i]);}//绘制身体//没有碰撞食物则移除最后一节身体(下标为0,cover掉)//否则不做移除,长度增加,改isCollision为false表示未碰撞if (!isCollision) {snakeBody.shift();}else {isCollision=false;}//绘制身体数组for(var i=0;i<snakeBody.length;i++){draw(snakeBody[i]);}//绘制蛇头snakeHead.drawHead();}

碰撞函数collision:

//碰撞函数,返回bolean值function collision(obj1, obj2) {var r1 = obj1.r, r2 = obj2.r;var dis = (obj1.x - obj2.x) * (obj1.x - obj2.x) + (obj1.y - obj2.y) * (obj1.y - obj2.y)if (dis < (r1 + r2) * (r1 + r2)) {audioEat.stop();audioEat.play();point++;return true;}else return false;}

绘制得分

得分绘制十分简单,插入ctx.fillText(‘当前得分:’ + point, 135, 220)即可

//绘制得分ctx.save();ctx.fillStyle ='#B0C4DE';ctx.font = "normal 20px 幼圆";ctx.fillText('当前得分:' + point, 135, 220);ctx.restore();if(!started) end();

开始界面和结束界面

现在我们的游戏大体上已经准备好了,但是直接进入游戏未免有些枯燥,我们需要一个开始界面和结束界面,当点击这个界面的时候游戏开始:

设置两个函数end和start分别表示结束界面和开始界面绘制这个开始或者结束界面给界面设置监听当点击屏幕时游戏开始,调用定时器,当碰撞到墙壁时清除定时器由于开始之后 wx.onTouchStart(function (res))仍然在执行,我们不能一直让监听执行调用定时器,所以我们用started记录当前游戏开始或结束的状态,仅仅在started=false时,可以调用定时器,在started=true时能够清除定时器

function end(){clearInterval(timer);//清除定时器audioDie.play();//死亡语音audioBackground.stop();//背景音乐ctx.save();ctx.drawImage(imageStart, 0, 0, canvas.width, canvas.height);ctx.beginPath();ctx.fillStyle = "#8A2BE2";ctx.fillRect(windowWidth * 5 / 7, 0, windowWidth * 2 / 7, windowHeight * 1 / 10);ctx.arc(canvas.width / 2, canvas.height / 2, 120, 0, Math.PI * 2, false);ctx.fillStyle = "#6495ED";ctx.fill();ctx.fillStyle = '#FF7F50';ctx.font = "normal 20px 幼圆";ctx.fillText('.游戏结束.', canvas.width / 2 - 50, canvas.height / 2 - 25);ctx.fillText('您的得分是'+point+"分", canvas.width / 2 - 60, canvas.height / 2);ctx.fillText('->点击屏幕重新开始<-', canvas.width / 2 - 100, canvas.height / 2 + 25);ctx.restore();//重置参数point=0;snakeHead.x=100;snakeHead.y=100;snakeHead.snakeDirection="right";direction='right';started=false;snakeBody.length=0;wx.onTouchStart(function (res) {if (!started) {start();}})}function start(){imageStart.onload = function () {ctx.clearRect(0,0,windowWidth,windowHeight);ctx.drawImage(imageStart, 0,0,canvas.width, canvas.height);ctx.beginPath();ctx.fillStyle ="#8A2BE2";ctx.fillRect(windowWidth * 5 / 7, 0, windowWidth * 2 / 7, windowHeight * 1 / 10);ctx.arc(canvas.width/2,canvas.height/2,120,0,Math.PI*2,false);ctx.fillStyle ="#6495ED";ctx.fill();ctx.fillStyle = '#FF7F50';ctx.font = "normal 20px 幼圆";ctx.fillText('<贪吃小虫>', canvas.width / 2-50, canvas.height / 2-30);ctx.fillText('->点击屏幕开始<-', canvas.width / 2 - 75, canvas.height / 2+20);audioBackground.loop = true;audioBackground.play();//调用定时器wx.onTouchStart(function (res) {if (!started) {audioStart.play();started = true;timer = setInterval(animation, 500);ctx.clearRect(0, 0, windowWidth, windowHeight);}})}

前台后台的切换

游戏界面有了,但是当切到后台时怎么办?从后台调到前台有如何处理?我们可以这样设置

设置stopped记录当前的路由的来源当stopped=true表示程序来自后台stopped=false表示直接打开程序在start函数中添加stopped状态,如果stopped=true直接调用定时器

function start(){imageStart.onload = function () {ctx.clearRect(0,0,windowWidth,windowHeight);ctx.drawImage(imageStart, 0,0,canvas.width, canvas.height);ctx.beginPath();ctx.fillStyle ="#8A2BE2";ctx.fillRect(windowWidth * 5 / 7, 0, windowWidth * 2 / 7, windowHeight * 1 / 10);ctx.arc(canvas.width/2,canvas.height/2,120,0,Math.PI*2,false);ctx.fillStyle ="#6495ED";ctx.fill();ctx.fillStyle = '#FF7F50';ctx.font = "normal 20px 幼圆";ctx.fillText('<贪吃小虫>', canvas.width / 2-50, canvas.height / 2-30);ctx.fillText('->点击屏幕开始<-', canvas.width / 2 - 75, canvas.height / 2+20);audioBackground.loop = true;audioBackground.play();//调用定时器if(stopped)//来源后台{audioStart.play();started = true;stopped=false;timer = setInterval(animation, 500);ctx.clearRect(0, 0, windowWidth, windowHeight);}else{wx.onTouchStart(function (res) {if (!started) {audioStart.play();started = true;timer = setInterval(animation, 500);ctx.clearRect(0, 0, windowWidth, windowHeight);}})}}}wx.onShow(function () {start();})wx.onHide(function(){clearInterval(timer);//清除定时器,暂停started=false;//停止运行stopped=true;//切后台})wx.showShareMenu({withShareTicket: true})

添加音频

此小游戏有4个音频,音频的设置往往能有不错的效果,要点如下

背景音频audioBackground:在小游戏运行在前台的调用获取食物音频audioEat:在碰撞函数collision中调用开始音频audioStart:在游戏开始时调用结束音频audioDie:在游戏结束时调用

var audioBackground = wx.createInnerAudioContext();//背景音乐audioBackground.src ="audio/background.mp3";var audioEat = wx.createInnerAudioContext();//吃的声音audioEat.src = "audio/eat.mp3";var audioDie = wx.createInnerAudioContext();//死亡声音audioDie.src = "audio/end.mp3";var audioStart = wx.createInnerAudioContext();//死亡声音audioStart.src = "audio/start.mp3";

展示

最后整个小游戏就完成了,展示如下

微信小游戏贪吃蛇

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。