Java 实现桌面烟花秀

news2025/2/24 5:22:26

前言

今天,我们将展示如何使用 Java Swing 创建一个烟花效果,覆盖整个桌面。我们将重点讲解如何在桌面上展示烟花、如何实现发射和爆炸效果,以及如何将这些效果整合到一个完整的程序中。

效果展示

如上图所示,我们在桌面实现了:

  1. 发射烟花
  2. 烟花爆炸的特效

完整代码

下面是整个项目的完整代码。大家可以新建一个名为 FireworkDisplay.java 的文件,将完整代码拷贝进去,运行 main() 方法,就可以在桌面看到一场烟花秀了。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;

// 发射粒子类
class LaunchParticle {
    public int x, y;       // 位置
    public int vx, vy;     // 速度
    public boolean exploded = false; // 是否已经爆炸
    public Color color;    // 粒子颜色
    public int targetHeight;  // 目标高度

    public LaunchParticle(int startX, int startY, int velocityX, int velocityY, int targetHeight, Color color) {
        this.x = startX;
        this.y = startY;
        this.vx = velocityX;
        this.vy = velocityY;
        this.targetHeight = targetHeight;
        this.color = color;
    }

    // 更新发射粒子的位置
    public void update() {
        x += vx;
        y += vy;
        vy += 1;  // 模拟重力

        // 当粒子到达目标高度或者开始下落时爆炸
        if (y <= targetHeight || vy >= 0) {
            exploded = true;
        }
    }
}

// 烟花爆炸粒子类
class FireworkParticle {
    public int x, y;       // 位置
    public int vx, vy;     // 速度
    public int life;       // 粒子寿命
    public Color color;    // 粒子颜色

    public FireworkParticle(int x, int y, int vx, int vy, int life, Color color) {
        this.x = x;
        this.y = y;
        this.vx = vx;
        this.vy = vy;
        this.life = life;
        this.color = color;
    }

    // 更新爆炸粒子的位置
    public void update() {
        x += vx;
        y += vy;
        vy += 1;  // 模拟重力
        life--;
    }

    public boolean isDead() {
        return life <= 0;
    }
}

// 显示烟花的面板
class FireworkPanel extends JPanel implements ActionListener {
    private final ArrayList<FireworkParticle> particles = new ArrayList<>();
    private final ArrayList<LaunchParticle> launchParticles = new ArrayList<>();
    private final Random random = new Random();
    private Timer timer;
    private int screenHeight;

    // 预定义一组颜色段,确保在任何背景下都好看
    private final Color[] colorPalette = {
        new Color(255, 128, 0),   // 橙色
        new Color(255, 51, 51),   // 红色
        new Color(51, 204, 255),  // 天蓝色
        new Color(51, 255, 51),   // 绿色
        new Color(204, 51, 255),  // 紫色
        new Color(255, 255, 51),  // 黄色
        new Color(255, 102, 178)  // 粉色
    };

    public FireworkPanel(int screenHeight) {
        this.screenHeight = screenHeight;
        // 设置面板为透明
        this.setOpaque(false);
        timer = new Timer(30, this);  // 每 30 毫秒更新一次
        timer.start();
    }

    // 选择随机颜色,从预定义的颜色段中选取
    private Color getRandomColor() {
        return colorPalette[random.nextInt(colorPalette.length)];
    }

    // 添加发射粒子
    public void addLaunchParticle() {
        int width = getWidth();
        int height = getHeight();

        if (width <= 0 || height <= 0) return;

        int startX = random.nextInt(width); // 发射起始位置
        int startY = height; // 从屏幕底部发射
        int targetHeight = (int) (screenHeight * 0.2); // 烟花将在屏幕高度的 4/5 处爆炸
        int velocityY = -(random.nextInt(10) + 50); // 增加初始向上速度,确保高度合适
        Color color = getRandomColor(); // 从预定义颜色中选取

        launchParticles.add(new LaunchParticle(startX, startY, 0, velocityY, targetHeight, color));
    }

    // 添加爆炸粒子,使用极坐标生成圆形分布,增加中心粒子密度
    public void addExplosion(int x, int y, Color color) {
        int numParticles = 200; // 每个烟花的粒子数量
        for (int i = 0; i < numParticles; i++) {
            double angle = 2 * Math.PI * random.nextDouble(); // 每个粒子的角度随机
            int speed = random.nextInt(15) + 5; // 粒子的速度增加随机性,靠近中心的速度会更小
            int vx = (int) (Math.cos(angle) * speed); // 使用极坐标计算x方向速度
            int vy = (int) (Math.sin(angle) * speed); // 使用极坐标计算y方向速度
            int life = random.nextInt(50) + 50; // 粒子寿命
            particles.add(new FireworkParticle(x, y, vx, vy, life, color));
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;

        // 绘制发射粒子
        for (LaunchParticle launchParticle : launchParticles) {
            g2d.setColor(launchParticle.color);
            g2d.fillOval(launchParticle.x - 3, launchParticle.y - 3, 6, 10); // 缩小发射粒子尺寸,使用半径为3的圆形
        }

        // 绘制爆炸粒子
        for (FireworkParticle particle : particles) {
            g2d.setColor(new Color(particle.color.getRed(), particle.color.getGreen(), particle.color.getBlue(), Math.max(particle.life * 255 / 100, 0)));
            g2d.fillOval(particle.x - 3, particle.y - 3, 6, 6); // 缩小爆炸粒子尺寸,使用半径为3的圆形
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // 更新发射粒子
        ArrayList<LaunchParticle> toRemoveLaunch = new ArrayList<>();
        for (LaunchParticle launchParticle : launchParticles) {
            launchParticle.update();
            if (launchParticle.exploded) {
                addExplosion(launchParticle.x, launchParticle.y, launchParticle.color); // 发射粒子爆炸
                toRemoveLaunch.add(launchParticle);
            }
        }
        launchParticles.removeAll(toRemoveLaunch);

        // 更新爆炸粒子
        ArrayList<FireworkParticle> deadParticles = new ArrayList<>();
        for (FireworkParticle particle : particles) {
            particle.update();
            if (particle.isDead()) {
                deadParticles.add(particle);
            }
        }
        particles.removeAll(deadParticles);

        // 持续生成发射粒子,保证烟花不断生成
        if (random.nextInt(15) == 0) { // 增加生成频率
            addLaunchParticle(); // 随机生成多个烟花
        }

        // 重新绘制
        repaint();
    }
}

public class FireworkDisplay {
    public static void main(String[] args) {
        // 获取屏幕大小
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

        // 创建全屏的透明窗口
        JWindow window = new JWindow();
        window.setSize(screenSize);
        window.setBackground(new Color(0, 0, 0, 0)); // 设置背景透明
        window.setLayout(new BorderLayout()); // 使用 BorderLayout 布局

        // 添加烟花面板
        FireworkPanel panel = new FireworkPanel(screenSize.height); // 传递屏幕高度
        window.add(panel, BorderLayout.CENTER); // 确保 FireworkPanel 占满整个窗口
        window.setAlwaysOnTop(true); // 窗口始终在最前面
        window.setVisible(true);

        // 强制重绘刷新
        RepaintManager.currentManager(panel).markCompletelyDirty(panel);

        // 启动后立即生成发射粒子
        panel.addLaunchParticle();
    }
}

全屏透明窗口

为了让烟花在桌面上显示而不是局限在普通的应用程序窗口中,我们需要创建一个 全屏透明窗口。我们使用 JWindow 类,这是一个没有边框和标题栏的窗口,非常适合用于全屏特效展示。

为什么需要全屏透明窗口?

普通的窗口只会在应用程序区域内显示,而我们希望烟花能够在整个桌面上绽放。通过设置一个透明的 JWindow,我们就能让窗口完全覆盖桌面。

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // 获取屏幕大小
JWindow window = new JWindow(); // 创建全屏窗口
window.setSize(screenSize); // 设置窗口为屏幕大小
window.setBackground(new Color(0, 0, 0, 0)); // 使窗口背景完全透明
window.setLayout(new BorderLayout());

window.setBackground(new Color(0, 0, 0, 0)); 中的最后一个参数 0 表示完全透明。这样一来,窗口显示的部分就是我们后续的烟花效果,桌面背景不会被覆盖。

粒子系统

我们通过粒子的方式来模拟烟花的发射效果。首先,发射粒子从屏幕底部向上移动。我们创建 LaunchParticle 类来管理这些粒子。每个粒子都有位置、速度等属性,它们随着时间不断更新位置,模拟上升的过程。

class LaunchParticle {
    public int x, y; // 粒子的位置
    public int vx, vy; // 粒子的速度
    public boolean exploded = false; // 是否已爆炸

    public LaunchParticle(int startX, int startY, int velocityX, int velocityY) {
        this.x = startX;
        this.y = startY;
        this.vx = velocityX;
        this.vy = velocityY;
    }

    public void update() {
        this.x += this.vx; // 更新水平位置
        this.y += this.vy; // 更新垂直位置
    }
}

每个粒子都有初始的位置和速度,我们通过 update() 方法来不断改变它们的位置,形成粒子上升的效果。

当发射粒子上升到一定高度时,粒子就会爆炸。爆炸后,它们会分裂成多个小粒子,这些粒子四处扩散,就可以模拟出烟花绽放的效果。我们通过 FireworkParticle 类来实现这些爆炸后的粒子。

class FireworkParticle {
    public int x, y;
    public int vx, vy; // 爆炸粒子的速度
    public int life; // 粒子的生命值

    public FireworkParticle(int x, int y, int vx, int vy, int life) {
        this.x = x;
        this.y = y;
        this.vx = vx;
        this.vy = vy;
        this.life = life;
    }

    public void update() {
        this.x += this.vx; // 更新位置
        this.y += this.vy;
        this.life--; // 生命值减少
    }

    public boolean isDead() {
        return life <= 0; // 粒子是否存活
    }
}

在爆炸发生时,我们会生成多个 FireworkParticle,每个粒子的速度和方向都是随机的,这样可以让烟花的爆炸效果更加多样化。

结语

本期的分享就结束了,有兴趣的小伙伴可以思考一下如何实现更多有趣的特效哦。

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

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

相关文章

【开源大模型生态9】百度的文心大模型

这张图展示了百度千帆大模型平台的功能架构及其与BML-AI开发平台和百度百舸AI异构计算平台的关系。以下是各个模块的解释&#xff1a; 模型广场&#xff1a; 通用大模型&#xff1a;提供基础的自然语言处理能力。行业大模型&#xff1a;针对不同行业的定制化模型。大模型工具链…

新的 MathWorks 硬件支持包支持从 MATLAB 和 Simulink 模型到高通 Hexagon 神经处理单元架构的自动化代码生成

MathWorks 今天宣布&#xff0c;推出针对 Qualcomm Hexagon™ 神经处理单元&#xff08;NPU&#xff09;的硬件支持包。该处理单元嵌入在 Snapdragon 系列处理器中。MathWorks 硬件支持包&#xff0c;则专门针对 Qualcomm Technologies 的 Hexagon NPU 架构进行优化&#xff0c…

基于SSM的“校园外卖管理系统”的设计与实现(源码+数据库+文档+开题报告)

基于SSM的“校园外卖管理系统”的设计与实现&#xff08;源码数据库文档开题报告) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 消费者系统结构图 商户系统结构图 管理员系统结构图 校…

数据脱敏 (Jackson + Hutool 工具包)

一、简介 系统使用 Jackson 序列化策略&#xff0c;对标注了 Sensitive 注解的属性进行脱敏处理 基于Hutool 脱敏案列&#xff1a; Retention(RetentionPolicy.RUNTIME) Target(ElementType.FIELD) JacksonAnnotationsInside// 表示只对有此注解的字段进行序列化 JsonSeriali…

MySQL高阶1831-每天的最大交易

题目 编写一个解决方案&#xff0c;报告每天交易金额 amount 最大 的交易 ID 。如果一天中有多个这样的交易&#xff0c;返回这些交易的 ID 。 返回结果根据 transaction_id 升序排列。 准备数据 Create table If Not Exists Transactions (transaction_id int, day date, …

吹爆上海交大的大模型实战教程!!—《动手学大模型》附实战教程及ppt

今天分享一个上海交大的免费的大模型课程&#xff0c;有相关教程文档和Slides&#xff0c;目前是2.2K星标&#xff0c;还是挺火的&#xff01; 《动手学大模型》系列编程实践教程&#xff0c; 由上海交通大学2024年春季《人工智能安全技术》课程&#xff08;NIS3353&#xff09…

深入剖析Docker容器安全:挑战与应对策略

随着容器技术的广泛应用&#xff0c;Docker已成为现代应用开发和部署的核心工具。它通过轻量级虚拟化技术实现应用的隔离与封装&#xff0c;提高了资源利用率。然而&#xff0c;随着Docker的流行&#xff0c;其安全问题也成为关注焦点。容器化技术虽然提供了良好的资源隔离&…

SHAP 模型可视化 + 参数搜索策略在轴承故障诊断中的应用

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 Python轴承故障诊断入门教学-CSDN博客 Python轴承故障诊断 (13)基于故障信号特征提取的超强机器学习识别模型-CSDN博客 Python轴承故障诊断 (14)高创新故障识别模型-CSDN…

Linux用户组管理

目录 一、增删改用户组 1.1. 创建一个新的用户组 1.2. 创建用户组并指定ID 1.3. 修改用户组的名 1.4. 修改用户组的ID 1.5. 删除一个用户组 二、用户组中的用户操作 2.1. 添加用户到一个已存在的用户组 2.2. 从用户组中移除用户 注&#xff1a;本章内容全部基于Centos…

论文阅读--Planning-oriented Autonomous Driving(二)

自动驾驶框架的各种设计比较。 ( a )大多数工业解决方案针对不同的任务部署不同的模型。 ( b )多任务学习方案共享一个具有分割任务头的主干。 ( c )端到端范式将感知和预测模块统一起来。以往的尝试要么采用( c.1 )中对规划的直接优化&#xff0c;要么采用( c.2 )中的部分元…

基于PHP的高校毕业生就业服务平台

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于phpMySQL的高校毕业生就业…

html,css基础知识点笔记(二)

9.18&#xff08;二&#xff09; 本文主要教列表的样式设计 1&#xff09;文本溢出 效果图 文字限制一行显示几个字&#xff0c;多余打点 line-height: 1.8em; white-space: nowrap; width: 40em; overflow: hidden; text-overflow: ellipsis;em表示一个文字的大小单位&…

MySQL实战面试题(附案例答案+建表语句+模拟数据+案例深度解析),练完直接碾压面试官

知识点思维导图 案例1 建表语句与模拟数据 用户表 users CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL UNIQUE, signup_date DATE NOT NULL ); INSERT INTO users (username, email, signu…

C++ | Leetcode C++题解之第416题分割等和子集

题目&#xff1a; 题解&#xff1a; class Solution { public:bool canPartition(vector<int>& nums) {int n nums.size();if (n < 2) {return false;}int sum 0, maxNum 0;for (auto& num : nums) {sum num;maxNum max(maxNum, num);}if (sum & 1)…

ICL、CoT、ReAct个人记录

In-Context Learning(ICL) 将一些带有标签的样本拼接起来&#xff0c;作为prompt的一部分。不涉及梯度更新&#xff0c;因此不属于ft CoT 但是其依然属于静态的黑盒子&#xff0c;依靠其推理的结果很难与真实知识保持一致&#xff0c;且限制了推理过程中及时反应和知识更新的…

技术生态系统中的绿色可持续发展与商业模式创新:The Open Group 2024大会引领未来发展趋势

绿色转型与商业模式创新在技术生态系统中的核心地位 在全球范围内&#xff0c;企业正面临着双重挑战&#xff1a;如何在推动技术生态系统创新的同时&#xff0c;践行可持续发展的承诺。随着气候变化压力的增加&#xff0c;绿色经济成为企业发展和创新的必然趋势。然而&#xf…

Python基础(八)——MySql数据库

一.数据库 【库——>表——>数据】 借助数据库对数据进行组织存储&#xff0c;借助SQL语言对数据库、数据进行操作管理 Mysql数据库 下载&#xff1a;https://www.mysql.com/ 查看是否安装配置成功&#xff1a; 安装DBeaver用于Mysql数据库图形化 安装&#xff1a;…

Python语言学习-pandas库学习

一、什么是Pandas库 Pandas是python的第三方库&#xff0c;他用于灵活的数据操作&#xff0c;数据可视化&#xff0c;数据清洗&#xff0c;数据的聚合和转换&#xff0c;数据的可视化 二、安装pandas库 在终端中运行 pip install pandas 导入Pandas库并重命名为pd import …

腹腔镜工具识别与定位系统源码分享

腹腔镜工具识别与定位检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comp…

电机知识总结

一.直流无刷电机&#xff08;BLDC&#xff09; 27N30P指有27个槽&#xff0c;30的极数&#xff0c;它的极对数&#xff1a;30/215,所以是15对极。 N必须是3的倍数&#xff0c;P必须是偶数&#xff0c; 电角度是电气特性&#xff0c;机械角度是空间特性&#xff0c;必须指明是谁…