还记得小时候玩过的经典贪食蛇小游戏吗,游戏规则简单,对新手来说很容易入门编程,在这里,TA远方来讲一下实现过程,详解过程按照实现思路来,请仔细往下看
阅读此文章需要满足的以下条件
- 会使用微信开发工具,或者HBuilderX开发工具
- 属性Javascript编程语言
- 熟悉使用Canvas组件
<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>
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_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();
},
})
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(){
//...
}
})
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(){
//...
}
})
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端浏览器上查看即可 ),
若觉得此文章有帮助,请点赞❤
再走不迟~,谢谢!