解决方案

微信小游戏——贪吃蛇

seo靠我 2023-09-25 12:48:59

博客简介

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

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

微信小游戏贪吃蛇

案例简介

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

布局

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

构建对象

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

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

事件监听

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

onTouchStart获取当前触摸点x,y坐标onTouchMove获取当前触摸点x坐标onTouchEnd发生时将方向改为相应的上下左右 wx.onTouchEnd(funSEO靠我ction (res) {snakeHead.snakeDirection = direction; })wx.onTouchMove(function (res) {moveX = SEO靠我res.changedTouches[0].clientX // 重新判断当前触摸点x坐标moveY = res.changedTouches[0].clientY // 重新判断当前触摸点y坐标dxSEO靠我 = moveX - startX;dy = moveY - startY;if (Math.abs(dx) > Math.abs(dy)) {if (dx > 0) direction = "rigSEO靠我ht";else if (dx < 0) direction = "left";}else if (Math.abs(dx) < Math.abs(dy)) {if (dy > 0) directioSEO靠我n = "buttom";else if (dy < 0) direction = "top";} })wx.onTouchStart(function (res) {startX =SEO靠我 res.changedTouches[0].clientX // 重新判断当前触摸点x坐标startY = res.changedTouches[0].clientY // 重新判断当前触摸点y坐标SEO靠我 })

蛇头对象的移动

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

蛇头对象在每一帧开始时根据方向来移动一个距离 SEO靠我function animation() {//开始绘制//判定方向switch (snakeHead.snakeDirection) {case top: snakeHead.y -= 1.8 * SEO靠我snakeHead.r; snakeHead.deg = -Math.PI / 2; break;case buttom: snakeHead.y += 1.8 * snakeHead.r; snakSEO靠我eHead.deg = Math.PI / 2; break;case left: snakeHead.x -= 1.8 * snakeHead.r; snakeHead.deg = Math.PI;SEO靠我 break;case right: snakeHead.x += 1.8 * snakeHead.r; snakeHead.deg = 0; break;}ctx.save();ctx.clearRSEO靠我ect(0, 0, windowWidth, windowHeight);ctx.fillStyle ="#8A2BE2";ctx.fillRect(0, 0, windowWidth, windowSEO靠我Height);//绘制蛇头snakeHead.drawHead(); }

蛇身的移动

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

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

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

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

食物刷新

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

初始化时生成15个食物对象,随机位置,大小和颜色在每一帧发生时根据食物当前位置绘制SEO靠我在画布上 //创建食物对象 //food构造函数 function Food() {this.x = parseInt(Math.random() * SEO靠我windowWidth);this.y = parseInt(Math.random() * windowHeight);this.r = parseInt(Math.random() * 10 + SEO靠我10);this.color = "rgb(" + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + "," SEO靠我+ parseInt(Math.random() * 255) + ")";//重新随机位置和颜色this.reset = function () {this.x = parseInt(Math.raSEO靠我ndom() * windowWidth);this.y = parseInt(Math.random() * windowHeight);this.r = parseInt(Math.random(SEO靠我) * 10 + 10);this.color = "rgb(" + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 25SEO靠我5) + "," + parseInt(Math.random() * 255) + ")";} } //创建食物对象数组 function makeFSEO靠我oods() {for (var i = 0; i < 15; i++) {//push20个食物对象foods.push(new Food());} }//获取屏幕宽高并且初始化食物SEO靠我对象,蛇身对象 function window() {wx.getSystemInfo({success(res) {windowWidth = res.windowWidth;winSEO靠我dowHeight = res.windowHeight;canvas.width = windowWidth;canvas.height = windowHeight;}});makeFoods()SEO靠我;//初始化20个食物//绘制食物for (var i = 0; i < foods.length; i++) {draw(foods[i]);}snakeBody.push({x: snakeHeaSEO靠我d.x,y: snakeHead.y,r: snakeHead.r,color: "#708090"}); } function animation() {//添加新的SEO靠我身体snakeBody.push({x: snakeHead.x,y: snakeHead.y,r: snakeHead.r,color: "#708090"});//判定方向switch (snakSEO靠我eHead.snakeDirection) {case top: snakeHead.y -= 1.8 * snakeHead.r; snakeHead.deg = -Math.PI / 2; breSEO靠我ak;case buttom: snakeHead.y += 1.8 * snakeHead.r; snakeHead.deg = Math.PI / 2; break;case left: snakSEO靠我eHead.x -= 1.8 * snakeHead.r; snakeHead.deg = Math.PI; break;case right: snakeHead.x += 1.8 * snakeHSEO靠我ead.r; snakeHead.deg = 0; break;}//开始绘制//绘制食物ctx.save();ctx.clearRect(0, 0, windowWidth, windowHeighSEO靠我t);ctx.fillStyle ="#8A2BE2";ctx.fillRect(0, 0, windowWidth, windowHeight);for (var i = 0; i < foods.SEO靠我length; i++) {draw(foods[i]);}//绘制身体//绘制身体数组for(var i=0;i<snakeBody.length;i++){draw(snakeBody[i]);}SEO靠我//绘制蛇头snakeHead.drawHead(); }

碰撞检验

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

(1)检验触壁

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

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

(2)检验碰撞食物

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

判断碰撞:(R+r)^2 <= (x1-x2)^2 + (y1-y2)^2蛇身增长:如果碰撞,那么我们不移除蛇尾碰撞随机刷新到另外一个位置分数point++设置变量iscollison来记录当前SEO靠我的碰撞状态 //动画函数 function animation() {//添加新的身体snakeBody.push({x: snakeHead.x,y: snakeHeSEO靠我ad.y,r: snakeHead.r,color: "#708090"});//边界if (snakeHead.x > windowWidth) snakeHead.snakeDirection =SEO靠我 "left";if (snakeHead.x <= 0) snakeHead.snakeDirection = "right";if (snakeHead.y <= 0) snakeHead.snaSEO靠我keDirection = "buttom";if (snakeHead.y > windowHeight) snakeHead.snakeDirection = "top";//判定方向switchSEO靠我 (snakeHead.snakeDirection) {case top: snakeHead.y -= 1.8 * snakeHead.r; snakeHead.deg = -Math.PI / SEO靠我2; break;case buttom: snakeHead.y += 1.8 * snakeHead.r; snakeHead.deg = Math.PI / 2; break;case leftSEO靠我: snakeHead.x -= 1.8 * snakeHead.r; snakeHead.deg = Math.PI; break;case right: snakeHead.x += 1.8 * SEO靠我snakeHead.r; snakeHead.deg = 0; break;}//判定是否触壁JudgeStared();//检验碰撞,移动食物for (var i = 0; i < foods.leSEO靠我ngth; i++) {isCollision = collision(snakeHead, foods[i]);//碰撞则重新绘制//发生碰撞if (isCollision) {foods[i].rSEO靠我eset();break;}}//开始绘制//绘制食物ctx.save();ctx.clearRect(0, 0, windowWidth, windowHeight);ctx.fillStyle =SEO靠我"#8A2BE2";ctx.fillRect(0, 0, windowWidth, windowHeight);for (var i = 0; i < foods.length; i++) {drawSEO靠我(foods[i]);}//绘制身体//没有碰撞食物则移除最后一节身体(下标为0,cover掉)//否则不做移除,长度增加,改isCollision为false表示未碰撞if (!isCollisioSEO靠我n) {snakeBody.shift();}else {isCollision=false;}//绘制身体数组for(var i=0;i<snakeBody.length;i++){draw(snaSEO靠我keBody[i]);}//绘制蛇头snakeHead.drawHead(); }

碰撞函数collision:

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

绘制得分

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

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

开始界面和结束界面

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

设置两个函数end和start分别表示结束界面和开始界面绘制这个开始或者结束界面给界面设置监听当点击屏幕时游戏开始,调用定时器,当碰撞到墙壁时清除定时器由于开始之后SEO靠我 wx.onTouchStart(function (res))仍然在执行,我们不能一直让监听执行调用定时器,所以我们用started记录当前游戏开始或结束的状态,仅仅在started=false时,SEO靠我可以调用定时器,在started=true时能够清除定时器 function end() {clearInterval(timer);//清除定时器audioDie.pSEO靠我lay();//死亡语音audioBackground.stop();//背景音乐ctx.save();ctx.drawImage(imageStart, 0, 0, canvas.width, caSEO靠我nvas.height);ctx.beginPath();ctx.fillStyle = "#8A2BE2";ctx.fillRect(windowWidth * 5 / 7, 0, windowWiSEO靠我dth * 2 / 7, windowHeight * 1 / 10);ctx.arc(canvas.width / 2, canvas.height / 2, 120, 0, Math.PI * 2SEO靠我, false);ctx.fillStyle = "#6495ED";ctx.fill();ctx.fillStyle = #FF7F50;ctx.font = "normal 20px 幼圆";ctSEO靠我x.fillText(.游戏结束., canvas.width / 2 - 50, canvas.height / 2 - 25);ctx.fillText(您的得分是+point+"分", canvSEO靠我as.width / 2 - 60, canvas.height / 2);ctx.fillText(->点击屏幕重新开始<-, canvas.width / 2 - 100, canvas.heigSEO靠我ht / 2 + 25);ctx.restore();//重置参数point=0;snakeHead.x=100;snakeHead.y=100;snakeHead.snakeDirection="rSEO靠我ight";direction=right;started=false;snakeBody.length=0;wx.onTouchStart(function (res) {if (!started)SEO靠我 {start();}}) } function start() {imageStart.onload = function () {ctx.clearSEO靠我Rect(0,0,windowWidth,windowHeight);ctx.drawImage(imageStart, 0,0,canvas.width, canvas.height);ctx.beSEO靠我ginPath();ctx.fillStyle ="#8A2BE2";ctx.fillRect(windowWidth * 5 / 7, 0, windowWidth * 2 / 7, windowHSEO靠我eight * 1 / 10);ctx.arc(canvas.width/2,canvas.height/2,120,0,Math.PI*2,false);ctx.fillStyle ="#6495ESEO靠我D";ctx.fill();ctx.fillStyle = #FF7F50;ctx.font = "normal 20px 幼圆";ctx.fillText(<贪吃小虫>, canvas.width SEO靠我/ 2-50, canvas.height / 2-30);ctx.fillText(->点击屏幕开始<-, canvas.width / 2 - 75, canvas.height / 2+20);SEO靠我audioBackground.loop = true;audioBackground.play();//调用定时器wx.onTouchStart(function (res) {if (!startSEO靠我ed) {audioStart.play();started = true;timer = setInterval(animation, 500);ctx.clearRect(0, 0, windowSEO靠我Width, windowHeight);}}) }

前台后台的切换

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

设置stopped记录当前的路由的来源SEO靠我当stopped=true表示程序来自后台stopped=false表示直接打开程序在start函数中添加stopped状态,如果stopped=true直接调用定时器 functioSEO靠我n start() {imageStart.onload = function () {ctx.clearRect(0,0,windowWidth,windowHeight);ctx.SEO靠我drawImage(imageStart, 0,0,canvas.width, canvas.height);ctx.beginPath();ctx.fillStyle ="#8A2BE2";ctx.SEO靠我fillRect(windowWidth * 5 / 7, 0, windowWidth * 2 / 7, windowHeight * 1 / 10);ctx.arc(canvas.width/2,SEO靠我canvas.height/2,120,0,Math.PI*2,false);ctx.fillStyle ="#6495ED";ctx.fill();ctx.fillStyle = #FF7F50;cSEO靠我tx.font = "normal 20px 幼圆";ctx.fillText(<贪吃小虫>, canvas.width / 2-50, canvas.height / 2-30);ctx.fillTSEO靠我ext(->点击屏幕开始<-, canvas.width / 2 - 75, canvas.height / 2+20);audioBackground.loop = true;audioBackgrSEO靠我ound.play();//调用定时器if(stopped)//来源后台{audioStart.play();started = true;stopped=false;timer = setInterSEO靠我val(animation, 500);ctx.clearRect(0, 0, windowWidth, windowHeight);}else{wx.onTouchStart(function (rSEO靠我es) {if (!started) {audioStart.play();started = true;timer = setInterval(animation, 500);ctx.clearReSEO靠我ct(0, 0, windowWidth, windowHeight);}})}} }wx.onShow(function () {start(); })wx.onHiSEO靠我de(function(){clearInterval(timer);//清除定时器,暂停started=false;//停止运行stopped=true;//切后台 }) SEO靠我 wx.showShareMenu({withShareTicket: true })

添加音频

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

背景音频audioBackgSEO靠我round:在小游戏运行在前台的调用获取食物音频audioEat:在碰撞函数collision中调用开始音频audioStart:在游戏开始时调用结束音频audioDie:在游戏结束时调用 SEO靠我 var audioBackground = wx.createInnerAudioContext();//背景音乐 audioBackground.src ="audio/bacSEO靠我kground.mp3"; var audioEat = wx.createInnerAudioContext();//吃的声音 audioEat.src = "audSEO靠我io/eat.mp3"; var audioDie = wx.createInnerAudioContext();//死亡声音 audioDie.src = "audiSEO靠我o/end.mp3"; var audioStart = wx.createInnerAudioContext();//死亡声音 audioStart.src = "aSEO靠我udio/start.mp3";

展示

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

微信小游戏贪吃蛇

“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2