前端实现弹小球功能

news2025/1/18 18:20:23

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

        首先看一下实现的效果。

效果演示

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

技术实现

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.coloradmin.cn/o/1416433.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

借款还款记录账本,助你轻松地应对借还款带来的种种问题

借还款明细管理看似琐碎&#xff0c;实则关乎我们的切身利益。现在有【晨曦记账本】为你的财务健康保驾护航&#xff0c;让你可以更加轻松地应对借款和还款带来的种种问题&#xff0c;让生活更加简单、有序。 所需工具&#xff1a; 一个【晨曦记账本】软件 操作步骤&#xf…

使用大模型检索增强 Rerank 模型,检索效果提升太明显了!

Rerank 在 RAG&#xff08;Retrieval-Augmented Generation&#xff09;过程中扮演了一个非常重要的角色&#xff0c;普通的 RAG 可能会检索到大量的文档&#xff0c;但这些文档可能并不是所有的都跟问题相关&#xff0c;而 Rerank 可以对文档进行重新排序和筛选&#xff0c;让…

【C++】——类和对象(中)

一、前言 好久没有更新内容了&#xff0c;今天为大家带来类和对形中期的内容 &#xff01; 二、正文 1.this指针 1.1this指针的引入 class Date { public:void Init(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout << _year …

2. HarmonyOS 应用开发 DevEco Studio 准备-2

2. HarmonyOS 应用开发 DevEco Studio 准备-2 首选项设置 中文设置 主题 字体 插件安装和使用 保存时操作 编辑器 工程树管理 代码树管理 标记 字符串可视化编辑 参考文档 常用快捷键 编辑 查找或替换 编译与运行 调试 其他 预览 页面预览 自定义组件预览 预览…

2023年中国工控自动化市场现状及竞争分析,美日占主角,国产品牌初崭头角

工控自动化是一种运用控制理论、仪器仪表理论、计算机和信息技术&#xff0c;对工业生产过程实现检测、控制、优化、调度、管理和决策&#xff0c;达到增加产量、提高质量、降低消耗、确保安全等目的综合性技术。产品应用领域广泛&#xff0c;可分为OEM型行业和项目型行业。 近…

Metaphor(EXA) 基于大语言模型的搜索引擎

文章目录 关于 Metaphor使用示例 关于 Metaphor Metaphor是基于大语言模型的搜索引擎&#xff0c;允许用户使用完整的句子和自然语言搜索&#xff0c;还可以模拟人们在互联网上分享和谈论链接的方式进行查询内容。 Metaphor同时还能与LLMs结合使用&#xff0c;允许LLMs连接互联…

༺༽༾ཊ—Unity之-05-抽象工厂模式—ཏ༿༼༻

首先创建一个项目&#xff0c; 在这个初始界面我们需要做一些准备工作&#xff0c; 建基础通用文件夹&#xff0c; 创建一个Plane 重置后 缩放100倍 加一个颜色&#xff0c; 任务&#xff1a;使用 抽象工厂模式 创建 人物与宠物 模型&#xff0c; 首先资源商店下载 人物与宠物…

【JavaWeb】【C00153】基于SSM的大学生家教平台管理系统(论文+PPT)

基于SSM的大学生家教平台管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm大学生家教平台管理系统 本系统分为前台模块、后台管理员模块、用户木块及家教模块。 其中前台的权限为&#xff1a;首页、家教、公告信息…

猫用空气净化器哪款牌子好?好用能吸毛的宠物空气净化器推荐

作为一个养猫多年的铲屎官&#xff0c;我真的无法抗拒猫星人的可爱魅力&#xff01;以前&#xff0c;每当我路过宠物店&#xff0c;我总会忍不住停下来&#xff0c;在玻璃窗前停留半个小时以上。但是后来&#xff0c;我终于有了自己的猫咪。每天都能享受到给它摸小肚子的乐趣&a…

Filter Listener

文章目录 一 过滤器&#xff08;Filter&#xff09;1 什么是过滤器2 为什么使用过滤器3 过滤器执行流程4 过滤器的生命周期5 过滤器的注册5.1 XML方式5.2 WebFilter 注解方式 6 FilterConfig7 过滤器链8 过滤器应用 二 什么是监听器1 监听器分类2 监听器使用2.1 监听对象的创建…

Mac忘记本机MySql怎么办?

Mac忘记本机MySql怎么办&#xff1f; 1.打开系统偏好设置 2.打开Mysql 3.停止服务 4.直接初始化服务上图有一个初始化数据库 5.输入8位密码确认 6.重启服务

Blender教程(基础)-初始用户界面-01

开始第一天的Blender学习、也是业余学习。希望记录下这一份学习的过程、并且分享给大家。今天带大家认识Blender这一款软件&#xff0c;先说说我为什么选择了Blender&#xff0c;我在软件市场找了好久&#xff0c;市场上其他雷同软件都是要么收费要么不好用&#xff0c;最终决定…

【面试深度解析】滴滴后端二面:12306场景设计、Redis缓存设计、MyBatis两级缓存(下)

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

Mac安装nvm,安装多个不同版本node,指定node版本

一.安装nvm brew install nvm二。配置文件 touch ~/.zshrc echo export NVM_DIR~/.nvm >> ~/.zshrc echo source $(brew --prefix nvm)/nvm.sh >> ~/.zshrc三.查看安装版本 nvm -vnvm常用命令如下&#xff1a;nvm ls &#xff1a;列出所有已安装的 node 版本nvm…

一张图文深入了解信息量概念

通信原理第10页最后一段&#xff1a; 概率论告诉我们&#xff0c;事件的不确定程度可以用其出现的概率来描述。因此&#xff0c;消息中包含的信息量与消息发生的概率密切相关。消息出现的概率越小&#xff0c;则消息中包含的信息量就越大。 这句话怎么理解呢&#xff1f; 比如…

小红构造数组-牛客周赛 Round 29(DFS方法)

题目很直白&#xff0c;方法就是暴力即可。 虽然说数据范围显得很大&#xff0c;但是在长整型范围内&#xff0c;一个数字的素因子数量最多不超64&#xff0c;而如果是不相同的素因子&#xff0c;虽然没有计算过&#xff0c;但是如果是12个不同的素因子应该会超过数据范围了。…

消息中间件之八股面试回答篇:三、RabbitMQ如何解决消息堆积问题(100万条消息堆积)+RabbitMQ高可用性和强一致性机制+回答模板

RabbitMQ中的消息堆积问题 当生产者发送消息的速度超过了消费者处理消息的速度&#xff0c;就会导致队列中的消息堆积&#xff0c;直到队列存储消息达到上限。之后发送的消息就会成为死信&#xff0c;可能会被丢弃&#xff0c;这就是消息堆积问题。 解决消息堆积有三种种思路…

c++阶梯之引用与内联函数

1. 引用 1.1 引用概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。 语法 类型& 引用变量名(对象名) 引用实体; 示例 很显然&#xff0c;在下面这…

21.Arrays类

Arrays类 1. 概述2. 常见方法3. sort 方法的自定义排序4. 代码示例5. 输出结果6. 注意事项 具体信息请查看 API 帮助文档 1. 概述 Arrays类是Java中的一个工具类&#xff0c;位于java.util包中。 它提供了一组静态方法&#xff0c;用于操作数组。通过Arrays类&#xff0c;我们…

springboot136人口老龄化社区服务与管理平台

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…