还记得小时候玩过的经典贪食蛇小游戏吗,游戏规则简单,对新手来说很容易入门编程,在这里,TA远方来讲一下实现过程,详解过程按照实现思路来,请仔细往下看
💡 阅读此文章需要满足的以下条件
- 会使用微信开发工具,或者HBuilderX开发工具
- 属性Javascript编程语言
- 熟悉使用Canvas组件
- 这里TA远方使用的是微信开发工具,新建项目的时候,选择小程序,使用Javascript语言,选择空模板,最后点击完成,
若有项目源码,直接选择小程序再打开项目就即可
- 接下来,在项目中的路径pages/index/index.wxml文件里,写一下布局,用层叠布局方式,需要放置三个Canvas组件,分别是下层canvA,中间层canvB,上层canvC,上层的加上一些写属性,如下图
<view class="page"> <view class="canvas-box"> <canvas class="canvas" canvas-id="canvA" id="canvA"></canvas> <canvas class="canvas" canvas-id="canvB" id="canvB"></canvas> <canvas class="canvas" canvas-id="canvC" id="canvC" :disable-scroll="true" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd" bindtouchcancel="onTouchEnd"></canvas> </view> </view>
讯享网
- 接着在对应的index.js文件里写下代码,获取布局中的那三个Canvas组件,做好初始化,代码如下
讯享网App({
data: {
canvas: {
/*包括画布的相关数据*/}, //... }, onLoad(){
wx.createSelectorQuery().select('#canvA').fields({
size: true }, res => {
const canvA = wx.createCanvasContext('canvA'); const canvB = wx.createCanvasContext('canvB'); const canvC = wx.createCanvasContext('canvC'); //初始画布的相关数据 this.data.canvas = {
width: res.width, height: res.height, canvA, canvB, canvC }; this.canvA_drawBg(); this.canvC_drawSnake(); this.canvB_drawFood(); this.redraw(this.data.canvas); }).exec(); }, //...绘制地图...背景图...还有障碍物(现在把它叫做墙壁) canvA_drawBg(){
//... }, //...绘制蛇自身 canvC_drawSnake(){
//... }, //...绘制食物... canvB_drawFood(){
//... }, //...重绘...实现游戏刷新的逻辑 redraw(canvas){
//... }, })
- 接下来,用下层canvA组件的来实现绘制,应该绘制什么呢,是游戏地图,效果如下图所示,绘制网格

讯享网
- 那么如何绘制游戏地图呢,用
火眼金睛观察一下,游戏中的小蛇每移动一步的位置和距离都在变化,用网格来表示最合适不过了,绘制过程很简单,看下方法canvA_drawBg的实现,代码如下
App({
data: {
canvas: {
/*包括画布的相关数据*/}, config: {
gridSize: 10,//网格大小 drawGrid: false,//绘制网格 wellCount: 5,//墙壁数量 }, //... } / * 绘制地图...背景图...还有障碍物(现在把它叫做墙壁) */ canvA_drawBg(){
const {
width, height, canvA } = this.data.canvas;//画布宽高 const {
gridSize:g, drawGrid, wellCount } = this.data.config;//关于游戏的配置 //g是网格大小, wellCount障碍物数量 //此处省略...初始化处理 this.data.canvas.w = w;//网格宽 this.data.canvas.h = h;//网格高 this.data.canvas.l = l;//left 左边距 this.data.canvas.t = t;//top 顶边距 this.data.canvas.ln = ln;//x grid count 横向 X坐标网格数 this.data.canvas.tn = tn;//y grid count 纵向 y坐标网格数 //绘制背景色 canvA.fillRect(0,0,width,height); //所有坐标集合,包括制障碍物的 const coordinaties = []; for(let i=0; i<ln; i++){
//是否绘制网格,纵向绘制 if (drawGrid) {
//... } //是否绘制障碍物,判断数量大于0即可 if (wellCount>0) {
//... } } //是否绘制网格,横向绘制 if (drawGrid) {
for(let i=0; i<tn; i++){
//... } } else {
//...如果不绘制网格,就绘制边框 } if(coordinaties.length>0) {
//... do{
//绘制障碍物... }while(this.data.wall.length<wellCount);//绘制障碍物的数量判断 //... } canvA.draw(); }, })
- 接下来,绘制蛇自身,在网格中的位置,方法canvC_drawSnake的实现代码如下
讯享网const MoveDire = {
u: 'up', d: 'down', l: 'left', r: 'right', }; const Colors = {
bgColor: '#A4D1FC', fillColor: '#365DC0', }; App({
data: {
canvas: {
/*包括画布的相关数据*/}, config: {
gridSize: 10,//网格大小 snakeMinLength: 1,//初始蛇身长度 }, snakeDire: MoveDire.u,//移动方向,默认向上移 //... } / * 绘制蛇自身 * @param isReset 是否重置蛇身长 */ canvC_drawSnake(isReset){
const {
gridSize:g, snakeMinLength } = this.data.config;//关于游戏的配置 const {
w, h, canvC, l, t, ln, tn } = this.data.canvas; const {
snakeDire } = this.data; if (isReset || this.data.snake.length<=0) {
this.data.snake = [this.createCoordinate(parseInt(ln/2), parseInt(tn/2), snakeDire)]; for(let i=1; i<snakeMinLength; i++){
this.addSnakeLength(); } } canvC.setFillStyle(Colors.fillColor); this.snake.forEach(f => {
canvC.fillRect(f.x,f.y,g,g); }); canvC.draw(); }, //...创建坐标信息,传入坐标系a和b createCoordinate(a,b){
//... } //...加长蛇身 addSnakeLength(){
//... } })
- 接着,绘制食物,用一个位置来表示,方法canvB_drawFood的实现代码如下
App({
data: {
canvas: {
/*包括画布的相关数据*/}, config: {
gridSize: 10,//网格大小 }, food: null,//食物坐标 //... }, / * 绘制食物 * @param isNew 是否用新的 */ canvB_drawFood(isNew){
const {
gridSize:g } = this.data.config; const {
canvB } = this.data.canvas; let f = this.data.food; if (isNew || f==null) {
f = this.createRandomFood(); this.data.food = f; } canvB.setFillStyle(Colors.fillColor); canvB.fillRect(f.x,f.y,g,g); canvB.draw(); }, //...获取新的食物,食物放置位置随机 createRandomFood(){
//... } })
- 最后,实现重绘的逻辑方法redraw,刷新游戏视图,代码如下
讯享网const App = getApp(); const Util = App.getUtil(); App({
data: {
canvas: {
/*包括画布的相关数据*/}, config: {
speed: 5,//初始移动速度 1~5 }, food: null,//食物坐标 timer: null,//定时器 //... }, / * 实现重绘的逻辑,刷新游戏 * @param canvas 画布信息 */ redraw(canvas){
const {
speed } = this.data.config; const maxSpeed = 200;//记录最大值 const moveSpeed = Math.max(maxSpeed*Math.abs(speed-1) - this.data.snake.length*100, maxSpeed);//下一次刷新等待时长越短,移动速度越大 //定时器 this.data.timer = setTimeout(() => {
const {
food } = this.data; //判断是否显示食物,实现闪烁效果 if (food) {
let timeout = Date.now()-food.t; if (timeout>1000*30) {
//如果超过30s,则食物过期,应重新出现 this.canvB_drawFood(true); } else if (food.show) {
// food.t = Date.now(); food.show = false; this.canvB_drawFood();//绘制 } else if (!food.show) {
food.show = true; this.data.canvas.canvB.draw();//不绘制 } else {
} } //移动蛇身,返回判断是否继续游戏 let confirmGame = this.moveCoordinaties(); if (confirmGame) {
this.canvC_drawSnake(); //使用window下的方法,如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销 requestAnimationFrame(this.redraw); return; } //游戏结束了 this.endGame(true); //显示对话框 Util.alert('游戏结束!确认要重新开始吗?', res => {
if (!res.confirm) return; wx.reLaunch({
url:'/pages/index/index' }) }); }, moveSpeed); }, //...绘制蛇身 canvC_drawSnake(){
//... }, //...移动蛇身,处理蛇身新坐标,新的位置 moveCoordinaties(){
//...蛇头移动后 //...判断是否吃到食物,碰到自己 //...判断是否碰到墙(障碍物) //...最后移动没问题的话,返回true, 否则返回false return true; }, //...对游戏结束的处理,关闭定时器 endGame(isEnd){
//... }, onUnload(){
this.endGame(); } })
- 对了,还有,游戏的交互逻辑,就用触摸操作手势,实现代码如下
import Gesture from './../../utils/gesture.js'; App({
data: {
gestures: {
},//定义触摸操作的数据 snakeDire: MoveDire.u,//当前的移动方向 }, onTouchStart(event){
this.data.gestures.a = event.touches[0];//触摸开始点 }, onTouchMove(event){
this.data.gestures.b = event.touches[0];//最后一个触摸点 }, //触摸结束 onTouchEnd(){
const {
snakeDire: dire } = this.data; //Gesture是引入来的,封装了手势操作的处理 const Ges = Gesture.G; //获取手势操作方法getG const g = Gesture.getG(this.data.gestures); switch(g){
case Ges.left: //向左划 //判断,若是相反方向就无效,以下同理 if (dire!=MoveDire.r) {
this.snakeDire = MoveDire.l; } break; case Ges.up: //向上划 if (dire!=MoveDire.d) {
this.snakeDire = MoveDire.u; } break; case Ges.right: //向右划 if (dire!=MoveDire.l) {
this.snakeDire = MoveDire.r; } break; case Ges.down: //向下划 if (dire!=MoveDire.u) {
this.snakeDire = MoveDire.d; } break; case Ges.click: //只是点击 default: this.pauseGame(); } this.data.gestures = {
}; }, //...暂停,或继续游戏 pauseGame(){
//... } })
💡 小提示
关于Gesture触摸手势操作的详解文章请点击此处查看
- 就写到这里,其它的细节不重要,在这里就不讲,重要是实现思路清晰,最后,项目中小程序编译运行没问题的话,结果如下图,一个动态图,这样看着比较直观

如需要看微信小程序项目源码的,还有类似uniapp项目源码也有,
请点击【项目源码】前往找到再下载(如果是在手机浏览器上看的话,可能会找不到下载项,请用PC端浏览器上查看即可 ),
若觉得此文章有帮助,请点赞❤再走不迟~,谢谢!




版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/44993.html