大家好!我又来了。今天我又写了一个超级无聊的小游戏
在无聊的道路上,渐行渐远。还记得儿时我们喜欢玩的这个游戏吗?
由于我申请的小程序不是小游戏,所以无法体验;
于是我又写成H5页面:http://www.yating.online/brick.html
喜欢就给我点个星吧:https://github.com/Chenyating/easyGame
一、简单介绍:简单的介绍一下我的小游戏:
通过红色小块的反弹,吃掉白色小块,长条需接住反弹回来的红色小块;
操作说明:
1、点击界面的空白处;
游戏规则:
1、红色小块开始从白色长条出发,周而复始随机撞击小白块;
2、红色小块回到底部时,必须在白色长条上;
结束条件:
红色小块落地;(吃完小白块需要猴年马月)
二、技术难点
- 小程序的canvas的使用;
- 本次开发的是小程序;
通过开发的过程,该游戏最大的难点如下:
1、小红运动的方式角度
2、小长条运动的方法:
三、实现步骤:
接下来我开始详细说明这个游戏设计过程,实现的逻辑;
红色小块简称:小红(red),
白色长条简称:小长(slider),
小白块简称:小白;
以下为我设置的变量:
data: {
// 画布配置
canvasWidth: 310,
canvasHeight: 500,
canvasCenterX: 0,
// 整块画布
context: null,
// 滑块配置
sliderX: 125,
sliderY: 490,
sliderWidth: 100, //滑块宽度
sliderHeight: 10, //滑块高度
sliderCenter: null, //滑块中心
sliderSpeed: 1, //滑块速度
// 滑块移动到的位置
mouseX: null,
position: 1, //1为右,-1为左
redraw: null, //滑块移动的循环作用
// 砖头们的设置
blocksContext: null,
blocks: null,
blocksWidth: 9,
// 小红砖
redBlock: null,
redX: 150,
redY: 480,
redK: -1,
redB: null,
redM: 1,
rered: null,
redSpeed: 5,
// 分值记录
fail: 0,
score: 0
},
一、绘制
1、小长的绘制:
// 绘制滑块
drawSlider(x) {
var bu = this.data.context
bu.setFillStyle('#ffffff');
bu.fillRect(x, this.data.sliderY, this.data.sliderWidth, this.data.sliderHeight);
bu.draw();
},
需要获取鼠标点击的位置,鼠标点击的位置相对于滑块中心点的方位,这里position为在x轴上移动的距离;
//获取鼠标X的位置
sliderDot(e) {
this.setData({
mouseX: e.detail.x - (this.data.sliderWidth / 2)
})
// 判断鼠标点击的滑块的位置相对于滑块的方向
if (this.data.mouseX >= this.data.sliderCenter) {
// 右边
this.setData({
position: 1
})
this.moveSlider(this.data.sliderX, this.data.position)
} else {
// 左边
this.setData({
position: -1
})
this.moveSlider(this.data.sliderX, this.data.position)
}
},
接着我们开始写,小长在底部移动的方法;
1、移动过程中,如果小长碰壁了,那么小长就停止移动,即使小长的中心没有到达鼠标点;
2、小长没有碰壁,则它朝指定位置移动,直到到达该指定位置;
// 重新绘制滑块
moveSlider(sliderX, x) {
var that = this;
// 移动循环
clearTimeout(this.data.redraw);
// 清除原来
// var bu = this.data.context;
// bu.clearRect(sliderX, this.data.sliderY, this.data.sliderWidth, this.data.sliderHeight)
// 同步更新数据
this.setData({
sliderX: sliderX + x
})
// 重绘
this.drawSlider(sliderX + x);
// 条件判断:每次移动的时候先判断是否碰到墙壁
this.setData({
sliderCenter: that.data.sliderX + 20
})
if (this.data.sliderX >= (this.data.canvasWidth - this.data.sliderWidth) || this.data.sliderX <= 0) {
// 如果碰到壁就停止
clearTimeout(this.data.redraw);
return;
} else {
// 判断一下滑块是否到鼠标点击的位置
if (this.data.sliderCenter == this.data.mouseX) {
// 如果到达鼠标位置,就结束;
clearTimeout(this.data.redraw);
return;
} else {
//否则就继续移动
this.data.redraw = setTimeout(() => {
this.moveSlider(sliderX + x, x);
}, this.data.sliderSpeed);
return;
}
}
},
2、绘制小白块
我用一个数组存放小白块的上色情况
// 初始化砖头们的值,
blocksArray() {
var that = this;
for (var i = 0; i < (that.data.canvasWidth / 10); i++) {
for (var j = 0; j < (that.data.canvasHeight / 10); j++) {
let blocks0 = 'blocks[' + i + '][' + j + '][0]'; //横坐标
let blocks1 = 'blocks[' + i + '][' + j + '][1]'; //纵坐标
let blocks2 = 'blocks[' + i + '][' + j + '][2]'; //上色与否 0,1
// 10行以内有颜色
if (j < 30) {
var color = 1;
} else {
// 否则无颜色
var color = 0;
}
that.setData({
[blocks0]: i * 10,
[blocks1]: j * 10,
[blocks2]: color
})
}
}
},
绘制小白块们
// 已知砖头数组,绘制砖头
drawBlocks() {
var that = this;
var context = that.data.blocksContext;
// 绘制众多小方块
context.setFillStyle('#ffffff');
for (var i = 0; i < that.data.blocks.length; i++) {
for (var j = 0; j < that.data.blocks[i].length; j++) {
// 绘制方块
if (that.data.blocks[i][j][2] == 1) {
context.fillRect(that.data.blocks[i][j][0], that.data.blocks[i][j][1], this.data.blocksWidth, this.data.blocksWidth)
} else {
continue;
}
}
}
3、绘制小红
// 绘制小红
drawRed(x, y) {
var bu = this.data.redBlock
bu.rect(x, y, this.data.blocksWidth, this.data.blocksWidth)
bu.setFillStyle('red')
bu.fill()
bu.draw()
},
二、小红的运动轨迹
1、计算小红的运动函数:y=kx+b
getLine(k, m) {
this.data.redB = this.data.redY - k * this.data.redX;
// console.log("y=" + k + "*x+" + this.data.redB, m)
this.moveRed(k, this.data.redB, m, this.data.redX, this.data.redY)
},
小红的运动轨迹为一条直线,小红的移动便就是随着这条直线运动
2、写小红移动的方法
小红在每次移动的过程中,都要不断的判断校验小红自身的状态是否还属于游戏规则之内:
简单的我用一个流程图来说明小红移动过程中的校验流程:
moveRed(k, b, m, x, y) {
// 移动循环
clearTimeout(this.data.rered);
// 1、判断小红是否碰到左右壁;
if ((x + m) == this.data.canvasWidth || (x + m) == -10) {
// 左右反弹
this.turnLeftRight(k, m);
return;
}
// 2、判断小红是否碰到顶部;
if ((x + m) * k + b == -10) {
// if ((x + m) * k + b == -10 || (x + m) * k + b == 490) {
this.turnUpDown(k, m);
return;
}
// 3、判断小红在底部时
if ((x + m) * k + b == 480) {
// 判断小红是否在长条上
if (this.data.redX >= this.data.sliderX - 10 && this.data.redX <= (this.data.sliderX + this.data.sliderWidth)) {
// 3.1判断小红在长条上,要改变k和m的值;
console.log("在长条上")
this.changgeKM();
console.log(this.data.redK, this.data.redM)
return;
} else {
clearTimeout(this.data.rered);
console.log("不在长条上")
// 3.2、判断小红不在长条上;落地计算,游戏结束
var nowfail = this.data.fail + 1;
this.setData({
fail: nowfail
})
// this.turnUpDown(k, m);
return;
}
}
// // 判断小红大于490时候,结束;
if ((x + m) * k + b > 480) {
clearTimeout(this.data.rered)
return;
}
// 4、判断小红是否碰到小白块;
var i = parseInt((x + m) / 10);
var j = parseInt(((x + m) * k + b) / 10);
if (this.data.blocks[i][j][2] == 1) {
// 判断一下头顶有没有小白
this.data.blocks[i][j][2] = 0;
// 分值计算
var nowscore = this.data.score + 1;
this.setData({
score: nowscore
})
// 重绘小白
this.drawBlocks();
// 碰到小白随机反弹
if (parseInt(Math.random() * 2) == 1) {
this.turnLeftRight(k, m);
return;
} else {
this.turnUpDown(k, m);
return;
}
} else {
//5、 绘制下一个小红;
this.painNextRed(k, b, m, x, y);
}
},
3、左右反弹的函数:
如图可知,当小红碰壁时,会将会左右反弹;
若原来第一条直线的函数为:y=kx+b1;
那么反弹以后的第二条直线函数便设为:y=-kx+b2;
二者直线的斜率互为相反数;这样每次判断以后就需要再次重新计算小红的运动轨迹函数;
例如图一,原来每次是在x轴上每次移动m;后来反弹以后每次移动为-m,此时斜率为-k;
turnLeftRight(k, m) {
this.setData({
redK: -k,
redM: -m
})
// console.log("左右换方向!", this.data.redK, this.data.redM)
// 反弹,获取反弹的直线函数,继续移动
this.getLine(this.data.redK, this.data.redM)
},
4、上反弹的函数
同理可得,当小红碰到顶壁的方法为
例如图一,原来每次是在x轴上每次移动m;后来反弹以后每次移动为m,但是此时斜率为-k;
turnUpDown(k, m) {
this.setData({
redK: -k,
redM: m
})
// console.log("下换方向!", this.data.redK, this.data.redM)
// 反弹,获取反弹的直线函数,继续移动
this.getLine(this.data.redK, this.data.redM)
},
5、下反弹时的判断
下反弹时,就需要考虑,小红落地时,是否在小长的范围内;
// 当小红到达底部490时:判断小红有没有被接住
if ((x + m) * k + b == 480) {
// 判断小红的X是否在小长长度之内
// console.log("小红现在的位置是:",this.data.redX,"小长现在的位置是",this.data.sliderX)
if (this.data.redX >= this.data.sliderX && this.data.redX <= (this.data.sliderX + this.data.sliderWidth)) {
this.turnUpDown(k, m);
return;
}
// 落地计算
var nowfail = this.data.fail + 1;
this.setData({
fail: nowfail
})
clearInterval(this.data.rered)
return;
}
6、根据小红落在小长的位置,进行不同角度的反弹
changgeKM() {
if (this.data.sliderX - 10 <= this.data.redX && this.data.redX <= this.data.sliderX + 20) {
this.data.redK = 0.5;
this.data.redM = -1;
this.getLine(this.data.redK, this.data.redM)
return;
}
if (this.data.sliderX + 20 <= this.data.redX && this.data.redX <= this.data.sliderX + 40) {
this.data.redK = 1;
this.data.redM = -1;
this.getLine(this.data.redK, this.data.redM)
return;
}
if (this.data.sliderX + 40 <= this.data.redX && this.data.redX <= this.data.sliderX + 50) {
this.data.redK = 2;
this.data.redM = -1;
this.getLine(this.data.redK, this.data.redM)
return;
}
if (this.data.sliderX + 50 <= this.data.redX && this.data.redX <= this.data.sliderX + 60) {
this.data.redK = -2;
this.data.redM = 1;
this.getLine(this.data.redK, this.data.redM)
return;
}
if (this.data.sliderX + 60 <= this.data.redX && this.data.redX <= this.data.sliderX + 80) {
this.data.redK = -1;
this.data.redM = 1;
this.getLine(this.data.redK, this.data.redM)
return;
}
if (this.data.sliderX + 80 <= this.data.redX && this.data.redX <= this.data.sliderX + 110) {
this.data.redK = -0.5;
this.data.redM = 1;
this.getLine(this.data.redK, this.data.redM)
return;
}
this.getLine(this.data.redK, this.data.redM)
return;
},
7、判断小红遇到小白的情况;
原来我考虑了。小红遇到小白出现的9种情况;其实不用这么麻烦;
(如图显示的是,小红遇到小白的9种消除的情况)
只要下一个小红的头顶上有一个小白,那么就将头顶上的小白给消除;
若头顶出现小白,则下一步小红随机左右,向下反弹;
// 判断小红四周的情况
if (this.data.blocks[i][j][2] == 1) {
// 判断一下侧面有没有小白,有的话就左右反弹
this.data.blocks[i][j][2] = 0;
// 分值计算
var nowscore = this.data.score + 1;
this.setData({
score: nowscore
})
// 重绘小白
this.drawBlocks();
if (parseInt(Math.random() * 2) == 1) {
this.turnLeftRight(k, m);
return;
}
this.turnUpDown(k, m);
return;
}
基本上这个游戏的雏形就完成了。
接下去还有很多需要优化的工作,后面会继续更新;
感谢阅读~