游戏编程笔记-起步
3.贪吃蛇游戏实现
代码下载:http://download.csdn.net/detail/you_lan_hai/3738025
核心代码实现如下,算法描述见 “起步(一)”:
#include "app.h" #include <string.h> #include <stdlib.h> int g_map[100][100];//2维地图 int g_nWidth = 610; //窗口宽度 int g_nHeight = 514;//窗口高度 int g_rows = 0; //地图行数 int g_cols = 0; //地图列数 int g_nSize = 30; //地图方格尺寸 struct Point//二维点 { int r;//行 int c;//列 }; Point g_snake[1000]; //蛇 int g_nLength = 0; //蛇的长度 int g_nSpeed; bool g_bLive = true; //是否存活 int g_nSnakeDir = 0; //蛇的当前移动方向。0-左,1-右,2-上,3-下 const Point g_direction[4] = {{0,-1},{0,1},{-1,0},{1,0}};//四个方向运动增量。 Point g_prizePos; //奖子坐标 bool g_bPause = true; //是否暂停 HBRUSH g_brushs[4]; //背景画刷 void setSnakeDir(int dir)//设置蛇的运动方向。 { if ((dir==0 && g_nSnakeDir==1) || (dir==1 && g_nSnakeDir==0) || (dir==2 && g_nSnakeDir==3) || (dir==3 && g_nSnakeDir==2))//反方向移动则令他暂停 { g_bPause = true; return;//直接返回,什么也不做。 } else { g_bPause = false; g_nSnakeDir = dir; } } void resetLevel()//重置关卡 { /*初始化地图。将四边初始为障碍,即不可通过。 * 0-可通过,1-障碍,2-奖子,3-蛇 */ memset(g_map,0,sizeof(g_map)); for (int r=0; r<g_rows; ++r) { g_map[r][0] = 1; g_map[r][g_cols-1] = 1; } for (int c=0; c<g_cols; ++c) { g_map[0][c] = 1; g_map[g_rows-1][c] = 1; } g_nSpeed = 500; g_bPause = true;//暂停 g_nSnakeDir = 1;//向右。 g_bLive = true; g_nLength = 3;//蛇初始有3个结点 g_snake[0].r = 1;//蛇头 g_snake[0].c = 3; g_snake[1].r = 1; g_snake[1].c = 2; g_snake[2].r = 1; g_snake[2].c = 1; //把初始状态写入地图 for (int i=0; i<g_nLength; ++i) { g_map[g_snake[i].r][g_snake[i].c] = 3; } //初始化奖子坐标 g_prizePos.r = 1; g_prizePos.c = g_cols-2; } void init()//游戏初始化 { g_brushs[0] = CreateSolidBrush(RGB(255,255,255));//白色画刷 g_brushs[1] = CreateSolidBrush(RGB(0,0,255));//绘制障碍画刷 g_brushs[2] = CreateSolidBrush(RGB(255,0,0));//绘制奖子画刷 g_brushs[3] = CreateSolidBrush(RGB(0,255,0));//绘制蛇画刷 srand(GetTickCount()); //矫正窗口高度、宽度数据 RECT rc; GetClientRect(getHWnd(),&rc); g_nWidth = rc.right - rc.left; g_nHeight = rc.bottom - rc.top; //计算行列值 g_rows = g_nHeight / g_nSize; g_cols = g_nWidth / g_nSize; resetLevel(); } void update()//逻辑更新 { //时钟控制 static int oldTime = 0; int curTime = GetTickCount(); if (curTime - oldTime < g_nSpeed) { return ; } oldTime = curTime; ////////////////////////////////////////////////////////////////////////// static int lastOcurTime = 0;//奖子上次出现的时间 if(curTime - lastOcurTime > 10000)//每隔10s更新一次位置。 { //lastOcurTime不为-1表示,没有被吃掉,则将原来位置的奖子从地图擦掉。 if (lastOcurTime != -1) { g_map[g_prizePos.r][g_prizePos.c] = 0; } lastOcurTime = curTime; //随机产生奖子位置。 bool flag = true; while(flag) { g_prizePos.r = rand()%(g_rows-2)+1; g_prizePos.c = rand()%(g_cols-2)+1; if (g_map[g_prizePos.r][g_prizePos.c] == 0)//符合条件 { flag = false; g_map[g_prizePos.r][g_prizePos.c] = 2; } } } if (!g_bLive || g_bPause)//死亡或暂停 { return ; } //先判断是否可走,即是否撞死。 Point newHead; newHead.r = g_snake[0].r + g_direction[g_nSnakeDir].r; newHead.c = g_snake[0].c + g_direction[g_nSnakeDir].c; if (g_map[newHead.r][newHead.c]==1 || g_map[newHead.r][newHead.c]==3) { g_bLive = false; if(IDYES == MessageBox(getHWnd(),L"你撞死啦!是否重新来过?", L"撞死提示",MB_YESNO)) { resetLevel(); } else { PostQuitMessage(0); } return ; } else if (g_map[newHead.r][newHead.c] == 2)//遇到奖子 { ++g_nLength ; lastOcurTime = -1; g_nSpeed -= 10; } else { //撤销尾部在地图上遗留数据 g_map[g_snake[g_nLength-1].r][g_snake[g_nLength-1].c] = 0; } memmove(g_snake+1,g_snake,sizeof(Point)*(g_nLength-1));//移动蛇的中间数据 g_snake[0].r = newHead.r; g_snake[0].c = newHead.c; //添加蛇头数据 g_map[g_snake[0].r][g_snake[0].c] = 3; ////////////////////////////////////////////////////////////////////////// } void render()//画面渲染 { HDC hDC = GetDC(getHWnd()); //获得系统绘图设备 HDC memDC = CreateCompatibleDC(0); //创建辅助绘图设备 HBITMAP bmpBack = CreateCompatibleBitmap(hDC,g_nWidth,g_nHeight);//创建掩码位图(画布) SelectObject(memDC,bmpBack); //将画布贴到绘图设备上 HPEN penBack = CreatePen(PS_SOLID,1,RGB(255,0,255));//创建画笔 SelectObject(memDC,penBack); //将画笔选到绘图设备上 //擦除背景 RECT rcClient;//区域结构 GetClientRect(getHWnd(),&rcClient);//获得客户区域 HBRUSH brushTemp = (HBRUSH)GetStockObject(WHITE_BRUSH);//获得库存物体,白色画刷。 FillRect(memDC,&rcClient,brushTemp);//填充客户区域。 ////////////////////////////////////////////////////////////////////////// //绘制2维网格,矩形画法。 for (int r=0; r<g_rows; ++ r) { for (int c=0; c<g_cols; ++c) { SelectObject(memDC,g_brushs[g_map[r][c]]); Rectangle(memDC,c*g_nSize,r*g_nSize,(c+1)*g_nSize,(r+1)*g_nSize); } } ////////////////////////////////////////////////////////////////////////// BitBlt(hDC,0,0,g_nWidth,g_nHeight,memDC,0,0,SRCCOPY);//复制到系统设备上显示 DeleteObject(penBack); //释放画笔资源 DeleteObject(bmpBack); //释放位图资源 DeleteDC(memDC); //释放辅助绘图设备 ReleaseDC(getHWnd(),hDC); //归还系统绘图设备 Sleep(10); } void clear()//资源释放 { //释放画刷资源 for (int i=0; i<4; ++i) { if(g_brushs != NULL) { DeleteObject(g_brushs[i]); g_brushs[i] = NULL; } } } /*窗口过程。如果没有处理消息请返回0,否则返回1。*/ LRESULT wndProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam ) { switch(uMsg) { case WM_KEYDOWN: { switch(wParam) { case VK_LEFT: setSnakeDir(0); break; //向左 case VK_RIGHT: setSnakeDir(1); break; //向右 case VK_UP : setSnakeDir(2); break; //向上 case VK_DOWN: setSnakeDir(3); break; //向下 case VK_ESCAPE: DestroyWindow(hwnd);break; } } break; default: return 0; } return 1; } //主函数 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE , LPSTR , int ) { if(!initApp(hInstance,L"贪吃蛇游戏",g_nWidth,g_nHeight)) { return 0; } //初始化游戏 init(); //游戏循环 mainLoop(); //释放资源 clear(); return 0; }