版权声明:访问者可将本网站提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。除此以外,将本网站任何内容或服务用于其他用途时,须征得本网站及相关权利人的书面许可,并支付报酬。转载需表明文章地址。
本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站,予以删除。
作者QQ:1765813715
由于这个游戏倾入了我大量的时间和创意 所以写了个版权声明
这个游戏在基础的俄罗斯方块上面还添加了 两种方块
一种是幽灵方块 (可以在方块间任意移动 直到它所在的这一列下面没有空)
另一种是 炸弹方块 (他可以在激活和非激活中转换,激活时到达底部后会清除周围3*3的方块 非激活就和普通方块没有什么区别
第一次用WindowsApi写游戏 注释很多。 也查了很多资料
Windows窗口的创建以后我在写一个详细的 这里我就直接从CALLBACK WndProc 开始讲解我的程序结构了
下面讲解每一个函数
void DrawPanel(HDC hdc); 绘制游戏面板
void RefreshPanel(HDC hdc); 刷新游戏面板
void creat(); 创造方块 一共有9中方块
bool isbottom(HWND hwnd); 判断是否到达底部 幽灵方块判断方式不同 以及到达底部后的处理(即融入地图 或者炸弹方块的 爆炸) 以及检测游戏结束
bool drop(HDC hdc,HWND hwnd); 下降 其中调用 isbottom() 如果到达底部 调用clear(),creat(),refreshpanel()
void move_left(); 向左移动 检测碰撞(幽灵方块也是特殊处理)
void move_right(); 同上
void transform(); 变形 变形时需要检测是否满足 炸弹方块的变形是 在激活和非激活状态之间转换
void clear(HDC hdc); 消行 内部还实现加分
void preview(HDC hdc); 图形预览框
这里添加下 过程记录吧 也方便其他像学习的人有一条明确的路线
4月28日 下午6点—晚上12点 学习窗口的创建 如何绘画出图形界面
4月29日 上午8点—11.30 写出 已测试( 方块生成 碰撞检测 向下移动 时钟系统 暂停功能 检测游戏结束)
未测试( 消行 )
中午1点—1点40 完成左右移动 测试了消行 但是没有成功 还未发现原因(先睡觉)。。。。
下午3点—4点 修改消行BUG 完成转换功能 到这里基本功能差不多就全部完成了
现在基本的俄罗斯方块已经完成了 不过我的俄罗斯方块比较高级 还有两种方块就是幽灵方块 和 炸弹方块
下午6点—7点 游戏测试 查找BUG(别说还真有)
晚上11点—11.半 完成幽灵方块
4月30日 上午8点—11点 完成炸弹方块 图形预览
晚上11点 完成BGM (找了几个小时)。。
注意:我是在vs2017上面开发的 测试在dev上面无法编译 而且由于我这个添加BGM 所以有可能在你的电脑上也无法编译
如果还缺少头文件请自行添加
如果是这样,就删除播放BGM 的代码 或者自己从新加载资源文件
在这里我把我的文件上传到百度网盘里 大家有需要的可以自己下载 其中bebug和release中的EXE文件 可以直接用来玩
http://pan.baidu.com/s/1cpf9AA
#include "stdafx.h" #include <windows.h> // C 运行时头文件 #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <tchar.h> #include<time.h> #include<iostream> #include "俄罗斯方块.h" #include<mmsystem.h> #pragma comment(lib,"winmm.lib")//知识就是财富啊 一个背景音乐搞了半天 #define CELL 20//方块大小 #define ROWS 25 //行数 #define COLS 15//列数 using namespace std; TCHAR str[256]; int id; //记录当前方块种类 int next_id;//记录下一个方块的ID 达到预览 int len; int score = 0; //得分 int level = 0; //游戏等级 int speed = 500; //游戏速率 初始化为1秒 后面的速率为 speed-min(level,7)*100; UINT timer = 0; bool ispause = 0; //记录是否暂停 bool panel[25][15] = { 0 }; //记录游戏底层画面 bool active; //记录炸弹方块是否被激活 int direction[8][2] = { 1,0,0,1,-1,0,0,-1,1,1,-1,-1,1,-1,-1,1 };//炸弹方块爆破的8个方向 COORD pre_peak; bool pre_panel[2][4]; //预览面板 bool* block=NULL; //通过记录方块组合 最左上角(我们后面称为顶点)的坐标 和组合的宽度高度 来确定这个组合的位置。 这样做有很大的好处 int block_top = 0; int block_left = 0; int block_height = 0; int block_width = 0; void DrawPanel(HDC hdc); void RefreshPanel(HDC hdc); void creat(); bool isbottom(HWND hwnd); bool drop(HDC hdc,HWND hwnd); void move_left(); void move_right(); void transform(); void clear(HDC hdc); void preview(HDC hdc); LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lparam);//回调函数 //建立窗口需要经过5个步骤 设计窗口类型 注册窗口类型 创建 显示 消息循环 int APIENTRY WinMain(HINSTANCE hInstance, //程序当前运行的实例句柄 HINSTANCE hPrevInstance, //当前实例的前一个实例 w32下不再起作用 LPSTR lpCmdLine, //表示传给程序的命令行参数 int nCmdShow) //指定程序窗口如何显示,如 最大化最小化等 { WNDCLASS wndclass; //下面都是注册的内容。。。。。(好多。。) HWND hwnd; MSG msg; TCHAR lpszClassName[] = TEXT("俄罗斯方块"); wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口样式 wndclass.lpfnWndProc = WndProc; //指向窗口函数的指针 wndclass.cbClsExtra = 0; //窗口类附加字节, 为该类窗口共享,一般置0 wndclass.cbWndExtra = 0; //窗口字节 一般值0 wndclass.hInstance = hInstance; //当前应用程序的实例句柄 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //指定窗口图标 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //指定窗口光标 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//设置背景色 wndclass.lpszMenuName = NULL; //窗口菜单资源名 wndclass.lpszClassName = lpszClassName; //窗口类名 if (!RegisterClass(&wndclass)) //注册 return FALSE; hwnd = CreateWindow(lpszClassName, TEXT("俄罗斯方块"), //创建 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); //显示 UpdateWindow(hwnd); //上传 while (GetMessage(&msg, NULL, 0, 0)) //消息循环 { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)//回掉函数 { switch (message) { case WM_CREATE: PlaySound( //BGM MAKEINTRESOURCE(130), //不知道为什么 这里不能直接写 IDR_WAVE1 而是要写他的数字id GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC | SND_LOOP); MoveWindow(hwnd, 200, 200, COLS*CELL+300, ROWS*CELL+40, FALSE);//16.40 pre_peak.X = 16; pre_peak.Y = 2; //确定预览框 顶点坐标 srand(int(time(0))); next_id = rand() % 9; creat(); timer = SetTimer(hwnd, 1, speed - min(7, level) * 50, NULL); //创建一个时钟 return 0; case WM_PAINT: HDC hdc; PAINTSTRUCT ps; hdc = BeginPaint(hwnd, &ps); DrawPanel(hdc); RefreshPanel(hdc); preview(hdc); TextOutW(hdc, 350+100, 370-330, TEXT("得分:"), 3); TextOutW(hdc, 350+100, 390-330, TEXT("等级:"), 3); TextOut(hdc, 350, 480, TEXT("Made by LYON"), strlen("Made by LYON")); len = wsprintf(str, TEXT("%d"), score); //。。。为了找这个 找了 一个小时。。。 怎么把整数转换为字符串 TextOutW(hdc, 390+100, 370-330, str, len); //其实也可以自己写一个转换函数的 ,,,不过。。。怎么能光用会的知识呢。。 len = wsprintf(str, TEXT("%d"), level); TextOutW(hdc, 390+100, 390-330, str, len); EndPaint(hwnd, &ps); return 0; case WM_TIMER: //时钟发来的消息 这个时候就应该下落一个了 if (!ispause) { hdc = GetDC(hwnd); drop(hdc, hwnd); ReleaseDC(hwnd, hdc); } return 0; case WM_KEYDOWN: //有按键按下了 hdc = GetDC(hwnd); //获取设备上下文 switch (wParam) { case VK_SPACE: if (ispause) { ispause = !ispause; timer = SetTimer(hwnd, 1, speed - min(7, level) * 50, NULL); } else { ispause = !ispause; KillTimer(hwnd, timer); //销毁时钟 } break; case VK_UP: if (ispause) break; transform(); RefreshPanel(hdc); break; case VK_LEFT: if (ispause) break; if (block_left == 0) break; //这一句也可以去掉 但是如果写在move 里面判断的话 还是会重绘界面 长按LEFT就会导致 方块 一闪一闪的 move_left(); RefreshPanel(hdc); break; case VK_RIGHT: if (ispause) break; if (block_left + block_width == 15) break; //同上 move_right(); RefreshPanel(hdc); break; case VK_DOWN: if (!ispause) { hdc = GetDC(hwnd); drop(hdc, hwnd); ReleaseDC(hwnd, hdc); } break; default: break; } ReleaseDC(hwnd, hdc); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } bool isbottom(HWND hwnd) //检测是否到达底部 { if (block == NULL) return 0; bool flag = 0; if (id == 7) //幽灵方块需要特殊判断 { flag = 1; for (int i = 1; block_top + i < 25; i++) //判断这一列 下面还有没有空 { if (!panel[block_top + i][block_left]) { flag = 0; break; } } } else { for (int i = block_height - 1; i >= 0 && !flag; i--) { for (int j = 0; j < block_width && !flag; j++) { if (*(block + i*block_width + j)) //判断每一个方块下面又没有方块了 { if (block_top + i + 1 < 0)continue; //还没有进入地图 if (panel[block_top + i + 1][block_left + j] || block_top + i + 1 == 25) { flag = 1; break; } } } } } if (flag) //如果到达底部了 { if (id == 8 && active) //炸弹方块到达底部后的处理方式不同 { for (int i = 0; i < 8; i++) { if (block_top + direction[i][0] < 0 || block_top + direction[i][0] == 25 || block_left + direction[i][1] < 0 || block_left + direction[i][1] >= 15) continue; panel[block_top + direction[i][0]][block_left + direction[i][1]] = FALSE; } return flag; } if (block_top < 0)//游戏结束 ,方块无法进入地图 说明下面已经满了 Gameover { if (timer) { KillTimer(hwnd,timer); } if (block) delete[] block; MessageBox(hwnd, TEXT("游戏结束"), TEXT("MSG"), MB_OK | MB_ICONEXCLAMATION); SendMessage(hwnd, WM_CLOSE, 0, 0); } else //将方块融入地图中 { for (int i = 0; i < block_height; i++) { for (int j = 0; j < block_width; j++) { if (*(block + i*block_width + j)) { panel[block_top + i][block_left + j] = TRUE; } } } } } return flag; } void clear(HDC hdc) //有问题 { bool flag; int number=0; for (int i = 0; i < block_height; i++) { flag = 1; for (int j = 0; j < 15; j++) { if (!panel[block_top + i][j]) { flag = 0; break; } } if (flag) { number++; MessageBeep(1);//消行时会有声音 for (int k = block_top+i; k > 0; k--) { for (int j = 0; j < 15; j++) { panel[k][j] = panel[k - 1][j]; } } for (int j = 0; j < 15; j++) panel[0][j] = FALSE; } } switch (number) { case 1:score += 5; break; case 2:score += 13; break; case 3:score += 20; break; case 4:score += 28; break; default: break; } level = score / 50; len = wsprintf(str, TEXT("%d"), score); TextOutW(hdc, 390+100, 370-330, str, len); len = wsprintf(str, TEXT("%d"), level); TextOutW(hdc, 390+100, 390-330, str, len); } bool drop(HDC hdc,HWND hwnd) //下落 { if (!isbottom(hwnd)) //还没有到达底部 block_top++; else //已经到达了底部 { clear(hdc);//消行 creat();//创建一个新组合 preview(hdc); RefreshPanel(hdc); } RefreshPanel(hdc); return 0; } void DrawPanel(HDC hdc) //绘制游戏面板 { int x, y; RECT rect;//矩形类 for (y = 0; y<ROWS; y++) { for (x = 0; x<COLS; x++) { //计算方块的边框范围 rect.top = y*CELL + 1; rect.bottom = (y + 1) *CELL - 1; rect.left = x*CELL + 1; rect.right = (x + 1) *CELL - 1; FrameRect(hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));//绘画矩形边框 } } rect.top = pre_peak.Y*CELL-1; rect.bottom = (pre_peak.Y + 2)*CELL+1; rect.left = pre_peak.X*CELL-1; rect.right = (pre_peak.X + 4)*CELL+1; FrameRect(hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH)); TextOut(hdc, pre_peak.X*CELL, pre_peak.Y*CELL - 30, TEXT("图形预览"), 4); } void RefreshPanel(HDC hdc) //刷新面板 { int x, y; RECT rect; HBRUSH h_bSolid = (HBRUSH)GetStockObject(GRAY_BRUSH), //设置笔刷颜色 h_bEmpty = (HBRUSH)GetStockObject(WHITE_BRUSH), h_bomb_active = CreateSolidBrush(RGB(255, 0, 0)), h_bomb_nactive= CreateSolidBrush(RGB(100, 0, 0)), h_ghost = (HBRUSH)GetStockObject(BLACK_BRUSH); //先刷屏 for (y = 0; y<ROWS; y++) { for (x = 0; x<COLS; x++) { //为避免刷掉方块的边框,rect范围必须比边框范围小1 rect.top = y*CELL + 2; rect.bottom = (y + 1) *CELL - 2; rect.left = x*CELL + 2; rect.right = (x + 1) *CELL - 2; if (panel[y][x]) FillRect(hdc, &rect, h_bSolid); //绘画矩形内部 else FillRect(hdc, &rect, h_bEmpty); } } if (block == NULL) return; if (id == 7) //幽灵方块为黑色 { rect.top = block_top*CELL + 2; rect.bottom = (block_top + 1)*CELL - 2; rect.left = block_left*CELL + 2; rect.right = (block_left + 1)*CELL - 2; FillRect(hdc, &rect, h_ghost); } else if (id == 8) { rect.top = block_top*CELL + 2; rect.bottom = (block_top + 1)*CELL - 2; rect.left = block_left*CELL + 2; rect.right = (block_left + 1)*CELL - 2; if(active) FillRect(hdc, &rect, h_bomb_active); else FillRect(hdc, &rect, h_bomb_nactive); } else { for (int i = 0; i < block_height; i++) //画出方块组合 { for (int j = 0; j < block_width; j++) { if (*(block + i*block_width + j)) { rect.top = (block_top + i)*CELL + 2; rect.bottom = (block_top + i + 1)*CELL - 2; rect.left = (block_left + j)*CELL; rect.right = (block_left + j + 1)*CELL - 2; FillRect(hdc, &rect, h_bSolid); } } } } } void creat() { srand(int(time(0))); id = next_id; next_id = rand() % 9; if (block != NULL) { delete[] block; } switch (id) { case 0: //直线型 block_height = 1; block_width = 4; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width / 2;//定位到中间上面 block = new bool[block_height*block_width]; //分配足够的空间储存组合 *(block) = TRUE; *(block + 1) = TRUE; *(block + 2) = TRUE; *(block + 3) = TRUE; break; case 1: //正方形 block_height = 2; block_width = 2; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width /2; block = new bool[block_height*block_width]; *(block) = TRUE; *(block + 1) = TRUE; *(block + 2) = TRUE; *(block + 3) = TRUE; break; case 2: //倒T型 block_height = 2; block_width = 3; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width /2; block = new bool[block_height*block_width]; *(block) = FALSE; *(block + 1) = TRUE; *(block + 2) = FALSE; *(block + 3) = TRUE; *(block + 4) = TRUE; *(block + 5) = TRUE; break; case 3: //左L型 block_height = 2; block_width = 3; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width / 2; block = new bool[block_height*block_width]; *(block) = TRUE; *(block + 1) = FALSE; *(block + 2) = FALSE; *(block + 3) = TRUE; *(block + 4) = TRUE; *(block + 5) = TRUE; break; case 4: //右L型 block_height = 2; block_width = 3; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width / 2; block = new bool[block_height*block_width]; *(block) = FALSE; *(block + 1) = FALSE; *(block + 2) = TRUE; *(block + 3) = TRUE; *(block + 4) = TRUE; *(block + 5) = TRUE; break; case 5: //正Z型 block_height = 2; block_width = 3; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width / 2; block = new bool[block_height*block_width]; *(block) = TRUE; *(block + 1) = TRUE; *(block + 2) = FALSE; *(block + 3) = FALSE; *(block + 4) = TRUE; *(block + 5) = TRUE; break; case 6: //倒Z型 block_height = 2; block_width = 3; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width / 2; block = new bool[block_height*block_width]; *(block) = FALSE; *(block + 1) = TRUE; *(block + 2) = TRUE; *(block + 3) = TRUE; *(block + 4) = TRUE; *(block + 5) = FALSE; break; case 7: //幽灵方块 block_height = 1; block_width = 1; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width / 2; block = new bool[block_height*block_width]; *(block) = TRUE; case 8: //炸弹方块 block_height = 1; block_width = 1; block_top = -block_height; block_left = (COLS - block_width) / 2 + block_width / 2; block = new bool[block_height*block_width]; *(block) = TRUE; active = TRUE; break; } } void move_left() //判断 方块的左边又没有方块 ,没有就可以过去 { if (id == 7) { if (block_left == 0) return; } else { for (int i = 0; i < block_height; i++) { for (int j = 0; j < block_width; j++) { if (*(block + block_width*i + j)) { if (panel[block_top + i][block_left + j - 1] || block_left == 0) return; } } } } block_left--; //没有就移动顶点坐标 } void move_right() //判断方块的右边有没有方块 { if (id == 7) { if (block_left ==14) return; } else { for (int i = 0; i < block_height; i++) { for (int j = block_width - 1; j >= 0; j--) { if (*(block + i*block_width + j)) { if (panel[block_top + i][block_left + j + 1] || block_left + block_width == 15) return; } } } } block_left++; } void transform()//变形 { if (id == 7 || id == 1) return;//幽灵方块和 正方形方块转换后没差 if (id == 8) //炸弹方块直接转换激活状态 { active = !active; return; } bool* zz = new bool[block_height*block_width]; //先建立一个缓存 表示转换后方块的位置 for (int i = 0; i < block_width; i++) { for (int j = 0; j < block_height; j++) { //zz[i][j]=block[block_height-j-1][i] 转换公式 *(zz + i*block_height + j) = *(block + (block_height-1-j)*block_width + i); //转换 } } //开始不重新定位 顶点 感觉转换特别死板 不是中心旋转 所以这里要改 一下顶点左边 //嗯 改了一下感觉好多了 int zz_top = block_top + (block_height - block_width) / 2; int zz_left = block_left + (block_width - block_height) / 2; if (zz_top + block_width >= 25 || zz_left + block_height > 15||zz_left<0) return; //判断方块是否越界 for (int i = 0; i < block_width; i++) //判断该位置是否已经存在方块 { for (int j = 0; j < block_height; j++) { if (panel[zz_top + j][zz_left + j]) return; } } for (int i = 0; i < block_width; i++) //前面都达标了 说明可以转换 , { for (int j = 0; j < block_height; j++) *(block + i*block_height + j) = *(zz + i*block_height + j); } delete[] zz; //删除缓存 int a; block_top = zz_top; block_left = zz_left; a = block_width; block_width = block_height; block_height = a; } void preview(HDC hdc) //图形预览 { memset(pre_panel, 0, sizeof(pre_panel)); RECT rect; HBRUSH h_brush = (HBRUSH)GetStockObject(GRAY_BRUSH); switch (next_id) { case 0: pre_panel[0][0] = TRUE; pre_panel[0][1] = TRUE; pre_panel[0][2] = TRUE; pre_panel[0][3] = TRUE; TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("直线 "), 7); break; case 1: pre_panel[0][1] = TRUE; pre_panel[0][2] = TRUE; pre_panel[1][1] = TRUE; pre_panel[1][2] = TRUE; TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("正方 "), 7); break; case 2: pre_panel[0][1] = TRUE; pre_panel[1][0] = TRUE; pre_panel[1][1] = TRUE; pre_panel[1][2] = TRUE; TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("倒T "), 7); break; case 3: pre_panel[0][0] = TRUE; pre_panel[1][0] = TRUE; pre_panel[1][1] = TRUE; pre_panel[1][2] = TRUE; TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("左L "), 7); break; case 4: pre_panel[0][2] = TRUE; pre_panel[1][0] = TRUE; pre_panel[1][1] = TRUE; pre_panel[1][2] = TRUE; TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("右L "), 7); break; case 5: pre_panel[0][0] = TRUE; pre_panel[0][1] = TRUE; pre_panel[1][1] = TRUE; pre_panel[1][2] = TRUE; TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("左闪电 "), 7); break; case 6: pre_panel[0][2] = TRUE; pre_panel[0][1] = TRUE; pre_panel[1][1] = TRUE; pre_panel[1][0] = TRUE; TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("右闪电 "), 7); break; case 7: pre_panel[0][1] = TRUE; TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("幽灵 "), 7); h_brush = (HBRUSH)GetStockObject(BLACK_BRUSH); break; case 8: h_brush = CreateSolidBrush(RGB(255, 0, 0)); TextOut(hdc, pre_peak.X*CELL+20, pre_peak.Y*CELL + 50, TEXT("炸弹 "), 7); pre_panel[0][1] = TRUE; break; } rect.top = pre_peak.Y*CELL; rect.bottom = (pre_peak.Y + 2)*CELL; rect.left = pre_peak.X*CELL; rect.right = (pre_peak.X + 4)*CELL; FillRect(hdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); for (int i = 0; i < 2; i++) { for (int j = 0; j < 4; j++) { if (pre_panel[i][j]) { rect.top = (pre_peak.Y + i)*CELL + 2; rect.bottom = (pre_peak.Y + i + 1)*CELL - 2; rect.left = (pre_peak.X + j)*CELL + 2; rect.right = (pre_peak.X + j + 1)*CELL - 2; FillRect(hdc, &rect, h_brush); } } } }
回顾总结:编写过程中遇到了很多困难,很多知识都不懂 只有自己到处找问人, 而且没有抽象为类 可扩展性 和 代码重用能力太差 (一个图形预览 我还额外的编写了一个构造的函数 完全可以用creat()里面的, 但是就是由于开始考虑不完全导致后面修改起来麻烦)