1.创建一个基于对话框的程序
2.添加如下所示的编辑框,文本框
并设置这几个按钮位置属性如下
3.准备一张背景图,放入res文件夹内,这样,准备工作就做好了,现在来添加代码了
4.添加Gdi+支持(见前文)
5.在主对话框类中添加两个结构
struct Snake { int x;//位置 int y; int direction;//方向 }; struct Food { int x; int y;//位置 };添加共有成员
int m_width;//游戏区宽度 int m_height ;//游戏区高度 Image* img;//背景图 list<Snake>snake;//蛇 Food food;//食物
别忘记添加对list的支持
#include<list> using std::list;
初始化背景图
在主对话框构造函数中
img = Image::FromFile(_T("res/bkg.jpg"));
6.添加宏,4个方向宏,2个蛇大小宏
#define SNAKE_LEFT 3 #define SNAKE_UP 1 #define SNAKE_RIGHT 2 #define SNAKE_DOWN 0 #define SNAKE_WIDTH 20 #define SNAKE_HEIGHT 20
7.添加定时器消息,初始化在InitInstance()函数中
SetTimer(0, 1000, NULL); srand(300); //蛇位置初始化 Snake snakes; snakes.x = rand() % 300; snakes.y = rand() % 300; snakes.direction = rand() % 3; for (int i = 0; i < 5; i++) { snakes.x += SNAKE_WIDTH; snake.push_back(snakes); } //食物初始化 srand(2000); food.x = rand() % m_width; food.y = rand() % m_height;
8.添加绘制蛇主体和食物的函数DrawSnake()
void CSnakeDlg::DrawSnake(Graphics & gh) { SolidBrush brush(Color(120,0,0,0)); IsHit(); gh.FillRectangle(&brush, RectF(food.x, food.y, SNAKE_WIDTH, SNAKE_HEIGHT));//绘制食物 for (auto &i : snake) { gh.FillRectangle(&brush, RectF(i.x, i.y, SNAKE_WIDTH, SNAKE_HEIGHT));//绘制蛇 } }
再添加碰撞检测函数IsHit()
void CSnakeDlg::IsHit() { SnakeRun(); Snake head = snake.back(); if (head.x<0 || head.x>m_width || head.y<0 || head.y>m_height)//撞墙判断 OnCancel(); }每一帧蛇移动一次,只动蛇头和蛇尾
void CSnakeDlg::SnakeRun() { //只动蛇头和蛇尾,推断出下一步蛇头的位置,压入列表中 ,然后删除蛇尾 //蛇头是尾元素 Snake snakes = snake.back(); Snake head; switch (snakes.direction) { case SNAKE_LEFT: head.direction = snakes.direction; head.x = snakes.x - SNAKE_WIDTH; head.y = snakes.y; break; case SNAKE_DOWN: head.direction = snakes.direction; head.x = snakes.x; head.y = snakes.y+ SNAKE_HEIGHT; break; case SNAKE_RIGHT: head.direction = snakes.direction; head.x = snakes.x + SNAKE_WIDTH; head.y = snakes.y; break; case SNAKE_UP: head.direction = snakes.direction; head.x = snakes.x ; head.y = snakes.y- SNAKE_HEIGHT; break; } snake.push_back(head); if(!IsEat())//没有吃到食物就弹出头部 snake.pop_front(); }是否吃到食物判断
bool CSnakeDlg::IsEat() { Snake snakes = snake.back(); PointF ptCenter1, ptCenter2; ptCenter1.X = snakes.x + SNAKE_WIDTH/2; ptCenter1.Y = snakes.y + SNAKE_HEIGHT/2; ptCenter2.X = food.x + SNAKE_WIDTH/2; ptCenter2.Y = food.y + SNAKE_HEIGHT/2; PointF pt = ptCenter1 - ptCenter2; if(pt.X<SNAKE_WIDTH&&pt.X>-SNAKE_WIDTH&&pt.Y<SNAKE_HEIGHT&&pt.Y>-SNAKE_HEIGHT) { food.x = rand() % m_width; food.y = rand() % m_height; return true; } return false; }
9.添加WM_SIZE消息
获取对话框的大小并保存
void CSnakeDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); m_width = cx-100; m_height = cy; // TODO: 在此处添加消息处理程序代码 }
10,添加对按键的支持
添加虚函数PreTranslateMessage(),该函数在Windows处理之前拦截按键消息
BOOL CSnakeDlg::PreTranslateMessage(MSG* pMsg) { Snake &head = snake.back(); // TODO: 在此添加专用代码和/或调用基类 switch (pMsg->wParam) { case VK_LEFT: if (head.direction != SNAKE_RIGHT)//方向不能相反 head.direction = SNAKE_LEFT; break; case VK_RIGHT: if (head.direction != SNAKE_LEFT) head.direction = SNAKE_RIGHT; break; case VK_UP: if (head.direction != SNAKE_DOWN) head.direction = SNAKE_UP; break; case VK_DOWN: if (head.direction != SNAKE_UP) head.direction = SNAKE_DOWN; break; } return CDialogEx::PreTranslateMessage(pMsg); }
11.利用双缓冲将该游戏绘制出来
void CSnakeDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDC* pDC = this->GetDC(); CBitmap bmp; CDC m_dcMemory; m_dcMemory.CreateCompatibleDC(pDC); bmp.CreateCompatibleBitmap(pDC, m_width, m_height); m_dcMemory.SelectObject(&bmp); Graphics gh(m_dcMemory.GetSafeHdc()); { gh.DrawImage(img, 0, 0, m_width, m_height);//背景 DrawSnake(gh); } pDC->BitBlt(0, 0, m_width, m_height, &m_dcMemory, 0, 0, SRCCOPY); CDialogEx::OnPaint(); } }
12.利用计时器将做出流畅游戏
void CSnakeDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 OnPaint(); CDialogEx::OnTimer(nIDEvent); }
游戏完成
该游戏还有许多不完善的地方
得分功能没有设计;小蛇自我碰撞检测没有设计,游戏开始暂停也没有设计
效果