win32 数字雨

效果

在这里插入图片描述

代码

main.cpp

#include "rain.h"
#include <time.h>
#include <vector>

#define MY_TIMER_ID 123         // 计时器ID
#define MY_TIMER_TIME 100       // 计时器间隔时间

LRESULT CALLBACK CallBack(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	// 设置窗口结构体信息
    WNDCLASSEX we;
    we.cbClsExtra = 0;
    we.cbSize = sizeof (WNDCLASSEX);
    we.cbWndExtra = 0;
    we.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);	// 背景定为全黑色
    we.hCursor = NULL;	// 不要鼠标
    we.hIcon = NULL;
    we.hIconSm = NULL;
    we.hInstance = hInstance;
    we.lpfnWndProc = CallBack;
    we.lpszClassName = TEXT("rain");
    we.lpszMenuName = NULL;
    we.style = CS_HREDRAW | CS_VREDRAW;	// 窗口样式没什么特殊的

    if (0 == RegisterClassEx(&we))
    {
        MessageBox(NULL, TEXT("Register Fail"), TEXT("提示"), MB_OK);
        return -1;
    }

    ShowCursor(FALSE); // 隐藏鼠标

	// WS_EX_TOPMOST --- 该窗口应放置在所有非最上面的窗口之上,并且即使在停用该窗口的情况下也应保持在它们之上。
	// WS_POPUP --- 该窗口是一个弹出窗口。
	// 窗口大小为屏幕大小
    HWND hWnd = CreateWindowEx(WS_EX_TOPMOST, TEXT("rain"), NULL, WS_POPUP,
		0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
		NULL, NULL, hInstance, NULL);
    if (NULL == hWnd)
    {
        MessageBox(NULL, TEXT("Create Fail"), TEXT("提示"), MB_OK);
        return -1;
    }

    ShowWindow( hWnd, nCmdShow);

    MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

LRESULT CALLBACK CallBack(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	static std::vector<Rains*> container;	// 雨容器 统一操作
	static int screenX;	// 屏幕宽
	static int screenY;	// 屏幕高
	
    switch (nMsg)
    {
        case WM_CREATE:
		{
			// 获取屏幕信息
			screenX = GetSystemMetrics(SM_CXSCREEN);
			screenY = GetSystemMetrics(SM_CYSCREEN);

			// 初始化四种雨
			static Rains rain_5(10, 5, 5, screenX, screenY);
			static Rains rain_10(15, 10, 10, screenX, screenY);
			static Rains rain_20(20, 20, 30, screenX, screenY);
			static Rains rain_40(10, 40, 40, screenX, screenY);
			container.push_back(&rain_5);
			container.push_back(&rain_10);
			container.push_back(&rain_20);
			container.push_back(&rain_40);

			SetTimer(hWnd, MY_TIMER_ID, MY_TIMER_TIME, NULL);	// 打开定时器
			srand((unsigned)time(NULL));	// 设置随机数种子

			break;
		}

		// 定时器绘制雨
        case WM_TIMER:
        {
            HDC hdc = GetDC(hWnd);
			// 创建内存DC
            HDC insidehdc = CreateCompatibleDC(hdc);
			// 创建兼容性位图
            HBITMAP hbitmap = CreateCompatibleBitmap(hdc, screenX, screenY);
            // 将DC和位图绑定在一起
            SelectObject(insidehdc, hbitmap);

			// 窗口刷黑
			PatBlt(insidehdc, 0, 0, screenX, screenY, BLACKNESS);
			// 设置背景透明
			SetBkMode(insidehdc, TRANSPARENT);

			// 绘制雨
			for (auto rains : container)
			{
				rains->paint(insidehdc);
			}

            // 将内存DC传递到窗口DC
            BitBlt(hdc, 0, 0, screenX, screenY, insidehdc, 0, 0, SRCCOPY);

            DeleteObject(hbitmap);
            DeleteDC(insidehdc);
            DeleteDC(hdc);

			break;
        }

        case WM_KEYDOWN:
            switch (wParam)
            {
                case VK_ESCAPE:
                {
					PostMessage(hWnd, WM_DESTROY, 0, 0);	// 按esc退出 非阻塞
                }
            }
            break;

        case WM_DESTROY:
            KillTimer(hWnd, MY_TIMER_ID);       // 关闭定时器
            PostQuitMessage (0);
            break;
    }
    
	return DefWindowProc(hWnd, nMsg, wParam, lParam);
}

rain.h

#ifndef _RAIN_H
#define _RAIN_H

#include <Windows.h>

/// 数字雨
class Rains
{
public:
	Rains(int rainLength, int rainWidth, int speed, int screenX, int screenY);
	~Rains();
	void paint(HDC &insidehdc);

private:
	const int SPACE = 3;			// 雨之间间隔为宽度的倍数
	const double PROPORTION = 1.5;	// 字母宽高比例
	int screenX;	// 屏幕宽度
	int screenY;	// 屏幕高度

	int rainLength;	// 长度
	int rainWidth;	// 宽度
	int rainNum;	// 数量
	int *rainPos;	// 位置
	char **rainStr;	// 内容
	HFONT hfont;	// 字体
	int speed;		// 下落速度

	bool isNumberInEnglishLetters(int randomNumber);
	char createRandCharacter();
	void randChangeRainStr();

	bool isRainOutOfScreen(int i);
	void rain();
};

#endif // !_RAIN_H

rain.cpp

#include "rain.h"

Rains::Rains(int rainLength, int rainWidth, int speed, int screenX, int screenY) : rainLength(rainLength), rainWidth(rainWidth), speed(speed), screenX(screenX), screenY(screenY)
{
	this->rainNum = screenX / (rainWidth * SPACE);

	rainPos = (int*)malloc(rainNum * sizeof(int));
	for (int i = 0; i < rainNum; ++i)
	{
		rainPos[i] = rand() % screenY;	// 使雨在一开始下落的位置参差不齐
	}

	rainStr = (char**)malloc(sizeof(char*) * this->rainNum);
	for (int n = 0; n < rainNum; ++n)
	{
		rainStr[n] = (char*)malloc(rainLength * sizeof(char));
	}

	hfont = CreateFont(int(rainWidth * PROPORTION),	// 字体的高度 单位px
		rainWidth,									// 字体的宽度 单位px
		0,											// 字体的倾斜角
		0,											// 字体的倾斜角
		FW_BOLD,									// 字体的粗细
		0,											// 是否为斜体
		0,											// 是否有下划线
		0,											// 是否有删除线
		DEFAULT_CHARSET,							// 使用的字符集
		OUT_DEFAULT_PRECIS,							// 指定如何选择字体
		CLIP_DEFAULT_PRECIS,						// 确定剪裁的精度
		DRAFT_QUALITY,								// 如何与选择的字体符合
		FIXED_PITCH | FF_SWISS,						// 间距标志和属性标志
		TEXT("Fixedays")							// 字体的名字
	);
}

Rains::~Rains()
{
	if (NULL != rainPos)
	{
		free(rainPos);
		rainPos = NULL;
	}

	if (NULL != rainStr)
	{
		for (int n = 0; n < rainNum; ++n)
		{
			free(rainStr[n]);
			rainStr[n] = NULL;
		}

		free(rainStr);
		rainStr = NULL;
	}

	if (NULL != hfont)
	{
		DeleteObject(hfont);
	}
}

// 画雨
void Rains::paint(HDC &insidehdc)
{
	randChangeRainStr();
	rain();

	// 绘制雨
	SelectObject(insidehdc, hfont);
	for (int i = 0; i < rainNum; ++i)
	{
		SetTextColor(insidehdc, RGB(255, 255, 255));
		for (int j = 0; j < rainLength; ++j)
		{
			TextOut(insidehdc, i * rainWidth * SPACE, int(rainPos[i] - j * rainWidth * PROPORTION), &rainStr[i][j], 1);
			SetTextColor(insidehdc, RGB(0, 255 - 255 / rainLength, 0));
		}
	}
}

// 数字是否为字母
bool Rains::isNumberInEnglishLetters(int randomNumber)
{
	return (randomNumber >= 65 && randomNumber <= 90) || randomNumber >= 97;
}

// 产生随机字符
char Rains::createRandCharacter()
{
	int randomNumber = 0;
	while (1)
	{
		randomNumber = rand() % 123;
		if (isNumberInEnglishLetters(randomNumber))
		{
			break;
		}
	}
	return randomNumber;
}

// 随机改变雨的内容
void Rains::randChangeRainStr()
{
	for (int i = 0; i < rainNum; ++i)
	{
		for (int j = 0; j < rainLength; ++j)
		{
			rainStr[i][j] = createRandCharacter();
		}
	}
}

// 雨是否超出了屏幕
bool Rains::isRainOutOfScreen(int i)
{
	return rainPos[i] - rainWidth * PROPORTION * rainLength >= screenY;
}

// 雨往下落
void Rains::rain()
{
	for (int i = 0; i < rainNum; ++i)
	{
		rainPos[i] += speed;

		// 超过屏幕下方的部分重新回到上方
		if (isRainOutOfScreen(i))
		{
			rainPos[i] = 0;
		}
	}
}

思路

使用定时器每隔一段时间刷新一下屏幕。
每次数字雨都下落一定高度,最后超出屏幕重置位置。

链接

百度云链接:https://pan.baidu.com/s/1g2lqs3cFjwv2W35yvvSmUw
提取码:ieug

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值