C++ Windows編程實現貪喫蛇(可用Dev C++實現)

最近學習Windows編程,試着自己做了個簡單的貪喫蛇遊戲。不到300行代碼,把Windows消息機制,繪製方法,多線程等知識都用上了,適合初學者入門。效果圖如下:


完整代碼附註釋:

#include <windows.h>
#include <stdio.h>
#include <vector>
#include<cstdlib>
#include<ctime>

#define SIZE 20    //指定遊戲地圖大小爲20*20
#define WIDTH 15    //指定每個格子的大小
#define FPS 10    //每秒刷新多少次
#define SPEED 3    //每刷新多少次蛇移動一格

HWND m_hwnd;//窗口句柄,唯一標示遊戲窗口
int g_nWidth = WIDTH*SIZE+10, g_nHeight = WIDTH*SIZE+33;//指定窗口大小
int map[SIZE][SIZE];//地圖數組,值爲0表示空,1表示蛇身,2表示蘋果
std::vector<int> x,y;//用於保存蛇身體每一個部位的位置
int ax,ay;//蘋果的位置
int px,py;//最後一次蛇經過的位置,用於喫過蘋果後加長蛇身
int dir;//表明當前方向
unsigned long long tk;//遊戲ticket數,即已經過多少邏輯幀
bool lock;//操作鎖,防止一次刷新操作兩次

void init(){//初始化參數
	srand((unsigned)time(NULL));
	tk = 0;
	dir = 2;
	x.push_back(0);
	y.push_back(0);
	px = py = 0;
	ax = -1;
	memset(map,0,sizeof(map));
	map[0][0] = 1;
	lock = false;
}

void gameover(){//輸了時調用
	MessageBox(m_hwnd,"你輸了","遊戲結束",MB_OK);//彈窗
	x.clear();//重新初始化
	y.clear();
	init();
}

void youwin(){//贏了時調用
	MessageBox(m_hwnd,"你贏了","遊戲結束",MB_OK);
	x.clear();
	y.clear();
	init();
}

void move(int d){//調用次函數以移動
	if(d == 0&&x[0]>0){//當移動方向爲上並且移動合法時,保存並去掉蛇尾,加一格蛇頭
		x.insert(x.begin(),x[0]-1);
		px = x[x.size()-1];
		x.erase(x.begin()+x.size()-1);
		y.insert(y.begin(),y[0]);
		py = y[y.size()-1];
		y.erase(y.begin()+y.size()-1);
	}
	else if(d == 0&&x[0]<=0)gameover();//當移動不合法時遊戲結束
	else if(d == 2&&x[0]<SIZE-1){
		x.insert(x.begin(),x[0]+1);
		px = x[x.size()-1];
		x.erase(x.begin()+x.size()-1);
		y.insert(y.begin(),y[0]);
		py = y[y.size()-1];
		y.erase(y.begin()+y.size()-1);
	}
	else if(d == 2&&x[0]>=SIZE-1)gameover();
	else if(d == 1&&y[0]>0){
		x.insert(x.begin(),x[0]);
		px = x[x.size()-1];
		x.erase(x.begin()+x.size()-1);
		y.insert(y.begin(),y[0]-1);
		py = y[y.size()-1];
		y.erase(y.begin()+y.size()-1);
	}
	else if(d == 1&&y[0]<=0)gameover();
	else if(d == 3&&y[0]<SIZE-1){
		x.insert(x.begin(),x[0]);
		px = x[x.size()-1];
		x.erase(x.begin()+x.size()-1);
		y.insert(y.begin(),y[0]+1);
		py = y[y.size()-1];
		y.erase(y.begin()+y.size()-1);
	}
	else if(d == 3&&y[0]>=SIZE-1)gameover();
}

void update(){//遊戲更新主邏輯,每幀調用此函數
	if(tk%SPEED==0){//每隔SPEED間隔幀移動一次
		move(dir);
		lock = false;
	}
	if(x[0]==ax&&y[0]==ay){//如果喫到了蘋果
		x.push_back(px);//加蛇尾
		y.push_back(py);
		ax = -1;//去掉蘋果
	}
	if(x.size()>=SIZE*SIZE){//如果蛇的長度大於等於地圖大小,遊戲結束
		youwin();
		return;
	}
	memset(map,0,sizeof(map));//刷新地圖
	for(int i = 0; i < x.size(); i++){
		if(map[x[i]][y[i]]==0)map[x[i]][y[i]] = 1;//如果沒有蛇身阻擋則放置蛇身
		else{//否則遊戲結束
			gameover();
			return;
		}
	}
	if(ax==-1){//蘋果被喫,刷新蘋果
		ax = rand()%SIZE;
		ay = rand()%SIZE;
		while(map[ax][ay]==1){
			ax = rand()%SIZE;
			ay = rand()%SIZE;
		}
	}
	map[ax][ay] = 2;
	tk++;
}

/* 此函數用於處理窗口接受的所有消息 */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
	switch(Message) {
		
		/* Upon destruction, tell the main thread to stop */
		case WM_DESTROY: {
			PostQuitMessage(0);
			break;
		}
		
		case WM_KEYDOWN:{//當收到按鍵消息
			if(!lock){//且操作鎖沒有鎖上,則改變蛇的移動方向並上鎖
				if(wParam == VK_UP&&(dir-0)%2!=0){
					dir = 0;
					lock = true;
				}
				else if(wParam == VK_DOWN&&(dir-2)%2!=0){
					dir = 2;
					lock = true;
				}
				else if(wParam == VK_LEFT&&(dir-1)%2!=0){
					dir = 1;
					lock = true;
				}
				else if(wParam == VK_RIGHT&&(dir-3)%2!=0){
					dir = 3;
					lock = true;
				}
			}
			if(wParam == VK_ESCAPE)PostQuitMessage(0);//如果是ESC鍵則退出
			break;
		}
		
		/* All other messages (a lot of them) are processed using default procedures */
		default:
			return DefWindowProc(hwnd, Message, wParam, lParam);
	}
	return 0;
}

void render()//渲染函數,用於繪製遊戲圖像
{  
    HDC hDC = GetDC(m_hwnd);//定義窗口句柄的DC
  
    HDC memDC = CreateCompatibleDC(0);//定義兼容DC
  
    HBITMAP bmpBack = CreateCompatibleBitmap(hDC,g_nWidth,g_nHeight);//定義位圖畫布
    SelectObject(memDC,bmpBack);
  
    HPEN penBack = CreatePen(PS_SOLID,1,RGB(0,0,0));//定義畫筆
    SelectObject(memDC,penBack);
  
    HBRUSH brushBack = CreateSolidBrush(RGB(255,255,255));//定義背景畫刷
    SelectObject(memDC,brushBack);
  
    RECT rcClient;
    GetClientRect(m_hwnd,&rcClient);
    FillRect(memDC,&rcClient,brushBack);//用背景畫刷以矩形填充整個窗口
    HBRUSH brushObj = CreateSolidBrush(RGB(255,0,0));//定義蛇身畫刷
    HBRUSH brushApple = CreateSolidBrush(RGB(0,0,255));//定義蘋果畫刷
    int dw = WIDTH;  
    int rows = SIZE;  
    int cols = SIZE;  
    for (int r=0; r<rows; ++ r){//繪製整個矩陣
        for (int c=0; c<cols; ++c){  
            if (map[r][c]==1){  
                SelectObject(memDC,brushObj);
            }
            else if(map[r][c] == 2){
            	SelectObject(memDC,brushApple);
			}
            else{  
                SelectObject(memDC,brushBack);  
            }  
            Rectangle(memDC,c*dw,r*dw,(c+1)*dw,(r+1)*dw);  
        }  
    }  
  
    DeleteObject(brushObj);//釋放資源
    BitBlt(hDC,0,0,g_nWidth,g_nHeight,memDC,0,0,SRCCOPY);
    DeleteObject(penBack);
    DeleteObject(brushBack);
    DeleteObject(bmpBack); 
    DeleteDC(memDC);
    ReleaseDC(m_hwnd,hDC);
}  

DWORD WINAPI startLoop(LPVOID lpParamter){//開始主循環,定義這個函數是爲了多線程運行,函數名隨意,函數格式一定
    while(1){
		update();
		render();
		Sleep(1000/FPS);
	}
    return 0L;
}

/* windows編程的主函數 */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	WNDCLASSEX wc; /* A properties struct of our window */
	HWND hwnd; /* A 'HANDLE', hence the H, or a pointer to our window */
	MSG msg; /* A temporary location for all messages */

	/* zero out the struct and set the stuff we want to modify */
	memset(&wc,0,sizeof(wc));
	wc.cbSize		 = sizeof(WNDCLASSEX);
	wc.lpfnWndProc	 = WndProc; /* This is where we will send messages to */
	wc.hInstance	 = hInstance;
	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
	
	/* White, COLOR_WINDOW is just a #define for a system color, try Ctrl+Clicking it */
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszClassName = "WindowClass";
	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION); /* Load a standard icon */
	wc.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION); /* use the name "A" to use the project icon */

	if(!RegisterClassEx(&wc)) {
		MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
		return 0;
	}

	hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"WindowClass","·½Ïò¼üÒƶ¯£¬ESCÍ˳ö",WS_VISIBLE|WS_EX_STATICEDGE,
		CW_USEDEFAULT, /* x */
		CW_USEDEFAULT, /* y */
		g_nWidth, /* width */
		g_nHeight, /* height */
		NULL,NULL,hInstance,NULL);

	if(hwnd == NULL) {
		MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
		return 0;
	}
	
	m_hwnd = hwnd;
	init();//初始化

	HANDLE hThread = CreateThread(NULL, 0, startLoop, NULL, 0, NULL);//創建線程
        CloseHandle(hThread);//釋放線程句柄

	/*
		This is the heart of our program where all input is processed and 
		sent to WndProc. Note that GetMessage blocks code flow until it receives something, so
		this loop will not produce unreasonably high CPU usage
	*/
	while(GetMessage(&msg, NULL, 0, 0) > 0) { /* If no error is received... */
		TranslateMessage(&msg); /* Translate key codes to chars if present */
		DispatchMessage(&msg); /* Send it to WndProc */
	}
	return msg.wParam;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章