流星雨屏幕程序

之前看到朋友曬流星雨屏幕圖,今天就來實現這個流星雨屏幕程序。

語言:C++,平臺:VS,框架:win32。

  首先來看看程序運行的情況。

在這裏插入圖片描述
  接着來介紹一下程序的主題部分,分爲三個部分:窗口的建立數據的存儲消息的處理

窗口的建立

  主要功能就是創建一個窗口來承載要顯示的東西,否則不可能直接在屏幕上面就操作吧,那你的屏幕上面豈不是會被搞成亂七八糟,還如何使用?

  廢話不多說,先創建一個窗口類(WNDCLASS)來定義一下要創建的窗口類型,主要類型就是:窗口風格,窗口的消息處理函數,窗口的圖標、光標。背景顏色等等。之後註冊該窗口類。

    TCHAR szClassName[] = TEXT("數字雨");    //窗口類名
	HWND hwnd;
	MSG msg;          //消息函數
	WNDCLASS wndclass;
	
	wndclass.style = CS_HREDRAW | CS_VREDRAW;       //窗口風格,垂直水平窗口改變就重新繪製
	wndclass.lpfnWndProc = WndProc;  //消息處理函數
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(hInstance,  MAKEINTRESOURCE(IDI_ICON1));   //加載一個圖標
	wndclass.hCursor = NULL;
	wndclass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szClassName;      //窗口類名

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, _T("註冊窗口失敗"), _T("提示"), MB_ICONERROR);
		return -1;
	}

  註冊成功之後就調用API創建窗口,指定窗口創建的位置大小邊框風格菜單等,即完成創建窗口。

  因爲是全屏,所以不能帶標題欄菜單欄等窗口類型,同時創建的窗口是彈出式窗口

    hwnd = CreateWindow(szClassName, NULL,WS_DLGFRAME | WS_THICKFRAME | WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL);
	if (!hwnd)
	{
		MessageBox(NULL, _T("創建窗口失敗"), _T("提示"), MB_ICONERROR);
		return -1;
	}

  創建完成之後,就顯示窗口,因爲是全屏顯示,所以最大化顯示窗口

	ShowWindow(hwnd, SW_SHOWMAXIMIZED);
	UpdateWindow(hwnd);
	ShowCursor(FALSE);

  然後開始消息循環機制

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

數據的存儲

  由於要顯示一些數字,當然也可以是其他漢字或者英文字母,所以這裏定義一個隊列來存儲這些數據。

  創建一個雙向鏈表來存儲字符

typedef struct charList
{
	struct charList * prev;
	TCHAR ch;    //放字符
	struct charList * next;
}CharList;

  創建一個循環隊列來存放這些字符串以及類型其屬性,比如字符數多少,顯示的位置等等。

typedef struct tagCharColumn
{
	struct charList * head, *cur; //頭結點和尾結點
	int x, y, iShownLen, iStrNum; //顯示位置,顯示字數,字符數
}CharQueue;

  然後開始創建隊列,上面定義了7組字符串,爲了隨機顯示,每次創建該隊列的時候,就隨機選取一組字符串,然後設置顯示的位置。

struct showChar
{
	TCHAR myChar[60];
	int iNum; //字符個數
}charArr[7] = {
	{ TEXT("10101010101010101011010"), 23 },
	{ TEXT("1010110101010101010101"), 21 },
	{ TEXT("10101111101100100001010100001"), 28 },
	{ TEXT("10101000101"), 11 },
	{ TEXT("101010101111011111110010000011110101010"), 40 },
	{ TEXT("101011101001010101010"), 21 },
	{ TEXT("101010101010101010101010101111"), 30 }
};

  之後生成各個節點,最後收尾相連,形成循環隊列。

void CreateQueue(CharQueue * cc, int cyScreen, int x)
{
	CharList * front;

	int NumTemp = rand() % 6;         //七組字符串

	cc->x = x;
	cc->y = rand() % 10 ? rand() % cyScreen : 0; //大約9/10的概率從中間開始下落。
	cc->iShownLen = 1;                           //一開始就顯示一個字符,然後慢慢增加,增加到等於歌詞字符數時保持不變
	cc->iStrNum = charArr[NumTemp].iNum;         //歌詞字符數
	cc->head = cc->cur = front = (CharList *)calloc(cc->iStrNum, sizeof(CharList)); //創建顯示列

	//生成每個節點
	int i;
	for (i = 0; i<cc->iStrNum - 1; i++)
	{
		cc->cur->prev = front;
		cc->cur->ch = charArr[NumTemp].myChar[i];
		front = cc->cur++;
		front->next = cc->cur;
	}

	//最後一個是標點符號
	cc->cur->prev = front;
	cc->cur->ch = charArr[NumTemp].myChar[i];
	cc->cur->next = cc->head;
	cc->head->prev = cc->cur;

	cc->cur = cc->head;             //首尾相連
} 

  注意這裏,鏈表存儲一個字符,而隊列來存儲整個字符串

消息的處理

  在窗口上面顯示圖案文字,這就涉及到GDI繪圖
首先獲取窗口大小,也就是屏幕大小,因爲是整體顯示,接着得到設備上下文(窗口)句柄。

	cxScreen = GetSystemMetrics(SM_CXSCREEN);  //獲得屏幕的寬度
	cyScreen = GetSystemMetrics(SM_CYSCREEN);  //獲得屏幕的高度
	hdc = GetDC(hwnd);   

  接下來就是重點了,不可以直接在窗口上面繪製,而是創建一個和窗口DC一樣兼容的內存DC

  然後創建一塊畫布,只有畫布上面纔可以繪製圖像文字等,再然後創建畫筆,把畫布和畫筆都放入到內存DC中,這樣就可以在內存DC上面繪製圖像文字了。

	hdcMem = CreateCompatibleDC(hdc);          //創建一個兼容的內存DC
	hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);    //創建一塊畫布
	SelectObject(hdcMem, hBitmap);             //將畫布放到內存DC中
	hFont = CreateFont(iFontHeight, iFontWidth, 0/*角度設置*/, 0/*角度設置*/, FW_DONTCARE/*黑體*/, 0, 0, 0,/*斜體 下劃線 啊、刪除線*/
		DEFAULT_CHARSET/*字符集*/, OUT_DEFAULT_PRECIS/*指定輸出精度*/, CLIP_DEFAULT_PRECIS/*指定裁剪精度*/,
		DRAFT_QUALITY/*指向輸出質量*/, FIXED_PITCH | FF_SWISS/*指定字體間距| 字體族*/, TEXT("CloudKaiTiGBK"));
	SelectObject(hdcMem, hFont);               //將字體放到內存DC中
	SetBkMode(hdcMem, TRANSPARENT);            //設置窗口背景

  最後創建屏幕顯示的隊列(通過屏幕的分辨率以及顯示文字直接的間隔來計算),播放音樂(音樂的播放需要加上相應的媒體庫 #pragma comment(lib, “WINMM.LIB”)),開啓定時器(主要功能就是下面文字的顯示,控制顯示的快慢)

	PlaySound(L"123.wav", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);//異步循環播放
	AllChar = (CharQueue *)calloc(NumOfColumn, sizeof(CharQueue));//自動初始化爲0
	for (i = 0; i<NumOfColumn; i++)
	{
		CreateQueue(AllChar + i, cyScreen, ColSpan * i + 17);
	}
	SetTimer(hwnd, 1, 100, NULL);

  定義的數據類型如下:

#define NumOfColumn 25  //顯示列的列數
#define ColSpan     64  //列字符串之間的間隔

HDC hdc=0;
static HDC hdcMem;
static HBITMAP hBitmap;
PAINTSTRUCT           ps;
static CharQueue * AllChar;
HFONT hFont=0;
static int cxScreen, cyScreen;
static int iFontWidth = 20, iFontHeight = 30;
int i, j, y, greenToblack;
CharQueue * ccElem;
CharList * temp;

  整塊的程序如下:

case WM_CREATE:

	cxScreen = GetSystemMetrics(SM_CXSCREEN);  //獲得屏幕的寬度
	cyScreen = GetSystemMetrics(SM_CYSCREEN);  //獲得屏幕的高度
	hdc = GetDC(hwnd);                         //獲得設備上下文環境句柄
	hdcMem = CreateCompatibleDC(hdc);          //創建一個兼容的內存DC
	hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);    //創建一塊畫布
	SelectObject(hdcMem, hBitmap);             //將畫布放到內存DC中
	hFont = CreateFont(iFontHeight, iFontWidth, 0/*角度設置*/, 0/*角度設置*/, FW_DONTCARE/*黑體*/, 0, 0, 0,/*斜體 下劃線 啊、刪除線*/
		DEFAULT_CHARSET/*字符集*/, OUT_DEFAULT_PRECIS/*指定輸出精度*/, CLIP_DEFAULT_PRECIS/*指定裁剪精度*/,
		DRAFT_QUALITY/*指向輸出質量*/, FIXED_PITCH | FF_SWISS/*指定字體間距| 字體族*/, TEXT("CloudKaiTiGBK"));
	SelectObject(hdcMem, hFont);               //將字體放到內存DC中
	SetBkMode(hdcMem, TRANSPARENT);            //設置窗口背景
	PlaySound(L"123.wav", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);//異步循環播放
	AllChar = (CharQueue *)calloc(NumOfColumn, sizeof(CharQueue));//自動初始化爲0
	srand(time(0));
	for (i = 0; i<NumOfColumn; i++)
	{
		CreateQueue(AllChar + i, cyScreen, ColSpan * i + 17);
	}
	SetTimer(hwnd, 1, 100, NULL);
	return 0;

  做完了這些初始化工作,然後就開始正式的文字顯示工作(邏輯處理)。

  主要思路是:隊列中的字符由隨機的位置從上往下逐個顯示,最新的顯示爲最亮的顏色,顯示過的文字逐步變爲背景顏色(即逐步消失),然後當文字顯示的超過屏幕的寬時,這刪除該隊列,重新創建一個新的隊列,就這樣循環顯示。

  具體步驟分爲以下幾步:

  1. 創建一個屏幕顯示的隊列文字的循環
  2. 拿出隊列中的一個字符進行顯示,每次顯示的文字書增加
  3. 將顯示過的文字顏色進行逐步改變成背景顏色,逐步消失
  4. 改變下次顯示文字的縱座標,進行位置的變更
  5. 判斷顯示的文字是否超出屏幕,如果超出,就刪除該隊列,創建新的隊列顯示
  6. GDI繪圖,將內存DC繪製的文字圖案複製到環境DC(即窗口DC)中,完成顯示。
  7. 接着就 一直循環這個過程就完成顯示。
case WM_TIMER:

	PatBlt(hdcMem, 0, 0, cxScreen, cyScreen,BLACKNESS);
	for (i = 0; i<NumOfColumn; i++)
	{
		ccElem = AllChar + i;
		temp = ccElem->head;
		SetTextColor(hdcMem, RGB(255, 255, 255));
		TextOut(hdcMem, ccElem->x, ccElem->y, &temp->ch, _tcslen(&(temp->ch)) /*字符個數*/);
		y = ccElem->y;
		greenToblack = 0;
		ccElem->head = ccElem->head->next;
		temp = temp->prev;
		for (j = 1; j<ccElem->iShownLen; j++)
		{
			SetTextColor(hdcMem, RGB(0, 255 - 255 * (greenToblack++) / (ccElem->iStrNum), 0));
			TextOut(hdcMem, ccElem->x, y -= iFontHeight, &temp->ch, _tcslen(&(temp->ch)));
			temp = temp->prev;
		}

		if (ccElem->iShownLen<ccElem->iStrNum)
		{
			ccElem->iShownLen++;
		}

		ccElem->y += iFontHeight;
		if (ccElem->y - ccElem->iStrNum*iFontHeight > cyScreen)
		{
			free(ccElem->cur);
			CreateQueue(ccElem, cyScreen, ColSpan * i + 17);
		}
		
	}
	hdc = GetDC(hwnd);
	BitBlt(hdc, 0, 0, cxScreen, cyScreen, hdcMem, 0, 0, SRCCOPY);
	ReleaseDC(hwnd, hdc);
	return 0;

  最後如果要退出程序,可以設置幾個按鍵程序響應函數,退出相應的進程。

  在退出的時候,也有幾個注意點,就是首先關閉之後,調用的WM_CLOSE消息,然後再調用WM_DESTROY消息,最後就退出消息循環,程序關閉。

case WM_KEYDOWN:
	{
		   switch (wParam)
		   {
		   case VK_LEFT:
		   {
				   KillTimer(hwnd, 1);
				   for (i = 0; i<NumOfColumn; i++)
				   {
					   ccElem = AllChar + i;
					   free(ccElem->cur);
				   }
				   free(AllChar);
				   DeleteObject(hBitmap);
				   DeleteDC(hdcMem);
				   DeleteObject(hFont);
				   ReleaseDC(hwnd, hdc);
				   PostQuitMessage(0);
		   }
			   break;
		   case VK_ESCAPE:
		   {
					KillTimer(hwnd, 1);
					for (i = 0; i<NumOfColumn; i++)
					{
						ccElem = AllChar + i;
						free(ccElem->cur);
					}
					free(AllChar);
					DeleteObject(hBitmap);
					DeleteDC(hdcMem);
					DeleteObject(hFont);
					ReleaseDC(hwnd, hdc);
					PostQuitMessage(0);
		   }
			   break;
		   }
	}
		break;
	case WM_CLOSE:
			//確定退出,銷燬窗口,拋出一個WM_DESTYRY的消息
			DestroyWindow(hwnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}

  這樣就完成整個流星雨屏幕程序的實現。如果需要源代碼,可以在CSDN博客上面下載,如果沒有積分,也可以私聊郵箱,在個人簡介裏面。
  網址:https://download.csdn.net/download/qq_34430371/12283490

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章