likes
comments
collection
share

梦回金山打字通后我在前端捡月饼

作者站长头像
站长
· 阅读数 8

今天打开蚂蚁森林将能量的时候玩了以下里面的那个小游戏,想着自己也做个差不多的试试。花了一个小时,成果如下,预览地址。基本实现了蚂蚁森林的那个游戏的基本方式。

游戏截图

梦回金山打字通后我在前端捡月饼

梦回金山打字通后我在前端捡月饼

梦回金山打字通后我在前端捡月饼

游戏玩法:

  1. 通过鼠标点击得分和累计个数
  2. 按键盘上对应的键也可得分和累计个数
  3. 分数小于0即为失败
  4. 没有被点击到的在出屏幕后会扣除相应的分数

以下是实现,主要使用canvas

一 准备

由于是一个非常简单的小游戏,因此新建一个html文件就好。

二 开始

由于主要是使用canvas,因此先创建它,这一步大伙都轻车熟路了

<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>
  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'

三 月饼

作为主要的东西,我们先来创建月饼,下面创建一个月饼类

  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方法如下,就是随随便便的画个文字上去。

    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用来获取字母

  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
    }

 //随机抽取26个英文字母中的一个
    function getRandomLetter() {
        let index = Math.floor(Math.random() * 26)
        return String.fromCharCode(index + 65)
    }
    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))
        }
     
    }

五 让画面动起来

 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绑定鼠标点击事件,如下

  //为月饼添加点击事件
    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方法中增加一些代码,如下

//依据分数来设置下落速度
 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
        }
    }
  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)
    }

到这里保存运行后就能看到月饼源源不断的下落,鼠标命中后即可得分。

除此之外再监听键盘点击事件,添加如下代码,即可实现脸滚键盘也能拾得月饼。

  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
            }


        }
    })

七 总结

一个花费时间不多得小游戏,代码结构得合理性肯定存在问题。有问题欢迎指出,文中所用资源素材均为百度出来的相关图片,如果需要资源请进入文章开头的预览地址获取,都是前端你懂的~