子墨的博客

总得让实力配上野心


  • 首页

  • 标签71

  • 分类16

  • 归档29

  • 关于

  • 搜索

手把手带你用Java写出炫酷的黑客帝国代码雨效果

置顶 发表于 2020-08-10 分类于 java 阅读次数:
本文字数: 13k 阅读时长 ≈ 12 分钟

前言

相信大家都看过黑客帝国,里面炫酷的代码雨一定给大家留下了深刻的印象,今天,博主就带大家用Java来实现这个特效,提前剧透,博主将在这篇博文里写下超详细的实现思路,相信即便是小白也能够看懂,下面我们开始

准备

  1. jdk1.8+
  2. java ide,如idea,或者eclipse

思路

  1. 首先确定我们要做的是一个窗体应用程序,在窗体中使用画布,画出我们的代码雨,而java中的窗体应用只需要继承JFrame类即可

  2. JFrame只是单纯的窗体容器,而画布,包含在JPanel组件中,现在我们需要将窗体和画布的大小都设置为电脑屏幕的大小,而除了大小之外,我们还需要对窗体进行一系列的设置,比如不显示标题栏,背景为黑色,鼠标不可见,按esc键退出程序等等,完成这些设置之后,我们的代码是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.awt.image.MemoryImageSource;

    public class Rain extends JFrame {
    // 屏幕大小
    private Dimension screen;
    // 画布容器
    private JPanel graphicsPanel;

    public Rain() {
    // 获取屏幕大小
    screen = getToolkit().getScreenSize();
    // 设置去掉标题栏
    setUndecorated(true);
    // 设置光标不可见
    Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
    Image image = defaultToolkit.createImage(new MemoryImageSource(0, 0, null, 0, 0));
    Cursor invisibleCursor = defaultToolkit.createCustomCursor(image, new Point(0, 0), "cursor");
    setCursor(invisibleCursor);
    // 设置全屏
    setSize(screen);
    // 设置按esc键退出
    addKeyListener(new KeyListener() {
    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
    System.exit(0);
    }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
    });

    graphicsPanel = new GraphicsPanel();
    // 设置面板
    setContentPane(graphicsPanel);
    // 设置界面可显示
    setVisible(true);
    }

    private class GraphicsPanel extends JPanel {
    @Override
    public void paint(Graphics g) {
    // 设置背景黑色填充
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, screen.width, screen.height);
    }
    }

    public static void main(String[] args) {
    new Rain();
    }
    }

    现在运行这个程序,不必奇怪,也不必害怕,我们会得到一个纯黑色的界面,我们可以通过按esc键退出程序,或者调出任务管理器杀掉该进程

  3. 准备工作做好了,现在我们来分析一下字符雨所具有的一些元素,很明显,我们需要在这个页面上画出一些随机字符,于是我们需要设置字符的大小,需要获取随机的字符,并且需要在页面上画出来,所以我们需要划分一下界面的行和列,基于以上分析,我们先画一个充满了随机字符的页面,于是我们将代码改进成这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.awt.image.MemoryImageSource;
    import java.util.Random;

    public class Rain extends JFrame {
    // 屏幕大小
    private Dimension screen;
    // 画布容器
    private JPanel graphicsPanel;
    // 字符大小
    private int size = 16;
    // 划分屏幕区域,行数
    private int rows;
    // 划分屏幕区域,列数
    private int cols;
    // 随机
    private Random random = new Random();

    public Rain() {
    // 获取屏幕大小
    screen = getToolkit().getScreenSize();
    // 设置去掉标题栏
    setUndecorated(true);
    // 设置光标不可见
    Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
    Image image = defaultToolkit.createImage(new MemoryImageSource(0, 0, null, 0, 0));
    Cursor invisibleCursor = defaultToolkit.createCustomCursor(image, new Point(0, 0), "cursor");
    setCursor(invisibleCursor);
    // 设置全屏
    setSize(screen);
    // 设置按esc键退出
    addKeyListener(new KeyListener() {
    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
    System.exit(0);
    }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
    });

    graphicsPanel = new GraphicsPanel();
    // 设置面板
    setContentPane(graphicsPanel);
    // 设置界面可显示
    setVisible(true);

    // 设置行数和列数
    rows = screen.width / size;
    cols = screen.height / size;
    }

    /**
    * 获取随机ASCII可见字符
    *
    * @return
    */
    private char getRandomChar() {
    return (char) (new Random().nextInt(94) + 33);
    }

    /**
    * 画布容器
    */
    private class GraphicsPanel extends JPanel {
    @Override
    public void paint(Graphics g) {
    // 设置背景黑色填充
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, screen.width, screen.height);

    // 遍历划分的区域,将所有位置填满随机字符
    for (int x = 0; x < rows; x++) {
    for (int y = 0; y < cols; y++) {
    // 设置随机字符的颜色和字体
    g.setColor(Color.CYAN);
    g.setFont(new Font("黑体", Font.BOLD, size));
    // 画
    g.drawString(String.valueOf(getRandomChar()), x * size, y * size);
    }
    }
    }
    }

    public static void main(String[] args) {
    new Rain();
    }
    }

    现在运行代码,你将会得到类似下面这样的界面,页面是静态的
    充满随机字符的页面

  4. 现在我们来尝试让界面动起来,怎么做呢,很简单,我们可以开一个线程,专门去刷新页面,具体就是定时调用JPanel的repaint方法,于是代码进一步更改成这样了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.awt.image.MemoryImageSource;
    import java.util.Random;

    public class Rain extends JFrame {
    // 屏幕大小
    private Dimension screen;
    // 画布容器
    private JPanel graphicsPanel;
    // 字符大小
    private int size = 16;
    // 划分屏幕区域,行数
    private int rows;
    // 划分屏幕区域,列数
    private int cols;
    // 随机
    private Random random = new Random();

    public Rain() {
    // 获取屏幕大小
    screen = getToolkit().getScreenSize();
    // 设置去掉标题栏
    setUndecorated(true);
    // 设置光标不可见
    Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
    Image image = defaultToolkit.createImage(new MemoryImageSource(0, 0, null, 0, 0));
    Cursor invisibleCursor = defaultToolkit.createCustomCursor(image, new Point(0, 0), "cursor");
    setCursor(invisibleCursor);
    // 设置全屏
    setSize(screen);
    // 设置按esc键退出
    addKeyListener(new KeyListener() {
    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
    System.exit(0);
    }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
    });

    graphicsPanel = new GraphicsPanel();
    // 设置面板
    setContentPane(graphicsPanel);
    // 设置界面可显示
    setVisible(true);

    // 设置行数和列数
    rows = screen.width / size;
    cols = screen.height / size;

    // 开启定时刷新
    new Refresher(100, graphicsPanel).start();
    }

    /**
    * 获取随机ASCII可见字符
    *
    * @return
    */
    private char getRandomChar() {
    return (char) (new Random().nextInt(94) + 33);
    }

    /**
    * 画布容器
    */
    private class GraphicsPanel extends JPanel {
    @Override
    public void paint(Graphics g) {
    // 设置背景黑色填充
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, screen.width, screen.height);

    // 遍历划分的区域,将所有位置填满随机字符
    for (int x = 0; x < rows; x++) {
    for (int y = 0; y < cols; y++) {
    // 设置随机字符的颜色和字体
    g.setColor(Color.CYAN);
    g.setFont(new Font("黑体", Font.BOLD, size));
    // 画
    g.drawString(String.valueOf(getRandomChar()), x * size, y * size);
    }
    }
    }
    }

    /**
    * 定时刷新画布容器
    */
    private class Refresher extends Thread {
    private int sleep;
    private JPanel panel;

    public Refresher(int sleep, JPanel panel) {
    this.sleep = sleep;
    this.panel = panel;
    }

    @Override
    public void run() {
    // 死循环固定休眠,即可达到定时刷新的效果
    while (true) {
    try {
    panel.repaint();
    Thread.sleep(sleep);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }

    public static void main(String[] args) {
    new Rain();
    }
    }

    现在运行程序,可以明显看到页面动起来了

  5. 现在再来仔细分析一下,黑客帝国的代码雨,可以看到,雨滴是一列一列从屏幕最上面,慢慢的下落到最下面;雨滴的头是和雨滴的身体的颜色不一样;雨滴的长度不会很长;重复这个过程;基于这个过程,我们不难想到将代码改写如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    import wiki.zimo.helper.CharHelper;

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.awt.image.MemoryImageSource;
    import java.util.Random;

    public class Rain extends JFrame {
    // 屏幕大小
    private Dimension screen;
    // 画布容器
    private JPanel graphicsPanel;
    // 字符大小
    private int size = 16;
    // 划分屏幕区域,行数
    private int rows;
    // 划分屏幕区域,列数
    private int cols;
    // 随机
    private Random random = new Random();
    // 保存每列字符下落的坐标,雨滴的头的初始坐标——Y坐标
    private int[] drops;
    // 雨滴的身体最大长度
    private int maxLength = 30;
    // 一次下落的最大列数
    private int maxDrop = 5;

    public Rain() {
    // 获取屏幕大小
    screen = getToolkit().getScreenSize();
    // 设置去掉标题栏
    setUndecorated(true);
    // 设置光标不可见
    Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
    Image image = defaultToolkit.createImage(new MemoryImageSource(0, 0, null, 0, 0));
    Cursor invisibleCursor = defaultToolkit.createCustomCursor(image, new Point(0, 0), "cursor");
    setCursor(invisibleCursor);
    // 设置全屏
    setSize(screen);
    // 设置按esc键退出
    addKeyListener(new KeyListener() {
    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
    System.exit(0);
    }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
    });

    graphicsPanel = new GraphicsPanel();
    // 设置面板
    setContentPane(graphicsPanel);
    // 设置界面可显示
    setVisible(true);

    // 设置行数和列数
    rows = screen.width / size;
    cols = screen.height / size;


    drops = new int[rows];
    // 随机drops
    for (int i = 0; i < drops.length; i++) {
    drops[i] = random.nextInt(cols);
    }

    // 开启定时刷新
    new Refresher(100, graphicsPanel).start();
    }

    /**
    * 获取随机ASCII可见字符
    *
    * @return
    */
    private char getRandomChar() {
    return (char) (new Random().nextInt(94) + 33);
    }

    /**
    * 画布容器
    */
    private class GraphicsPanel extends JPanel {
    @Override
    public void paint(Graphics g) {
    // 设置背景黑色填充
    Font font = g.getFont();
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, screen.width, screen.height);

    // 按行遍历
    for (int x = 0; x < rows; x++) {
    // 取出随机的y坐标
    int y = drops[x];
    // 画出雨滴的头
    g.setColor(Color.CYAN);
    g.setFont(new Font("黑体", Font.BOLD, size));
    g.drawString(String.valueOf(CharHelper.getRandomChar()), x * size, y * size);

    // 倒着往回画出雨滴的身体,颜色渐变
    int color = 255;
    int len = 0;
    while (--y > 0) {
    len++;
    color -= 255 / y;
    if (color < 0) {
    color = 0;
    }
    g.setFont(font);
    g.setColor(new Color(0, color, 0, color));
    g.drawString(String.valueOf(CharHelper.getRandomChar()), x * size, y * size);
    if (len >= maxLength) {
    break;
    }
    }

    // 随机雨滴下落
    drops[x] += random.nextInt(maxDrop);
    // 完全坠落
    if (drops[x] >= cols + maxLength) {
    // 随机开始
    // drop[x] = random.nextInt(cols);
    // 从头开始
    drops[x] = 0;
    }
    }
    }
    }

    /**
    * 定时刷新画布容器
    */
    private class Refresher extends Thread {
    private int sleep;
    private JPanel panel;

    public Refresher(int sleep, JPanel panel) {
    this.sleep = sleep;
    this.panel = panel;
    }

    @Override
    public void run() {
    // 死循环固定休眠,即可达到定时刷新的效果
    while (true) {
    try {
    panel.repaint();
    Thread.sleep(sleep);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }

    public static void main(String[] args) {
    new Rain();
    }
    }
  6. 最终效果

    最终效果图

相关文章
  • 从零开始 部署 wisedu-unified-login-api
  • 2019年第十一届蓝桥杯校内选拔赛JavaB组题解
觉得不错,打赏一下
子墨 微信支付

微信支付

子墨 支付宝

支付宝

  • 本文作者: 子墨
  • 本文链接: https://blog.zimo.wiki/posts/a49748b4/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
java 黑客帝国 代码雨
记一次排查jetbrains系列ide(如idea,pycharm等) alt+f12 快捷键被占用问题解决过程
从零开始 部署 wisedu-unified-login-api
  • 文章目录
  • 站点概览
子墨

子墨

子墨的博客
29 日志
16 分类
71 标签
RSS
GitHub E-Mail CSDN QQ Gitee
友情链接
  • 高正杰的博客

Tag Cloud

  • 8076
  • HttpCanary1
  • JavaScript2
  • Jupyter Notebook1
  • c++1
  • centos1
  • cuda1
  • c语言6
  • deepin1
  • dns2tcp1
  • fiddler1
  • hexo2
  • html1
  • i至诚1
  • jar1
  • java3
  • jetbrains1
  • linux3
  • linux server1
  • markdown1
  • nginx1
  • nodejs1
  • python2
  • python31
  • pytorch1
  • tesseract-ocr1
  • ubantu1
  • virtualenvwrapper-win1
  • war1
  • windows2
  • windows server1
  • 个人博客2
  • 代理1
  • 代码托管1
  • 代码雨1
  • 伪装位置1
  • 使用指南1
  • 刷recovery1
  • 力扣1
  • 劫持1
  • 双系统1
  • 小爱课程表1
  • 小米61
  • 常识1
  • 快捷键冲突1
  • 抓包1
  • 折腾1
  • 挖矿木马1
  • 服务器1
  • 机器学习2
  • 极客1
  • 树梅派4001
  • 油猴脚本1
  • 爬虫1
  • 环境搭建1
  • 直播服务器1
  • 科普1
  • 程序综合设计6
  • 算法1
  • 终端1
  • 编译1
  • 考研7
  • 自动打卡1
  • 蓝桥杯1
  • 解锁bl1
  • 运维1
  • 部署1
  • 钉子户1
  • 题解1
  • 黑客帝国1
  • 黑苹果1
  1. 1. 前言
  2. 2. 准备
  3. 3. 思路
蜀ICP备18029083号 © 2019 – 2022 子墨 | 站点总字数: 167k | 站点阅读时长 ≈ 2:32
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Pisces v7.3.0
载入网站运行时间中...
|
0%