如何利用uniapp开发一个贪吃蛇小游戏?下面本篇文章就手把手带大家在uniapp中实现贪吃蛇小游戏,希望对大家有所帮助!
第一次玩贪吃蛇还隐约记得是?️后父亲给我玩的第一个游戏
该小游戏使用uniapp
开发
前置详细内容就不细说了详细看:https://juejin.cn/post/7085727363547283469#heading-14
游戏演示
代码结构
详细代码结构如果需要请到github查看
主要分为:开始游戏、地块、蛇身、虫子、污染地块,游戏音效
<template> <view ref="body" class="content"> <view>蛇蛇目前:{{snakes.length}}米长</view> <view class="game-field"> <!-- 地面板块 --> <view class="block" v-for="(x, i) in blocks" :key="i"></view> </view> <view v-show="!started || ended" class="game-board-wrap"> <view v-show="!started" class="game-board"> <view class="title">选择游戏难度</view> <radio-group name="radio" @change="bindLevelChange"> <label class="label"> <radio value="1" :checked="level==1" /><text>简单模式</text> </label> <label class="label"> <radio value="2" :checked="level==2" /><text>正常模式</text> </label> <label class="label"> <radio value="3" :checked="level==3" /><text>困难模式</text> </label> <label class="label"> <radio value="4" :checked="level==4" /><text>地狱模式</text> </label> </radio-group> <button type="primary" @click="start">开始游戏</button> </view> <view v-show="ended" class="settle-board"> <view class="title">游戏结束</view> <view class="result">您的蛇蛇达到了{{snakes.length}}米</view> <view class="btns"> <button type="primary" @click="reStart">再次挑战</button> <button type="primary" plain @click="rePick">重选难度</button> </view> </view> </view> </view> </template> <script> export default { data() { return { blocks: [], // 板块 worms: [], // 虫子 snakes: [0, 1, 2, 3], // 蛇身 direction: "right", // 蛇移动方向 }; }, onLoad() { this.initGame(); }, methods: { initGame() { this.blocks = new Array(100).fill(0); // 生成100个地面板块 this.worms = [Math.floor(Math.random() * 96) + 4]; // 随机生成虫子 this.snakes = [0, 1, 2, 3]; // 初始化蛇身位置 } } } </script>
渲染蛇身
给我们的蛇穿上他的外衣 蛇身的渲染根据snakes
(里边放着蛇的身体)来匹配地面板块的索引 从而找到对应的格格并修改背景图来渲染蛇身 蛇头和蛇尾就是取snakes
第0位和最后一位 并找到对应的格格修改当前背景图
<template> <view class="game-field"> <view class="block" :style="`background-image: ${bg(x, i)}" v-for="(x, i) in blocks" :key="i"> </view> </view> </template> <script> import worm from "worm.png"; import snakeBody from "snake_body.png"; import snakeHead from "snake_head.png"; import snakeTail from "snake_tail.png"; import polluteBlock from "pollute.png"; import wormBoom from "worm_4.png"; export default { methods: { bg(type, index) { let bg = ""; switch (type) { case 0: // 地板 bg = "unset"; break; case 1: // 虫子 if (this.boom) { bg = `url(${wormBoom})`; } else { bg = `url(${worm})`; } break; case 2: // 蛇 let head = this.snakes[this.snakes.length - 1]; let tail = this.snakes[0]; if (index === head) { bg = `url(${snakeHead})`; } else if (index === tail) { bg = `url(${snakeTail})`; } else { bg = `url(${snakeBody})`; } break; case 3: // 污染的地块 bg = `url(${polluteBlock})`; break; } return bg; }, } } </scipt>
控制蛇的方向
控制蛇的方向pc端我们通过监听键盘事件找到对应的键盘键的编码上下左右来改变蛇的方向 而手机端我们通过touch时间手指触摸点及滑动点的XY轴值来判断蛇的方向
<template> <view ref="body" class="content" @keyup.left="bindLeft" @keyup.right="bindRight" @keyup.down="bindDown" @keyup.up="bindUp" @touchstart="handleTouchStart" @touchmove="handleTouchMove"> <view>蛇蛇目前:{{snakes.length}}米长</view> <view class="game-field"> <view class="block" :style="`background-image: ${bg(x, i)}; v-for="(x, i) in blocks" :key="i"></view> </view> </view> </template> <script> export default { data(){ return { direction: "right", started: false, // 游戏开始了 ended: false, // 游戏结束了 level: 1, // 游戏难度 lastX: 0, lastY: 0, } }, onLoad() { this.initGame(); }, methods:{ initGame() { this.blocks = new Array(100).fill(0); // 生成100个地面板块 this.worms = [Math.floor(Math.random() * 96) + 4]; // 随机生成虫子 this.snakes = [0, 1, 2, 3]; // 初始化蛇身位置 document.onkeydown = (e) => { switch (e.keyCode) { // 获取当前按下键盘键的编码 case 37: // 按下左箭头键 this.bindLeft(); break; case 39: // 按下右箭头键 this.bindRight(); break; case 38: // 按下上箭头键 if (!this.started) { this.level--; } else { this.bindUp(); } break; case 40: // 按下下箭头键 if (!this.started) { this.level++; } else { this.bindDown(); } break; } } }, handleTouchStart(e) { // 手指开始位置 this.lastX = e.touches[0].pageX; this.lastY = e.touches[0].pageY; }, handleTouchMove(e) { let lastX = e.touches[0].pageX; // 移动的x轴坐标 let lastY = e.touches[0].pageY; // 移动的y轴坐标 let touchX = lastX - this.lastX; let touchY = lastY - this.lastY if (Math.abs(touchX) > Math.abs(touchY)) { if (touchX < 0) { if(this.direction === "right") return; this.direction = 'left' } else if (touchX > 0) { if(this.direction === "left") return; this.direction = 'right' } } else { if (touchY < 0) { if(this.direction === "down") return; this.direction = 'up' } else if (touchY > 0) { if(this.direction === "up") return; this.direction = 'down' } } this.lastX = lastX; this.lastY = lastY; }, bindUp() { if (this.direction === "down") return; this.direction = "up"; }, bindDown() { if (this.direction === "up") return; this.direction = "down"; }, bindLeft() { if (this.direction === "right") return; this.direction = "left"; }, bindRight() { if (this.direction === "left") return; this.direction = "right"; }, } } </script>
给贪吃蛇添加音效
添加游戏音效游戏代入感就强了很多 现在我们要给蛇加上背景音乐、点击交互音乐、蛇隔儿屁的音乐、蛇吃掉食物的音乐、虫子爆炸倒计时的音乐和虫子爆炸的音乐
先给添加上背景音乐 总有刁民可以玩到地图满为止 背景音乐的话要loop播放 我们只需要 使用uni.createInnerAudioContext
来创建并返回内部 audio 上下文 innerAudioContext
对象 拿到音乐的路径并且设置自动播放
<script> import bgm from 'bgm.mp3'; export default { data(){ return { bgmInnerAudioContext:null, } }, methods:{ start() { // 开始游戏 this.initGame(); this.handleBgmVoice() }, handleBgmVoice() { // 背景音乐 this.bgmInnerAudioContext = uni.createInnerAudioContext() // 创建上下文 this.bgmInnerAudioContext.autoplay = true; // 自动播放 this.bgmInnerAudioContext.src= bgm; // 音频地址 this.bgmInnerAudioContext.loop = true; // 循环播放 } } } <script>
背景音乐确实响起来了 蛇gameover后还一直响 顿时我听着就不耐烦 这时我们在蛇gameover后暂停背景音乐pause
音乐会暂停而不会清楚
<script> import bgm from 'bgm.mp3'; export default { data(){ return { bgmInnerAudioContext:null, } }, methods:{ start() { // 开始游戏 this.initGame(); this.handleBgmVoice() }, handleBgmVoice() { // 背景音乐 this.bgmInnerAudioContext = uni.createInnerAudioContext() // 创建上下文 this.bgmInnerAudioContext.autoplay = true; // 自动播放 this.bgmInnerAudioContext.src= bgm; // 音频地址 this.bgmInnerAudioContext.loop = true; // 循环播放 } checkGame(direction, next) { let gameover = false; let isSnake = this.snakes.indexOf(next) > -1; let isPollute = this.pollutes.indexOf(next) > -1; // 撞到蛇和被污染的地块游戏结束 if (isSnake || isPollute) { gameover = true; } // 撞到边界游戏结束 switch (direction) { case "up": if (next < 0) { gameover = true; } break; case "down": if (next >= 100) { gameover = true; } break; case "left": if (next % 10 === 9) { gameover = true; } break; case "right": if (next % 10 === 0) { gameover = true; } break; } return gameover; }, toWards(direction) { let gameover = this.checkGame(direction, next); if (gameover) { this.ended = true; this.handleDieVoice() this.bgmInnerAudioContext.pause() // 游戏结束 暂停背景音乐 clearInterval(this.timer); clearInterval(this.boomTimer); } else { // 游戏没结束 this.snakes.push(next); let nextType = this.blocks[next]; this.blocks[next] = 2; // 如果是空白格 if (nextType === 0) { this.snakes.shift(); } else { // 如果是虫子格 this.handleEatVoice() // 吃掉虫子后的音乐 this.worms = this.worms.filter((x) => x !== next); let nextWorm = this.createWorm(); this.worms.push(nextWorm); } this.blocks[tail] = 0; this.paint(); } }, } } <script>
首个音乐添加成功其他的也就简单多了 虫子爆炸倒计时也需要爆炸或者gameover后需要清楚倒计时音效stop
(下次播放会从头开始) 剩余的不需要清楚音效和循环播放 下面附上剩余的代码
<script> export default { data() { return { bgmInnerAudioContext:null, clockInnerAudioContext:null, } }, watch: { boomCount(val) { if (val === 0) { // 超过爆炸时间还没吃到,则将虫子格子变成被污染的土地,并且重置爆炸状态,同时生成一只新的虫子: this.handleExplodeVoice() // 爆炸的音乐 this.clockInnerAudioContext.stop() // 清楚倒计时音乐 const boomWorm = this.worms.pop(); this.pollutes.push(boomWorm); this.blocks[boomWorm] = 3; // 被污染的地方我们用3表示 this.boom = false; this.worms.push(this.createWorm()); } } }, methods:{ // 蛇吃到食物后的声音 handleEatVoice() { const innerAudioContext = uni.createInnerAudioContext(); innerAudioContext.autoplay = true; innerAudioContext.src = eatVoice; }, // 虫子污染爆炸后的声音 handleExplodeVoice(){ const innerAudioContext = uni.createInnerAudioContext(); innerAudioContext.autoplay = true; innerAudioContext.src = explodeVoice; }, // 游戏背景音乐 handleBgmVoice() { this.bgmInnerAudioContext = uni.createInnerAudioContext() this.bgmInnerAudioContext.autoplay = true; this.bgmInnerAudioContext.src= bgm; this.bgmInnerAudioContext.loop = true; }, // 按钮点击的声音 handleClickVoice() { const innerAudioContext = uni.createInnerAudioContext() innerAudioContext.autoplay = true; innerAudioContext.src= click; }, // 爆炸倒计时的声音 handleClockVoice() { this.clockInnerAudioContext = uni.createInnerAudioContext() this.clockInnerAudioContext.autoplay = true; this.clockInnerAudioContext.src= clock; }, // 蛇gameover后的声音 handleDieVoice() { const innerAudioContext = uni.createInnerAudioContext() innerAudioContext.autoplay = true; innerAudioContext.src= die; }, checkGame(direction, next) { let gameover = false; let isSnake = this.snakes.indexOf(next) > -1; let isPollute = this.pollutes.indexOf(next) > -1; // 撞到蛇和被污染的地块游戏结束 if (isSnake || isPollute) { gameover = true; } // 撞到边界游戏结束 switch (direction) { case "up": if (next < 0) { gameover = true; } break; case "down": if (next >= 100) { gameover = true; } break; case "left": if (next % 10 === 9) { gameover = true; } break; case "right": if (next % 10 === 0) { gameover = true; } break; } return gameover; }, paint() { this.worms.forEach((x) => { this.blocks[x] = 1; }); this.snakes.forEach((x) => { this.blocks[x] = 2; }); this.$forceUpdate(); }, toWards(direction) { let gameover = this.checkGame(direction, next); if (gameover) { this.ended = true; this.handleDieVoice() this.bgmInnerAudioContext.pause() // 游戏结束 暂停背景音乐 this.clockInnerAudioContext && this.clockInnerAudioContext.stop() // 清楚倒计时音乐 clearInterval(this.timer); clearInterval(this.boomTimer); } else { // 游戏没结束 this.snakes.push(next); let nextType = this.blocks[next]; this.blocks[next] = 2; // 如果是空白格 if (nextType === 0) { this.snakes.shift(); } else { // 如果是虫子格 this.handleEatVoice() // 吃掉虫子后的音乐 this.worms = this.worms.filter((x) => x !== next); let nextWorm = this.createWorm(); this.worms.push(nextWorm); } this.blocks[tail] = 0; this.paint(); } }, // 生成下一只虫子 createWorm() { this.boom = false; let blocks = Array.from({ length: 100 }, (v, k) => k); // 在不是蛇和被污染的地方生成虫子 let restBlocks = blocks.filter(x => this.snakes.indexOf(x) < 0 && this.pollutes.indexOf(x) < 0); let worm = restBlocks[Math.floor(Math.random() * restBlocks.length)]; // 根据游戏难度,概率产出会爆炸的虫子: this.boom = Math.random() / this.level < 0.05; // 生成了新虫子说明吃到了那个爆炸的虫子,重置下爆炸 if (this.boom) { this.boomCount = 10; this.boomTimer && clearInterval(this.boomTimer); this.handleClockVoice() this.boomTimer = setInterval(() => { this.boomCount--; }, 1000) } else { this.clockInnerAudioContext && this.clockInnerAudioContext.stop() clearInterval(this.boomTimer); } return worm; }, } } <script>
结语
特别鸣谢@大帅老猿@末世未然 感谢大帅老师的带队及指导以及每天的督促,也特别的感谢队友的帮助及支持
源码地址:https://github.com/MothWillion/snake_eat_worm
原文地址:https://juejin.cn/post/7087525655478272008
作者:Sophora
推荐:《uniapp教程》