字符雨实现,我们需要处理的消息:
struct Rain{
int x;
int y;
char str[];
}
定义雨滴结构体:
在这之前,我们定义一些常量:
#define STRLEN 22 //定义数组长度
#define COUNT 123 //定义
typedef struct Rain{
int x;
int y;
char szStr[STRLEN];
}
然后我们在我们自己的视图类中添加雨滴结构体成员:
Rain RainArr[COUNT]
int CMyView::OnCreate(LPCREATESTRUCT cs)
{
x = cs->cx;
y = cs->cy;
for (size_t i = 0; i < COUNT; i++)
{
RainArr[i].x = i * 15;
RainArr[i].y = rand() % cs->cy;
for (size_t j = 0; j < STRLEN; j++)
{
RainArr[i].szStr[j] = CreateChar();
}
}
SetTimer(0, 100, NULL);
return 0;
}
char CMyView::CreateChar()
{
int nFlag;
nFlag = rand() % 3;
if (nFlag == 0) {
return rand() % 10 + '0';
}
else if (nFlag == 1) {
return rand() % 26 + 'a';
}
else {
return rand() % 26 + 'A';
}
return 0;
}
我们这样创建了雨滴结构之后,我们还需要绘制,这时候,我们在视图类中创建一个定时器,当窗口有无效区域的时候,就去绘制
1.在OnCreate中,创建定时器:
SetTimer(0,1,NULL);
然后我们创建定时器消息,使用MFC的宏:ON_WM_TIMER()
处理函数:
afx_msg void OnTimer(UINT_PTR id){
//制造无效区域:
invalidateRect(NULL,FALSE);
//
}
双缓冲机制:
因为我们直接使用设备句柄,往设备上画图的时候,如果数据量非常大,就会闪屏,这里使用双缓冲,即可解决
双缓冲机制后面会将,这里简单理解一下:
我们先将绘图绘制在一张图片上,然后将图片完整复制给屏幕
void CMyView::OnDraw(CDC* pDc)
{
UpDate();
UpDateY();
//通过当前视图窗口拿到设备句柄
hDC = ::GetDC(this->m_hWnd);
// 通过当前设备句柄拿到一个内存句柄
hMemDC = ::CreateCompatibleDC(hDC);
// 创建图片句柄
hBitMap = ::CreateCompatibleBitmap(hDC, x, y);
// 把图片送到内存DC中
::SelectObject(hMemDC, hBitMap);
// 设置视图窗口背景色
::SetBkColor(hMemDC, RGB(0, 0, 0));
for (size_t i = 0; i < COUNT; i++)
{
for (size_t j = 0; j < STRLEN; j++)
{
// 设置字体渐变颜色
SetTextColor(hMemDC, RGB(0, 255 - j * 10, 0));
// 竖着打印字符
TextOut(hMemDC, RainArr[i].x, RainArr[i].y - j * 10, &(RainArr[i].szStr[j]), 1);
}
}
// 把图片送给设备
::BitBlt(hDC, 0, 0, x, y, hMemDC, 0, 0, SRCCOPY);
// 释放句柄
::DeleteDC(this->hDC);
}
void UpData(){
for(int i = 0 ; i
void UpdataY(){
for(int i = 0 ;i < COUNT ;i++){
RainArr[i].y+=5;
if(RainArr[i].y - STRLEN *10 > y){
RainArr[i].y = 0;
}
}
}
我们这样实现之后,已经实现了基本的代码雨,但是当我们放大窗口的时候,代码雨视图还是以前的大小,我们就需要处理WM_SIZE消息:
在消息映射中添加宏:
void CMyView::OnSize(UINT, int x, int y)
{
this->x = x;
this->y = y;
}
这里本人为了深刻理解MFC底层,创建的是控制台应用,自己写成了MFC类,如果大家有什么看不懂的,可以私信问我:
#include
#include
#define STRLEN 22
#define COUNT 128
//实现我们自己的框架类
class CMyFrameWnd :public CFrameWnd {
afx_msg int OnCreate(LPCREATESTRUCT cs);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_CREATE()
END_MESSAGE_MAP()
//实现我们自己的应用程序类
class CMyApp :public CWinApp {
public:
CMyApp() {};
//重写虚函数
virtual BOOL InitInstance() {
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "FirstMFC");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
//实现我们自己的视图类:
class CMyView :public CView {
public:
virtual void OnDraw(CDC* pDc);
private:
int x;
int y;
struct Rain
{
int x;
int y;
char szStr[STRLEN];
};
Rain RainArr[COUNT] = { 0 };
HDC hDC;
HDC hMemDC;
HBITMAP hBitMap;
char CreateChar();
void UpDateY();
void UpDate();
afx_msg int OnCreate(LPCREATESTRUCT cs);
afx_msg void OnTimer(UINT_PTR);
afx_msg void OnSize(UINT, int x, int y);
public:
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyView,CView)
ON_WM_CREATE()
ON_WM_TIMER()
ON_WM_SIZE()
END_MESSAGE_MAP()
CMyView* m_View = new CMyView;
CMyApp theApp;
int CMyFrameWnd::OnCreate(LPCREATESTRUCT cs)
{
RECT rect;
GetWindowRect(&rect);
m_View->Create(NULL, "MFCView", WS_CHILD | WS_VISIBLE | WS_BORDER, rect, this, AFX_IDW_PANE_FIRST);
return 0;
}
void CMyView::OnDraw(CDC* pDc)
{
UpDate();
UpDateY();
//通过当前视图窗口拿到设备句柄
hDC = ::GetDC(this->m_hWnd);
// 通过当前设备句柄拿到一个内存句柄
hMemDC = ::CreateCompatibleDC(hDC);
// 创建图片句柄
hBitMap = ::CreateCompatibleBitmap(hDC, x, y);
// 把图片送到内存DC中
::SelectObject(hMemDC, hBitMap);
// 设置视图窗口背景色
::SetBkColor(hMemDC, RGB(0, 0, 0));
for (size_t i = 0; i < COUNT; i++)
{
for (size_t j = 0; j < STRLEN; j++)
{
// 设置字体渐变颜色
SetTextColor(hMemDC, RGB(0, 255 - j * 10, 0));
// 竖着打印字符
TextOut(hMemDC, RainArr[i].x, RainArr[i].y - j * 10, &(RainArr[i].szStr[j]), 1);
}
}
// 把图片送给设备
::BitBlt(hDC, 0, 0, x, y, hMemDC, 0, 0, SRCCOPY);
// 释放句柄
::DeleteDC(this->hDC);
}
char CMyView::CreateChar()
{
int nFlag;
nFlag = rand() % 3;
if (nFlag == 0) {
return rand() % 10 + '0';
}
else if (nFlag == 1) {
return rand() % 26 + 'a';
}
else {
return rand() % 26 + 'A';
}
return 0;
}
void CMyView::UpDateY()
{
for (size_t i = 0; i < COUNT; i++)
{
RainArr[i].y += 5;
if (RainArr[i].y - STRLEN * 10 > y) {
RainArr[i].y = 0;
}
}
}
void CMyView::UpDate()
{
for (int i = 0; i < COUNT; i++) {
RainArr[i].szStr[rand() % STRLEN] = CreateChar();
}
}
int CMyView::OnCreate(LPCREATESTRUCT cs)
{
x = cs->cx;
y = cs->cy;
for (size_t i = 0; i < COUNT; i++)
{
RainArr[i].x = i * 15;
RainArr[i].y = rand() % cs->cy;
for (size_t j = 0; j < STRLEN; j++)
{
RainArr[i].szStr[j] = CreateChar();
}
}
SetTimer(0, 100, NULL);
return 0;
}
void CMyView::OnTimer(UINT_PTR id)
{
InvalidateRect(NULL, FALSE);
}
void CMyView::OnSize(UINT, int x, int y)
{
this->x = x;
this->y = y;
}
这里需要注意,这里我们在视图类中实现了字符雨,但这是不科学的,因为视图类只负责显示窗口,数据不应该保存在视图类中,我们之后学习文档类,会解决该问题。