C++实现代码雨
2023-07-16 19:29:33

0. 最终效果

只适用于Linux系统。

1. 思路

将每一列分开来处理。

每一列上半部分暗一些,最下面的字符为白色。

2. 遇到的坑

清屏导致终端闪烁

不论是调用<ncurses.h>里的clear()函数,还是直接用不优雅的system("clear"),在清屏时,都会导致显示的内容闪烁。

我没有查到什么好的解决方案,我通过使用输出空字符代替清屏来解决这个问题。

终端尺寸变化导致显示效果不全

Windows下可以使用Hook机制——maybe,我没在Windows下写过复杂的东西——而在Linux下,貌似可以使用类似的信号机制,但是信号的Handler只能传递一个int类型的变量,我还没有想到如何在这种情况下,通过它修改一个类里面的值。

我的确可以使用一种不甚优雅的方式:在每次处理列的时候,都去获取一次窗口的长和宽。

可以这样做,设立一个全局变量,当每次收到SIGWINCH信号的时候,就去修改这个变量的值,主函数中每次循环都去根据这个变量的值,调整窗口大小。

代码

Matrix.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef MATRIX_HPP
#define MATRIX_HPP

#include <ncurses.h>
#include <forward_list>

class Matrix {
public:
Matrix(int show_length = 9, int color = 0);
~Matrix();
bool Run();
bool ShowColumn(int column);
private:
WINDOW* my_window;
int row_recorder[2000];
int window_height;
int window_width;
int show_length;
int color;
};

#endif

Matrix.cpp
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
/*****************************************
* Filename: Matrix.cpp
* Author: Coder109
* Date: 2023-07-16 07:20:42
* Description: Deal with the matrix rain
*****************************************/

#include "Matrix.hpp"
#include <cstdlib>
#include <cstring>
#include <ncurses.h>
#include <unistd.h>

const char character_list[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYXZ";

Matrix::Matrix(int show_length, int color) {
this->show_length = show_length;
this->color = color;

this->my_window = initscr();
cbreak();
noecho();
getmaxyx(this->my_window, this->window_height, this->window_width);

start_color();
init_pair(1, COLOR_BLUE, COLOR_BLACK);
init_pair(2, COLOR_WHITE, COLOR_BLACK);
init_pair(3, COLOR_BLACK, COLOR_BLACK);
attron(A_BOLD);

for(int i = 0; i < this->window_width; i++) {
this->row_recorder[i] = -rand() % 30;
}
}

Matrix::~Matrix() {}

bool Matrix::ShowColumn(int column) {
int curr_row = 0;
if(this->row_recorder[column] >= this->window_height + this->show_length) {
curr_row = 0;
this->row_recorder[column] = 0;
} else {
curr_row = this->row_recorder[column]++;
}

// Output empty char in order to solve the problem:
// When clearing the screen, the terminal will flicker.
for(int curr_processing_row = 0;
curr_processing_row < curr_row - this->show_length;
curr_processing_row++) {
attron(COLOR_PAIR(3));
mvaddch(curr_processing_row, column, ' ');
attroff(COLOR_PAIR(3));
}

// Upper half of the column should be darker, and the bottom char should be white
for(int curr_processing_row = curr_row - this->show_length;
curr_processing_row <= curr_row;
++curr_processing_row) {
char show_char = character_list[std::rand() % strlen(character_list)];
if(curr_processing_row < 0) {
continue;
} else if(curr_processing_row >= this->window_height) {
break;
} else if(curr_processing_row == curr_row) {
attron(COLOR_PAIR(2));
mvaddch(curr_processing_row, column, show_char);
attroff(COLOR_PAIR(2));
} else if(curr_processing_row < curr_row - this->show_length / 2) {
attron(COLOR_PAIR(1));
attron(A_DIM);
mvaddch(curr_processing_row, column, show_char);
attroff(A_DIM);
attron(COLOR_PAIR(1));
} else {
attron(COLOR_PAIR(1));
mvaddch(curr_processing_row, column, show_char);
attroff(COLOR_PAIR(1));
}
}

// Output empty char in order to solve the problem:
// When clearing the screen, the terminal will flicker.
for(int curr_processing_row = curr_row + 1;
curr_processing_row < this->window_height;
curr_processing_row++) {
attron(COLOR_PAIR(3));
mvaddch(curr_processing_row, column, ' ');
attroff(COLOR_PAIR(3));
}

return true;
}

bool Matrix::Run() {
for(int curr_column = 0; curr_column < window_width; curr_column++) {
if(!this->ShowColumn(curr_column)) {
return false;
}
}
refresh();
usleep(70000);
return true;
}

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include "src/Matrix.hpp"

using namespace std;

int main(int argc, char **argv) {
Matrix *my_matrix = new Matrix();
while(1) {
my_matrix->Run();
}
delete my_matrix;
return 0;
}