小型项目:贪吃蛇中学习win32 API

来源:互联网 发布:line聊天软件 官方网站 编辑:程序博客网 时间:2024/04/30 11:27

贪吃蛇

用C++和少量win32 Api实现一个经典小游戏:贪吃蛇,主要是为了学习之用。

一.首先构思贪吃蛇类应该具有哪些属性和功能,并且需要用什么样的数据结构来存储数据。我们把贪吃蛇和它将要吃的贪物看成一个整体来考虑。

属性有:

1.贪吃蛇的蛇头Head

2.贪吃蛇的身长Length

3.是否存在食物isExistFood

4.食物所在的坐标(Food_x,Food_y)

5.蛇头的方向direction ={(-1,0)(1,0)(0,1)(0,-1)}

6.游戏的速度Speed

应该具有的功能:

1.根据方向进行挪动Move()

2.重绘贪吃蛇redraw(),因为需要不停地移动,由此需要经常重绘

3.判断游戏是否结束,isGameOver()程序终止条件

4.随机在地图中生成食物createfood()

5.游戏进程函数onGame()

6.初始游戏界面Init()

7.删除蛇身结点函数,避免内存泄露。del()

因此,贪吃蛇类的大体框架就可以出来了。

class Snake{public:node *head;//蛇头指针int Length;//蛇长int level;//速度(等级)bool isFoodExist;int Food_x,Food_y;//食物存在标记int dir[2];//蛇头方向public:void del();//删除结点,避免内存泄露void Init();//初始化界面void ReDraw();//重绘蛇身函数void CreateFood();//随机生成食物bool isGameOver();//判断游戏是否结束void onGame();//游戏进程函数void Move();//移动};
我们在初始化的时候,贪吃蛇的身长应该有三个结点,因此可以添加

Snake():Length(3),isFoodExist(false),level(1)//构造函数{   dir[0] = 1,dir[1] = 0;head = new node(4,2);//申请起始蛇身3节node *temp1 = new node(3,2);node *temp2 = new node(2,2);head->next = temp1;temp1->next = temp2;temp2->next = NULL;}
接下来就实现各个部分的功能代码:

最主要的部分是游戏进程函数onGame(),它的主要功能有

1.贪吃蛇的移动,Move

2.贪吃蛇的重绘

3.处理上下左右的方向

4.处理吃到食物和不吃到食物的处理方法。吃到食物,则置食物标示为false,如果没吃到食物,则删除蛇尾结点。

完整代码

#include <iostream>#include <windows.h>#include <conio.h>#include <time.h>#define _CRTDBG_MAP_ALLOC#include <stdlib.h>#include <crtdbg.h>using namespace std;#define RIGHT 40#define LEFT 1#define TOP 1#define BOTTOM 25struct node//蛇身(链表结构){int x,y;//蛇身结点坐标node *next;//下一个结点node(){}node(int _x,int _y):x(_x),y(_y),next(NULL){}node(const node&){}~node(){}};void SetPos(int i,int j)//设定光标位置,win32 API{COORD pos = {i-1,j-1};//坐标变量HANDLE Out = GetStdHandle(STD_OUTPUT_HANDLE);//获取输出句柄SetConsoleCursorPosition(Out,pos);//设定控制台光标位置}class Snake{public:node *head;//蛇头指针int Length;//蛇长int level;//速度(等级)bool isFoodExist;int Food_x,Food_y;//食物存在标记int dir[2];//蛇头方向public:Snake():Length(3),isFoodExist(false),level(1)//构造函数{   dir[0] = 1,dir[1] = 0;head = new node(4,2);//申请起始蛇身3节node *temp1 = new node(3,2);node *temp2 = new node(2,2);head->next = temp1;temp1->next = temp2;temp2->next = NULL;}void del();//删除结点,避免内存泄露void Init();//初始化界面void ReDraw();//重绘蛇身函数void CreateFood();//随机生成食物bool isGameOver();//判断游戏是否结束void onGame();//游戏进程函数void Move();//移动};void Snake::del(){node *p = head;while (p != NULL){p = p->next;delete head;head = p;}}void Snake::Init()//初始化界面{int i;for (i=1; i <= RIGHT; ++i){SetPos(i,1);cout << "-";SetPos(i,25);cout << "-";}for (i=2; i <= BOTTOM; ++i){SetPos(1,i);cout << "|";SetPos(RIGHT,i);cout << "|";}SetPos(54,3);cout << "贪吃蛇";SetPos(54,5);cout << "长度:" << Length;SetPos(54,7);cout << "等级:" << level;}void Snake::CreateFood()//随机出食物的坐标并判断坐标是否合法{node *p = NULL;srand((unsigned int)time(0));    //用系统时间来做随机数种子while (true) {Food_x = rand() % 38 + 2;//随机出食物的坐标Food_y = rand() % 23 + 2;p = head;while (p!=NULL)//判断食物是否产生在蛇体内{if (p->x==Food_x && p->y==Food_y)//有冲突break;p = p->next;}if (p==NULL)//所有结点都不冲突。生成成功break;}}bool Snake::isGameOver(){if (head->x >= RIGHT || head->x <=LEFT ||head->y <= TOP || head->y >= BOTTOM)//是否撞到边缘return true;node *p = head->next;while (p!=NULL){if ((head->x==p->x) && (head->y==p->y))//撞到蛇身return true;p = p->next;}return false;}void Snake::ReDraw()//重绘蛇身{node *p = head;while (p!=NULL){SetPos(p->x,p->y);cout << "*";p = p->next;}SetPos(Food_x,Food_y);cout << "*";}void Snake::Move(){node *New;New = new node(head->x+dir[0],head->y+dir[1]);//新的蛇身结点New->next = head;head = New;}void Snake::onGame(){system("cls");//刷新屏幕node *p = NULL;Init();//初始化界面ReDraw();//重绘蛇身char x;while (true){if (_kbhit())         //_kbhit()判断是否有键盘操作{x = _getch();  //重缓冲区读出一个字符赋给xif ((x=='W'||x=='w') && !(dir[0]==0&&dir[1]==1))  //改变蛇的方向(不可以是反方向)下dir[0] = 0, dir[1] = -1;if ((x=='S'||x=='s') && !(dir[0]==0&&dir[1]==-1)) //上dir[0] = 0, dir[1] = 1;if ((x=='A'||x=='a') && !(dir[0]==1&&dir[1]==0))  //右dir[0] = -1, dir[1] = 0;if ((x=='D'||x=='d') && !(dir[0]==-1&&dir[1]==0)) //左dir[0] = 1, dir[1] = 0;while (_kbhit())  //读掉这之后所有的键盘输入_getch();}if (!isFoodExist)     //如果视图中没有食物,则随机生成食物{CreateFood();isFoodExist = true;}Move();//移动蛇if (head->x==Food_x && head->y==Food_y)//如果蛇吃到了食物{isFoodExist = false;++Length;//蛇身+1SetPos(60,5);cout << "长度 : " << Length;//改变界面信息if (Length%10==0)//每十个蛇身升一级{++level;SetPos(60,7);cout << "等级 : " << level;}if (level==10)//最高等级达成。退出游戏break;}ReDraw();//重绘蛇身if (isFoodExist)//如果没有吃到食物,需要删除蛇尾。{p=head;while ((p->next)->next!=NULL)p=p->next;SetPos(p->next->x,p->next->y);cout << " ";delete(p->next);p->next=NULL;}if(isGameOver())//判断是否游戏结束break;Sleep(500-level*50);//等待,具体时间和等级有关}system("cls");if (level==10)//通关{SetPos(10,5);cout << "成功通关!" << endl;}else{SetPos(10,5);cout << "游戏结束!你的成绩为:" << Length << endl;}}int main(){Snake game;system("cls");cout<<"*                      贪吃蛇                       *"<<endl;cout<<"*      W,A,S,D控制移动                  *"<<endl;cout<<"*       每10节蛇身升一级,并提高速度,10级通关      *"<<endl;_getch();game.onGame();game.del();_CrtDumpMemoryLeaks();//内存泄露检测!return 0;}

2.将上面的程序用SDK程序改写成图形化界面,while(true){}逻辑需要用SetTimer来改写.其它大体逻辑不变.

#include <Windows.h>#include <time.h>#define UNIT 20struct NODE//蛇身(链表结构){int x, y;NODE *next;NODE(int _x,int _y):x(_x),y(_y),next(NULL){}};RECT RectWindow;NODE *head = NULL;int Length = 3;int level = 1;bool isFoodExist = FALSE;int Food_x,Food_y;int dir[2];bool isGameOver(){if (head->x >= RectWindow.right || head->x <= RectWindow.left || head->y <= RectWindow.top || head->y >= RectWindow.bottom)//是否撞到边缘return true;NODE *p = head->next;while (p){if ((head->x==p->x) && (head->y==p->y))//撞到蛇身return true;p = p->next;}return false;}void AddNewHead(){NODE *New = new NODE(head->x+dir[0],head->y+dir[1]);//新的蛇身结点New->next = head;head = New;}void CreateFood()//随机出食物的坐标并判断坐标是否合法{NODE *p = NULL;srand((unsigned int)time(0)); //用系统时间来做随机数种子while (true) {Food_x = (rand() % (RectWindow.right-60)) / UNIT * UNIT + UNIT ;  //保证产生的食物与蛇可以连接Food_y = (rand() % (RectWindow.bottom-60)) / UNIT * UNIT + UNIT;p = head;while (p)//判断食物是否产生在蛇体内{if (p->x==Food_x && p->y==Food_y)//有冲突break;p = p->next;}if (p==NULL)//所有结点都不冲突。生成成功break;}}void DrawSnake(HWND hwnd)//重绘蛇身{NODE *p = head;HDC hdc = GetDC(hwnd);//HBRUSH brush = (HBRUSH)SelectObject(hdc,GetStockObject(WHITE_BRUSH));//把黑色画刷选进hdcwhile (p){Rectangle(hdc,p->x,p->y,p->x+UNIT,p->y+UNIT);p = p->next;}Rectangle(hdc,Food_x,Food_y,Food_x+UNIT,Food_y+UNIT);//SelectObject(hdc,brush);//恢复handleReleaseDC(hwnd,hdc);}LRESULT CALLBACK WndProc(HWND hwnd,      // handle to windowUINT uMsg,      // message identifierWPARAM wParam,  // first message parameterLPARAM lParam);   // second message parameter);int WINAPI WinMain(   HINSTANCE hInstance,      // handle to current instance   HINSTANCE hPrevInstance,  // handle to previous instance   LPSTR lpCmdLine,          // command line   int nCmdShow              // show state   ){WNDCLASS wc;wc.cbClsExtra = 0;wc.cbWndExtra = 0;wc.hbrBackground = (HBRUSH)COLOR_WINDOW;wc.hCursor = LoadCursor(NULL,IDC_ARROW);wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);wc.hInstance = hInstance;wc.lpfnWndProc = WndProc;wc.lpszClassName = "ls";wc.lpszMenuName = "ls";wc.style = CS_VREDRAW | CS_HREDRAW;RegisterClass(&wc);HWND hwnd = CreateWindow("ls","ls",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);if (!hwnd){return FALSE;}ShowWindow(hwnd,nCmdShow);UpdateWindow(hwnd);MSG msg;while (GetMessage(&msg,NULL,0,0) > 0){TranslateMessage(&msg);DispatchMessage(&msg);}return TRUE;}LRESULT CALLBACK WndProc(HWND hwnd,      // handle to windowUINT uMsg,      // message identifierWPARAM wParam,  // first message parameterLPARAM lParam)   // second message parameter);{PAINTSTRUCT ps ;HDC hdc;switch (uMsg){case WM_CREATE:{isFoodExist = FALSE;dir[0] = -UNIT,dir[1] = 0;//debughead = new NODE(400,200);//申请起始蛇身3节NODE *temp1 = new NODE(420,200);NODE *temp2 = new NODE(440,200);head->next = temp1;temp1->next = temp2;temp2->next = NULL;GetClientRect(hwnd, &RectWindow);  //获取窗口大小SetTimer (hwnd, 1, 200, NULL) ;//设置时间break;}case WM_PAINT:{hdc = BeginPaint (hwnd, &ps);if (!isFoodExist)   //如果视图中没有食物,则随机生成食物{CreateFood();isFoodExist = true;}DrawSnake(hwnd);EndPaint(hwnd,&ps);break;}case WM_KEYDOWN:{if ((wParam=='W'||wParam=='w') && !(dir[0]==0&&dir[1]==UNIT)) //改变蛇的方向(不可以是反方向)下dir[0] = 0, dir[1] = -UNIT;if ((wParam=='S'||wParam=='s') && !(dir[0]==0&&dir[1]==-UNIT)) //上dir[0] = 0, dir[1] = UNIT;if ((wParam=='A'||wParam=='a') && !(dir[0]==UNIT&&dir[1]==0)) //右dir[0] = -UNIT, dir[1] = 0;if ((wParam=='D'||wParam=='d') && !(dir[0]==-UNIT&&dir[1]==0)) //左dir[0] = UNIT, dir[1] = 0;}case WM_TIMER:{if (isGameOver()){KillTimer(hwnd,1);return 0;}AddNewHead();if (head->x==Food_x && head->y==Food_y)//如果蛇吃到了食物{isFoodExist = false;}if (isFoodExist)//如果没有吃到食物,需要删除蛇尾。{NODE *p = head;while ((p->next)->next!=NULL)p=p->next;delete(p->next);p->next=NULL;}InvalidateRect(hwnd, NULL, TRUE);break;}case WM_DESTROY:PostQuitMessage(0) ;break;default:return DefWindowProc(hwnd,uMsg,wParam,lParam);}return 0;}



0 0
原创粉丝点击