今天打开蚂蚁森林将能量的时候玩了以下里面的那个小游戏,想着自己也做个差不多的试试。花了一个小时,成果如下,预览地址。基本实现了蚂蚁森林的那个游戏的基本方式。
游戏截图
游戏玩法:
- 通过鼠标点击得分和累计个数
- 按键盘上对应的键也可得分和累计个数
- 分数小于0即为失败
- 没有被点击到的在出屏幕后会扣除相应的分数
以下是实现,主要使用canvas
一 准备
由于是一个非常简单的小游戏,因此新建一个html
文件就好。
二 开始
由于主要是使用canvas,因此先创建它,这一步大伙都轻车熟路了
html
<body style="height: 100vh;overflow:hidden">
<div style="position: absolute;z-index: 10;font-size: 20px;color:#fff">
当前得分:<span id='score'>0</span>,点击个数:<span id='number'>0</span>
</div>
</body>
js
let canvas = document.createElement("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
let ctx = canvas.getContext("2d");
//饼
let img = new Image();
img.src = './img/1.webp'
//背景图
let bg = new Image()
bg.src = './img/bg2.jpg'
三 月饼
作为主要的东西,我们先来创建月饼,下面创建一个月饼类
js
class MoonCake {
shot = false
constructor(x, y, r, point, char) {
this.x = x;
this.y = y;
this.r = r;
this.point = point;
this.char = char
}
draw() {
ctx.beginPath();
ctx.fillStyle = 'orange';
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.fill();
ctx.drawImage(img, this.x - this.r, this.y - this.r, this.r * 2, this.r * 2)
ctx.strokeStyle = '#fff'
drawText(this.x, this.y, this.char, 'black', 60)
ctx.stroke();
}
update() {
this.y += speed
this.draw()
this.overflowRemove()
}
overflowRemove() {
if (this.y >= canvas.height + this.r * 2) {
if (!this.shot) {
document.querySelector('#score').innerHTML = Number(document.querySelector('#score').innerHTML) - this.point
}
this.remove()
}
}
remove() {
moonCakes.splice(moonCakes.indexOf(this), 1)
}
}
这是一个仅仅能跑的类,draw
方法负责绘制月饼,里面的drawText
方法如下,就是随随便便的画个文字上去。
js
function drawText(x, y, text, color = 'white', size = 30) {
ctx.font = `${size}px Arial`;
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.strokeStyle = 'white';
ctx.fillText(`${text}`, x, y);
ctx.strokeText(`${text}`, x, y)
}
update
方法顾名思义是用来更新当前月饼的状态的,由于这边是垂直下落,因此改变y轴的值就好。
overflowRemove
方法用来移除超出屏幕的月饼并且减分 remove
方法用来移除月饼
四 生成月饼
在这里我添加了以下方法用来生成月饼,并添加了一个isOverlap
辅助方法用来处理坐标生成坐标重叠的问题,在重叠的时候重新调用一次生成月饼的方法。然后还添加了一个随机字母的方法getRandomLetter
用来获取字母
js
let moonCakes = [];//存放所有月饼
let textArray = []//存放点击后展示的得分数字
let onceNum = 5 //控制生成数量
function isOverlap(x, y, r) {
for (let i = 0; i < moonCakes.length; i++) {
let m = moonCakes[i];
if (Math.abs(m.x - x) < r + m.r && Math.abs(m.y - y) < r + m.r) {
return true
}
}
return false
}
js
//随机抽取26个英文字母中的一个
function getRandomLetter() {
let index = Math.floor(Math.random() * 26)
return String.fromCharCode(index + 65)
}
js
function genMoonCake() {
let r = 80;//半径
let x = Math.random() * (canvas.width - r * 2) + r;
let y = -Math.abs(Math.random() * canvas.height)
let point = parseInt(Math.abs(Math.random() * 100))
let chart = getRandomLetter()
if (isOverlap(x, y, r)) {
genMoonCake()
} else {
moonCakes.push(new MoonCake(x, y, r, point, chart))
}
}
五 让画面动起来
js
function start() {
if (moonCakes.length < onceNum - 1) {
genMoonCake()
}
for (let i = 0; i < moonCakes.length; i++) {
moonCakes[i].update();
}
requestAnimationFrame(start)
}
start()
添加一个start
方法并且通过requestAnimationFrame
不断调用自身,现在保存执行后应该能看到画面动起来了。
六 点击月饼
如何点击月饼呢,在canvas中我们是无法为绘制的元素直接绑定鼠标或者键盘的监听事件的,因此我们需要通过坐标去判断是否点击到,直接为canvas绑定鼠标点击事件,如下
js
//为月饼添加点击事件
canvas.addEventListener('click', function (e) {
let x = e.offsetX;
let y = e.offsetY;
console.log(x, y)
for (let i = 0; i < moonCakes.length; i++) {
let moonCake = moonCakes[i];
let r = moonCake.r;
//判断是否点击中月饼此刻的坐标范围
if (Math.pow(x - moonCake.x, 2) + Math.pow(y - moonCake.y, 2) <= Math.pow(r, 2)) {
document.querySelector('#number').innerHTML = Number(document.querySelector('#number').innerHTML) + 1
moonCake.shot = true
console.log('点击中了', moonCake)
//在点击位置绘制文字
textArray.push({
x,
y,
text: `+${moonCake.point}`
})
console.log(textArray)
//移除本次点击的分数文字
setTimeout(() => {
textArray.pop()
}, 200);
document.querySelector('#score').innerHTML =
Number(document.querySelector('#score').innerHTML+ moonCake.point
moonCake.remove();
break;
}
}
})
然后我们在start
方法中增加一些代码,如下
js
//依据分数来设置下落速度
function setSpeed(num) {
if (num <= 500 && speed <= 2) {
speed = 2
}
if (num > 500 && num <= 700 && speed <= 3) {
speed = 3
}
if (num > 700 && num <= 800 && speed <= 4) {
speed = 4
}
if (num > 800 && num <= 1500 && speed <= 6) {
speed = 6
}
if (num > 1500 && num < 1700 && speed <= 8) {
speed = 8
}
if (num > 1700 && speed <= 8) {
speed = 12
}
}
js
function start() {
console.log(moonCakes.length)
let num = Number(document.querySelector('#score').innerHTML)
if (num < 0) {
return alert('结束')
}
setSpeed(num)
//清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
//绘制背景
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
//绘制分数文字
textArray.forEach((item) => {
drawText(item.x, item.y, item.text)
})
//在当前月饼数量小于特定值后才生成月饼
if (moonCakes.length < onceNum - 1) {
//不断的生成月饼
genMoonCake()
}
for (let i = 0; i < moonCakes.length; i++) {
moonCakes[i].update();
}
requestAnimationFrame(start)
}
到这里保存运行后就能看到月饼源源不断的下落,鼠标命中后即可得分。
除此之外再监听键盘点击事件,添加如下代码,即可实现脸滚键盘也能拾得月饼。
js
window.addEventListener('keyup', e => {
let key = e.key.toUpperCase()
for (let i = 0; i < moonCakes.length; i++) {
let moonCake = moonCakes[i];
if (moonCake.char == key) {
document.querySelector('#number').innerHTML = Number(document.querySelector('#number').innerHTML) + 1
moonCake.shot = true
textArray.push({
x: moonCake.x,
y: moonCake.y,
text: `+${moonCake.point}`
})
setTimeout(() => {
textArray.pop()
}, 200);
calcSumPoint(moonCake.point)
moonCake.remove();
break
}
}
})
七 总结
一个花费时间不多得小游戏,代码结构得合理性肯定存在问题。有问题欢迎指出,文中所用资源素材均为百度出来的相关图片,如果需要资源请进入文章开头的预览地址获取,都是前端你懂的~