前端实现弹小球功能

        这篇文章将会做弹小球游戏,弹小球游戏大家小时候都玩过,玩家需要在小球到达游戏区域底部时候控制砖块去承接小球,并不断的将小球弹出去。

        首先看一下实现的效果。

效果演示

玩家需要通过控制鼠标来实现砖块的移动,保证在小球下落到底部时接到小球。

技术实现

html布局

        游戏区域包括小球和砖块2个部分,小球在来回移动,砖块在底部移动。

import {useEffect, useState} from "react";

const BounceBall = () => {

    // 游戏区域配置
    const gameArea = {
        width: 500,
        height: 400
    }

    // 小球对象
    const initBall = {
        width: 20, // 小球宽度
        height: 20,// 小球高度
        posX: 240, // 240 ~ 260
        posY: 190,  // 190 ~ 210
        speedX: 0, // 小球移动速度
        speedY: 2  // 小球移动速度
    }

    const [ball, setBall] = useState(initBall)

    // 砖块对象
    const initPaddle = {
        width: 80, // 砖块宽度
        height: 20,// 砖块高度
        posX: 210, // 210 ~ 290
        posY: 380  // 380 ~ 400
    }

    const [paddle, setPaddle] = useState(initPaddle)



    // 游戏区域样式
    const gameAreaStyle = {
        position: "relative", /*相对定位*/
        width: `${gameArea.width}px`, /*区域宽度*/
        height: `${gameArea.height}px`, /*区域高度*/
        backgroundColor: "#333"
    }

    // 小球样式
    const ballStyle = {
        position: "absolute",/* 绝对定位 */
        top: `${ball.posY}px`,   /*小球位置*/
        left: `${ball.posX}px`, /*小球位置*/
        width: `${ball.width}px`,/* 设置小球的宽度 */
        height: `${ball.height}px`, /* 设置小球的高度 */
        borderRadius: "50%", /* 设置小球为圆形 */
        backgroundColor: "red" /* 设置小球的背景颜色 */
    }

    // 砖块样式
    const paddleStyle = {
        position: 'absolute', /* 绝对定位 */
        top: `${paddle.posY}px`, /*板块位置*/
        left: `${paddle.posX}px`, /*板块位置*/
        width: `${paddle.width}px`, /* 设置砖块的宽度 */
        height: `${paddle.height}px`, /* 设置砖块的高度 */
        backgroundColor: "blue", /* 设置砖块的背景颜色 */
        border: "1px solid black" /* 设置砖块的边框 */
    }

    return (<>
        <div style={gameAreaStyle}>
            {/*小球*/}
            <div style={ballStyle}>
            </div>
            {/*砖块*/}
            <div style={paddleStyle}>
            </div>
        </div>
    </>)
}

游戏控制处理

小球移动处理

        小球移动通过定时器实现,定时器会在小球发生碰撞、游戏失败后清楚。

const [failCnt, setFailCnt] = useState(0)
// 定时器控制小球移动
useEffect(()=>{
       const intervalId = setInterval(()=>{
            // 小球移動
            ball.posX += ball.speedX;
            ball.posY += ball.speedY;
            setBall({...ball})
        }, 20)
        return ()=> {clearInterval(intervalId)} // 返回是一个函数,并非结果
 },[ball.speedX, ball.speedY, failCnt])
砖块移动处理

        砖块移动通过控制鼠标移动实现

// 砖块移动
useEffect(()=>{
        document.addEventListener('mousemove',(ev => {
            const mouseX = ev.clientX;
            let paddlePosX = mouseX - paddle.width/2;
            if (paddlePosX < 0){
                paddlePosX = 0;
            }else if (paddlePosX >= gameArea.width - paddle.width){
                paddlePosX = gameArea.width - paddle.width;
            }
            setPaddle({
                ...paddle,
                posX: paddlePosX
            })
        }))
},[])
小球位置判断

   需要考虑边界碰撞、砖块碰撞等场景

// 小球位置判断
useEffect(()=>{

    // 边界判断
    if (ball.posX <= 0 || ball.posX >= gameArea.width - ball.width) {
        ball.speedX = -1 * ball.speedX;
        setBall({...ball});
        return;
    }
    if (ball.posY <= 0) {
        ball.speedY = -1 * ball.speedY;
        setBall({...ball});
        return;
    }

    // 判断是否碰撞到砖块
    if (ball.posY >= gameArea.height - ball.height - paddle.height) {
        // 未碰撞砖块
        if (ball.posX + ball.width <= paddle.posX || ball.posX >= paddle.posX + paddle.width) {
            alert('游戏失败')

            setBall({...initBall})
            setPaddle({...initPaddle})
            setFailCnt(failCnt + 1)
            return;
        }
        console.log(ball)
        // 计算小球x speed
        const collisionPoint = (ball.posX + ball.width/2 ) - (paddle.posX + paddle.width/2) ; // 计算碰撞点距离挡板中心点的距离
        ball.speedX = collisionPoint * 0.1
        ball.speedY = -1 * ball.speedY
        setBall({...ball})
    }
}, [ball.posX, ball.posY])

整体代码

import {useEffect, useState} from "react";

const BounceBall = () => {

    // 游戏区域配置
    const gameArea = {
        width: 500,
        height: 400
    }

    // 小球对象
    const initBall = {
        width: 20, // 小球宽度
        height: 20,// 小球高度
        posX: 240, // 240 ~ 260
        posY: 190,  // 190 ~ 210
        speedX: 0, // 小球移动速度
        speedY: 2  // 小球移动速度
    }

    const [ball, setBall] = useState(initBall)

    // 砖块对象
    const initPaddle = {
        width: 80, // 砖块宽度
        height: 20,// 砖块高度
        posX: 210, // 210 ~ 290
        posY: 380  // 380 ~ 400
    }

    const [paddle, setPaddle] = useState(initPaddle)


    const [failCnt, setFailCnt] = useState(0)
    // 定时器控制小球移动
    useEffect(()=>{
       const intervalId = setInterval(()=>{
            // 小球移動
            ball.posX += ball.speedX;
            ball.posY += ball.speedY;
            setBall({...ball})
        }, 20)
        return ()=> {clearInterval(intervalId)} // 返回是一个函数,并非结果
    },[ball.speedX, ball.speedY, failCnt])


    // 小球位置判断
    useEffect(()=>{

        // 边界判断
        if (ball.posX <= 0 || ball.posX >= gameArea.width - ball.width) {
            ball.speedX = -1 * ball.speedX;
            setBall({...ball});
            return;
        }
        if (ball.posY <= 0) {
            ball.speedY = -1 * ball.speedY;
            setBall({...ball});
            return;
        }

        // 判断是否碰撞到砖块
        if (ball.posY >= gameArea.height - ball.height - paddle.height) {
            // 未碰撞砖块
            if (ball.posX + ball.width <= paddle.posX || ball.posX >= paddle.posX + paddle.width) {
                alert('游戏失败')

                setBall({...initBall})
                setPaddle({...initPaddle})
                setFailCnt(failCnt + 1)
                return;
            }
            console.log(ball)
            // 计算小球x speed
            const collisionPoint = (ball.posX + ball.width/2 ) - (paddle.posX + paddle.width/2) ; // 计算碰撞点距离挡板中心点的距离
            ball.speedX = collisionPoint * 0.1
            ball.speedY = -1 * ball.speedY
            setBall({...ball})
        }
    }, [ball.posX, ball.posY])


    // 砖块移动
    useEffect(()=>{
        document.addEventListener('mousemove',(ev => {
            const mouseX = ev.clientX;
            let paddlePosX = mouseX - paddle.width/2;
            if (paddlePosX < 0){
                paddlePosX = 0;
            }else if (paddlePosX >= gameArea.width - paddle.width){
                paddlePosX = gameArea.width - paddle.width;
            }
            setPaddle({
                ...paddle,
                posX: paddlePosX
            })
        }))

    },[])

    // 游戏区域样式
    const gameAreaStyle = {
        position: "relative", /*相对定位*/
        width: `${gameArea.width}px`, /*区域宽度*/
        height: `${gameArea.height}px`, /*区域高度*/
        backgroundColor: "#333"
    }

    // 小球样式
    const ballStyle = {
        position: "absolute",/* 绝对定位 */
        top: `${ball.posY}px`,   /*小球位置*/
        left: `${ball.posX}px`, /*小球位置*/
        width: `${ball.width}px`,/* 设置小球的宽度 */
        height: `${ball.height}px`, /* 设置小球的高度 */
        borderRadius: "50%", /* 设置小球为圆形 */
        backgroundColor: "red" /* 设置小球的背景颜色 */
    }

    // 砖块样式
    const paddleStyle = {
        position: 'absolute', /* 绝对定位 */
        top: `${paddle.posY}px`, /*板块位置*/
        left: `${paddle.posX}px`, /*板块位置*/
        width: `${paddle.width}px`, /* 设置砖块的宽度 */
        height: `${paddle.height}px`, /* 设置砖块的高度 */
        backgroundColor: "blue", /* 设置砖块的背景颜色 */
        border: "1px solid black" /* 设置砖块的边框 */
    }

    return (<>
        <div style={gameAreaStyle}>
            {/*小球*/}
            <div style={ballStyle}>
            </div>
            {/*砖块*/}
            <div style={paddleStyle}>
            </div>
        </div>
    </>)
}

export default BounceBall

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/353878.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux操作系统运维-用户与用户组管理

Linux操作系统运维-用户与用户组管理 用户种类与标识查看 超级用户&#xff08;root&#xff09;&#xff1a;可以不受限制地执行所有操作&#xff0c;拥有系统最高权限&#xff0c;修改系统设置与管理用户均需要root权限系统用户&#xff08;system&#xff09;&#xff1a;…

Flutter App 生命周期观察监听

前言 本文主要讲解两种 Flutter生命周期观察监听 方式一&#xff1a;Flutter SDK 3.13 之前的方式&#xff0c;WidgetsBindingObserver&#xff1b; 方式二&#xff1a;Flutter SDK 3.13 开始的新方式&#xff0c;AppLifecycleListener&#xff1b; 测试平台&#xff1a;IO…

《HelloGitHub》第 94 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 …

宏景-eHR-frcodeaddtreeservlet接口存在SQL注入

指纹特征 FOFA&#xff1a;icon_hash"947874108" || body<div class"hj-hy-all-one-logo" || app"HJSOFT-HCM" 漏洞复现 POST /templates/attestation/../../servlet/FrCodeAddTreeServlet HTTP/1.1 Host: User-Agent: Mozilla/5.0 (Windo…

精品基于Uniapp+ssm宠物时光管理系统App

《[含文档PPT源码等]精品基于Uniappssm宠物时光管理系统App》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;Java 后台框架&#xff1a;ssm 安卓框架&#xff1a…

游戏开发丨基于Pygame的贪吃蛇小游戏

文章目录 写在前面需求分析程序设计程序分析运行结果系列文章写在后面 写在前面 本期内容 基于pygame的贪吃蛇小游戏 所需环境 pythonpycharm或anacondapygame 下载地址 https://download.csdn.net/download/m0_68111267/88789657 需求分析 本游戏使用Pygame模块开发&a…

Go 知识chan

Go 知识chan 1. 基本知识1.1 定义1.2 操作1.3 操作限定1.4 chan 读写 2. 原理2.1 数据结构2.2 环形队列2.3 等待队列2.4 类型消息2.5 读写数据2.6 关闭chan 3. 使用3.1 操作符使用3.2 select3.3 for-range https://a18792721831.github.io/ 1. 基本知识 chan是go里面里面提供…

ssh异常报错:Did not receive identification string from

一、问题描述 某次外出在异地工作场所xshell炼乳远程服务器时&#xff0c;报错&#xff1a;Connection closed by foreign host. D&#xff0c;服务器查看secure日志或sshd服务状态会显示&#xff1a;id not receive identification string from client_ip; 二、分析处理 1&a…

综合案例 - 商品列表

文章目录 需求说明1.my-tag组件封装&#xff08;完成初始化&#xff09;2.may-tag封装&#xff08;控制显示隐藏&#xff09;3.my-tag组件封装&#xff08;v-model处理&#xff1a;信息修改&#xff09;4.my-table组件封装&#xff08;整个表格&#xff09;①数据不能写死&…

修复idea,eclipse ,clion控制台中文乱码

控制台乱码问题主要原因并不在编译器IDE身上&#xff0c;还主要是Windows的控制台默认编码问题。。。 Powershell&#xff0c;cmd等默认编码可能不是UTF-8&#xff0c;无需改动IDE的settings或者properties&#xff08;这治标不治本&#xff09;&#xff0c;直接让Windows系统…

编写nginx脚本,安装失败

这是我写的nginx脚本-&#xff08;正确的&#xff0c;已经修改过的&#xff09; 这是我在运行脚本是出现的问题 这是我在nginx官网上粘贴的内容&#xff0c;请注意我用红笔画的地方&#xff0c;与第一张我写的脚本图片作对比&#xff0c;会发现多出现两个转义符号\。第二幅图就…

Axolotl:一款极简的大模型微调(Finetune)开源框架

今天给大家分享一款工具&#xff0c;Axolotl[1] 是一个旨在简化各种AI模型的微调过程的工具&#xff0c;支持多种配置和架构。 特点&#xff1a; 可训练各种 Huggingface 模型&#xff0c;如 llama、pythia、falcon、mpt支持 fullfinetune、lora、qlora、relora 和 gptq使用简…

Linux GPIO模拟SPI接口介绍

一. 前言 上次通过GPIO模拟IIC的驱动一块0.98英寸的OLED后&#xff0c;想着下次用下GPIO模拟SPI接口试下。本文会介绍怎么用GPIO模拟SPI接口的方式为设备添加一块spi nor flash&#xff0c;flash模块如下图所示&#xff1a; 上图是一个spi nor模块&#xff0c;上面焊接了W25Q32…

acwing周赛140 b题

思路&#xff1a;我们按照从小到大的顺序将数组逆转好&#xff0c;然后枚举数组首项&#xff0c;分别让其1&#xff0c;-1&#xff0c;0&#xff0c;然后求出公差&#xff0c;从前往后遍历即可。 代码&#xff1a; int ans1(){//不动int cha (a[n] - a[1] 1) / (n - 1);int…

原创改进 | 融合蝠鲼觅食与联想学习的量子多目标灰狼优化算法(Matlab)

​前面的文章里作者介绍了多目标灰狼优化算法(Multi-Objective Grey Wolf Optimizer&#xff0c;MOGWO)&#xff0c;该算法是由Mirjalili等(灰狼算法的提出者)于2016年提出[1]&#xff0c;发表在中科院一区期刊《expert systems with applications》。 MOGWO保留了灰狼算法的种…

无际线复选框

效果演示 实现了一个网格布局&#xff0c;其中每个网格是一个复选框&#xff0c;可以选择是否显示。每个复选框都有一个漂浮的天花板&#xff0c;表示它是一个房间的天花板。每个房间的天花板都有一个不同的形状和颜色&#xff0c;分别对应不同的房间。整个页面的背景是一个由两…

小米13utltra激活冰箱

安装adb驱动https://www.jianshu.com/p/6c41d34d9202 直接下载使用 连接上小米手机,退出账号,关闭锁屏密码,打开usb调试,usb调试(安全设置) adb shell dpm set-device-owner com.catchingnow.icebox/.receiver.DPMReceiver如果显示Success&#xff0c;则您已成功激活冰箱。 …

【DeepLearning-8】MobileViT模块配置

完整代码&#xff1a; import torch import torch.nn as nn from einops import rearrange def conv_1x1_bn(inp, oup):return nn.Sequential(nn.Conv2d(inp, oup, 1, 1, 0, biasFalse),nn.BatchNorm2d(oup),nn.SiLU()) def conv_nxn_bn(inp, oup, kernal_size3, stride1):re…

PostgreSQL技术大讲堂 - 第43讲:流复制原理

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第43讲&#…

每日OJ题_算法_二分查找⑦_力扣153. 寻找旋转排序数组中的最小值

目录 力扣153. 寻找旋转排序数组中的最小值 解析代码 力扣153. 寻找旋转排序数组中的最小值 153. 寻找旋转排序数组中的最小值 - 力扣&#xff08;LeetCode&#xff09; 难度 中等 已知一个长度为 n 的数组&#xff0c;预先按照升序排列&#xff0c;经由 1 到 n 次 旋转 后…
最新文章