这几天跟别人学习制作了一个小游戏,现在分享一下.
贪吃蛇,一个经典的小游戏,
开发环境vs2017
在WinMain函数中,窗口创建显示完成后添加一句SendMessage(hWnd,WM_SIZE,0,0)来调整窗口的大小,
//主函数WinMain() int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
//类名 TCHAR cls_name[] = TEXT("MyWinClass");
//设计窗口类 WNDCLASSEX wce = { 0 };
wce.cbSize = sizeof(WNDCLASSEX); wce.style = CS_HREDRAW | CS_VREDRAW; wce.lpfnWndProc = WindowProc; wce.cbClsExtra = 0; wce.cbWndExtra = 0; wce.hInstance = hInstance; wce.hIcon = LoadIcon(hInstance, IDI_APPLICATION); wce.hIconSm = wce.hIcon; wce.hCursor = LoadCursor(NULL, IDC_ARROW); wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);/*::GetStockObject(BLACK_BRUSH)*/ /*黑色刷子*/ wce.lpszMenuName = NULL; wce.lpszClassName = cls_name;
//注册窗口类 //ATOM nres = RegisterClassEx(&wce); if (FALSE == RegisterClassEx(&wce)) return 1;
//创建窗口 HWND hWnd = CreateWindowEx( WS_EX_APPWINDOW, cls_name, TEXT("我的窗口"), WS_OVERLAPPEDWINDOW, 200, 120, 600, 400, NULL, NULL, hInstance, NULL);
//回调函数要写上默认的处理DefWindowProc,不然创建失败 if (!hWnd) return 1;
//显示,更新窗口 ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //调整窗口大小 SendMessage(hWnd, WM_SIZE, 0, 0); //添加加速键表 HACCEL hAccel = NULL;/*::LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1);*/
//消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(hWnd, hAccel, &msg))//有加速键表时用这,没有就不用 { TranslateMessage(&msg); DispatchMessage(&msg); }
}
return 0; } |
2.在WM_SIZE消息中
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) {
case WM_DESTROY: PostQuitMessage(0); break;
case WM_SIZE: { hdc = GetDC(hWnd); RECT crc, wrc; GetClientRect(hWnd, &crc); GetWindowRect(hWnd, &wrc); int bordrewidth=(wrc.right - wrc.left) - (crc.right - crc.left); int bordreheight = (wrc.bottom - wrc.top) - (crc.bottom - crc.top); SetWindowPos(hWnd, NULL, 0, 0, 600 + bordrewidth, 600 + bordreheight, SWP_NOMOVE); OnStart(hWnd); //启动游戏 ReleaseDC(hWnd, hdc); }break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } |
这样就固定了窗口大小,不能改变.
3.在窗口上划分格子(我只分了横竖20)
定义全局变量
HDC hdc; int row = 20; //20行 int col = 20; //20列 #define WIDTH 30 //格子的宽度 #define HEIGHT 30 //格子的高度 #define MAXSIZE 50 //蛇身最大长度 POINT m_food; //随机食物坐标 //定义贪吃蛇的结构体 struct Snake { POINT m_pos[MAXSIZE]; //身体每个格子的坐标 int len; //身体个数 int m_direction; //前进方向 }m_snake;
|
在WM_PAINT消息中添加MyPaint()函数画格子
case WM_PAINT:
{
PAINTSTRUCT ps;
hdc = ::BeginPaint(hWnd, &ps);
{
MyPaint(hdc);
}
EndPaint(hWnd, &ps);
}break;
下面就是MyPaint函数的实现
void MyPaint(HDC hdc) { HBRUSH hbr = CreateSolidBrush(RGB(255, 0, 0)); //定义红色画刷 HGDIOBJ hOldBrh = SelectObject(hdc, hbr); for (int y=0;y<row;y++) { for (int x=0;x<col;x++) { Rectangle(hdc, x*WIDTH, y*HEIGHT, (x + 1)*WIDTH, (y + 1)*HEIGHT); //画格了,行20,列20 } } SelectObject(hdc, hOldBrh);
HBRUSH bhrgreen = CreateSolidBrush(RGB(0, 255, 0)); //绿色画刷 hOldBrh = SelectObject(hdc, bhrgreen); for (int n=0;n<m_snake.len;n++) { Rectangle(hdc, m_snake.m_pos[n].x*WIDTH, m_snake.m_pos[n].y*HEIGHT, (m_snake.m_pos[n].x+1)*WIDTH,(m_snake.m_pos[n].y+1)*HEIGHT); //画蛇的身体 } SelectObject(hdc, hOldBrh);
HBRUSH hbrblue = CreateSolidBrush(RGB(0, 0, 255)); //蓝色画刷 hOldBrh = SelectObject(hdc, hbrblue); Rectangle(hdc, m_food.x*WIDTH, m_food.y*HEIGHT, (m_food.x + 1)*WIDTH, (m_food.y + 1)*HEIGHT); //画随机食物 SelectObject(hdc, hOldBrh); } |
//随机食物坐标
void generatefood()
{
srand((unsigned)time(NULL));
m_food.x = rand() % 20;
m_food.y = rand() % 20;
}
//初始化蛇
void init_snake()
{
m_snake.len = 3;
m_snake.m_pos[0].x = 0;
m_snake.m_pos[0].y = 0;
m_snake.m_direction = 1;
for (int n = 1; n < MAXSIZE; n++)
{
m_snake.m_pos[n].x = -1;
m_snake.m_pos[n].y = -1;
}
generatefood();
}
下面就是启动游戏了
void OnStart(HWND hWnd)
{
init_snake();
SetTimer(hWnd, 1, 300, NULL); //设置时间
}
在WM_TIMER消息中
case WM_TIMER: { //循环输出蛇身位置 for (int n = m_snake.len - 1; n >= 1; n--) { m_snake.m_pos[n] = m_snake.m_pos[n - 1]; } //判断前进方向 if (0 == m_snake.m_direction)//向上 m_snake.m_pos[0].y = m_snake.m_pos[0].y - 1; if (1 == m_snake.m_direction)//向右 m_snake.m_pos[0].x = m_snake.m_pos[0].x + 1; if (2 == m_snake.m_direction)//向下 m_snake.m_pos[0].y = m_snake.m_pos[0].y + 1; if (3 == m_snake.m_direction)//向左 m_snake.m_pos[0].x = m_snake.m_pos[0].x - 1; //碰到食物蛇身+1,重新随机食物坐标 if (m_snake.m_pos[0].x == m_food.x&&m_snake.m_pos[0].y == m_food.y) { m_snake.len++; generatefood(); } //判断蛇头碰墙,就Game over if (m_snake.m_pos[0].x > 19 || m_snake.m_pos[0].y > 19 || m_snake.m_pos[0].x < 0 || m_snake.m_pos[0].y < 0) { KillTimer(hWnd, 1); if (IDYES == MessageBox(hWnd, TEXT("Game over,你想重新开始吗?"), TEXT("提示"), MB_YESNO)) { OnStart(hWnd); } else { PostQuitMessage(0); } } hdc = GetDC(hWnd); MyPaint(hdc);//重绘窗口所有数据 ReleaseDC(hWnd, hdc);
}break; |
接下来还有方向控制了,这简单
在WM_KEYDOWN消息中给蛇的m_direction赋值就行了
case WM_KEYDOWN: { switch (wParam) { case VK_UP: m_snake.m_direction = 0; break; case VK_RIGHT: m_snake.m_direction = 1; break; case VK_DOWN: m_snake.m_direction = 2; break; case VK_LEFT: m_snake.m_direction = 3; break; } }break; |
效果:
当然还有很多少足处,比如蛇的头可以从身体中穿过.
这些以后再弄了,先学习学习嘛