前言
“Hello World”是大多数初学者接触计算机编程的第一个程序,也是最简单的程序。但面对枯燥乏味的“黑框框”,大多数初学者学习兴趣并不强烈。因此,我开发出此"粒子渲染版"Hello World,感受计算机编程不一样的美。
完整代码及注释
// 设置 Windows 版本和 API 版本
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
// 包含必要的头文件
#include<graphics.h>
#include<conio.h>
#include<ctime>
#include<random>
#include <ShellScalingApi.h> // 引用头文件
#pragma comment(lib, "Shcore.lib") // 链接库文件
using namespace std;
// 常量定义
const double PI = 3.1415926;
// 随机数生成器
default_random_engine e(time(0));
uniform_int_distribution<int> random_color(0, 255);
//初始化界面(1920*1080)
int xScreen = 1920;
int yScreen = 1080;
//初始化函数
void init()
{ // 获取图形窗口句柄
HWND hwnd = initgraph(xScreen, yScreen);
// 设置窗口样式,去除标题栏
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & (!WS_CAPTION));
// 设置窗口位置和大小
SetWindowPos(hwnd, HWND_TOP, 0, 0, xScreen, yScreen, SWP_SHOWWINDOW);
// 开始图形绘制批处理
BeginBatchDraw();
}
// 绘制矩形的函数
void rectangle(DWORD* pMem, int x1, int y1, int x2, int y2, COLORREF color)
{
for (; y1 <= y2; ++y1)
for (int i = y1 * xScreen + x1, x = x1; x <= x2; ++i, ++x)
if (i > 0 && i < xScreen * yScreen) pMem[i] = BGR(color);
}
// 粒子类
class Paticals {
public:
// 构造函数
Paticals(TCHAR* str, int X, int Y, int size);
// 获取目标位置
void get_destination();
// 移动粒子
void move();
private:
// 成员变量
int X, Y;
int size;
TCHAR* str;
DWORD* ImageBuffer;
// 存储粒子属性的向量
vector<COLORREF> color;
vector<double> dest_x;
vector<double> dest_y;
vector<double> origin_x;
vector<double> origin_y;
vector<double> v;
};
// 粒子类的构造函数
Paticals::Paticals(TCHAR* str, int X, int Y, int size) :
str(str), X(X), Y(Y), size(size)
{ // 随机数分布
uniform_int_distribution<int> random_x(0, xScreen - 1);
uniform_int_distribution<int> random_y(0, yScreen - 1);
uniform_real_distribution<double> random_v(0.5, 1);
// 获取目标位置和粒子属性
get_destination();
// 遍历粒子属性向量,初始化粒子属性
for (int i = 0; i < dest_x.size(); ++i)
{
v.push_back(random_v(e));
origin_x.push_back(random_x(e));
origin_y.push_back(random_y(e));
color.push_back(RGB(random_color(e), random_color(e), random_color(e)));
}
}
// 获取目标位置的函数
void Paticals::get_destination()
{
// 获取图像缓冲区
ImageBuffer = GetImageBuffer();
// 设置文本样式
settextstyle(size, 0, L"楷体");
settextcolor(WHITE);
// 在指定位置绘制文本
outtextxy(X, Y, str);
int i = 0;
// 外层循环遍历2D网格的y坐标。
for (int y = 0; y < yScreen; ++y)
{
// 计算一维数组'ImageBuffer'的索引。
for (int x = 0; x < xScreen; ++x)
{
// 检查索引是否在范围内。
int index = y * xScreen + x;
// 检查'ImageBuffer'中当前像素是否非零。
if (index > 0 && index < xScreen * yScreen)
{
if (ImageBuffer[index] != 0)
{
int flag = 0; // 将标志变量初始化为0。
// 嵌套循环(2x2)检查当前像素周围的6x6区域。
for (int _y = y; _y < y + 6; ++_y)
for (int _x = x; _x < x + 6; ++_x)
{
index = _y * xScreen + _x;
// 检查索引是否在范围内。
if (index > 0 && index < xScreen * yScreen)
// 检查区域中的像素是否不是黑色。
if (ImageBuffer[index] != BGR(0, 0, 0)) ++flag;
}
// 检查区域中是否有超过25个像素不是黑色。
if (flag > 25)
{
// 记录区域的坐标。
dest_x.push_back(x);
dest_y.push_back(y);
// 将区域中的像素设置为黑色并跳到下一个区域。
for (int _y = y; _y < y + 6; ++_y)
{
for (int _x = x; _x < x + 6; ++_x)
{
int index = _y * xScreen + _x;
// 检查索引是否在范围内。
if (index > 0 && index < xScreen * yScreen) ImageBuffer[index] = BLACK;
}
}
x += 5;
}
}
}
}
}
}
// 根据其目的地和速度移动粒子的方法。
void Paticals::move()
{
for (int i = 0; i < dest_x.size() && i < origin_x.size(); ++i)
{
// 如果y坐标不等于目的地,则更新y坐标。
if (int(origin_x[i]) != int(dest_x[i]))
origin_x[i] > dest_x[i] ? origin_x[i] -= v[i] : origin_x[i] += v[i];
// 如果y坐标不等于目的地,则更新y坐标。
if (int(origin_y[i]) != int(dest_y[i]))
origin_y[i] > dest_y[i] ? origin_y[i] -= v[i] : origin_y[i] += v[i];
// 根据更新后的位置和颜色在'ImageBuffer'上绘制矩形。
rectangle(ImageBuffer, origin_x[i], origin_y[i], origin_x[i] + 6 - 2, origin_y[i] + 6 - 2, color[i]);
}
}
// 主函数
int main()
{
// 设置进程的DPI感知,以适应高分辨率显示器
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
// 初始化绘图环境等内容
init();
//输出的文字内容英语中文都可,可替换
TCHAR str[] = _T("Hello World!");
//60,320是输出文字的坐标(文字的左上角),300是字体大小
//所有参数均可改
Paticals characters(str, 60, 320, 300);
// 主循环,直到键盘有输入
while (!_kbhit())
{
// 清空绘图区域
cleardevice();
// 移动并绘制粒子效果,实现文字的动态效果
characters.move();
// 刷新绘图缓冲区
FlushBatchDraw();
}
// 等待用户按下任意键
_getch();
// 关闭图形窗口
closegraph();
// 返回0表示程序正常结束
return 0;
}
步骤
-
图形初始化: 在
init
函数中,程序通过initgraph
初始化图形窗口,设置窗口的样式并去除标题栏。然后通过SetWindowPos
设置窗口的位置和大小。最后,通过BeginBatchDraw
开始图形绘制批处理,这可以提高图形的绘制效率。 -
绘制矩形: 使用
rectangle
函数在图形窗口中绘制矩形。该函数接受左上角和右下角的坐标以及颜色,通过遍历坐标范围内的每个点,将相应位置的像素颜色设置为指定颜色。 -
粒子类: 定义了一个
Paticals
类来表示粒子。在构造函数中,通过传递粒子的初始位置、字符、大小等参数,生成具有随机属性的粒子群体。每个粒子有目标位置、当前位置、速度和颜色等属性。 -
目标位置计算:
get_destination
函数根据粒子的数量和初始位置,计算出每个粒子的目标位置,使它们在一个圆形轨迹上均匀分布。 -
粒子移动:
move
函数用于更新粒子的位置,并在图形窗口中绘制粒子。通过迭代每个粒子,根据其速度朝着目标位置移动,并绘制一个小矩形代表粒子。图形窗口被清空,然后绘制所有粒子,最后通过putimage
函数将图形缓冲区中的内容显示到图形窗口上。
总体来说,这个程序通过粒子的目标位置和当前位置之间的差异,以及每个粒子的速度,模拟了一个简单的粒子系统,并通过图形库在窗口中实时展示粒子的运动。
运行效果
开发环境:
Visual Studio 2022 version 17.8.2Thank You for Downloading Visual Studio Community Edition (microsoft.com)
EasyX_2023大暑版 EasyX Graphics Library for C++
结言
欢迎各位初学者及此方面专家就此程序,进行讨论。