【Chapter 3】基于Chp2的贪吃蛇的例子

稍微看了下

还是好难啊啊啊啊啊QAQ

Please请对我好一点啊啊啊啊啊TAT

 

放代码慢慢分析

代码依旧是有俱乐部培训的师兄提供

什么时候能自己写啊[望天]

代码渣画画渣读书废

都不知道怎么有勇气活到现在的是么嗯哼?

 

会写注释的程序员都是好男人/女人

#include <windows.h>
#include <list>
#include <stdlib.h>
#include <time.h>

#define LEFT 0
#define RIGHT 1
#define UP 2
#define DOWN 3

#define COL_NUM 20
#define ROW_NUM 20
#define BLOCK_SIZE 25

void Game_Init(HWND hwmd);
void SetFood();
void Game_Exit();
void Game_Render(HDC hdc);
bool Game_Update(HWND hwmd);

struct Block	//For every little block
{
	int col;
	int row;
	bool operator== (Block& b) {
		return col == b.col && row == b.row;
	}
	bool operator!= (Block& b) {
		return !((*this) == b);
	}
};

std::list<Block> snake;
Block food;
int direction;
HBRUSH redBrush, orangeBrush, whiteBrush;

inline void Fill_Block(HDC hdc, Block& b);

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */


int WINAPI WinMain (HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int iCmdShow)
					
{
    HWND hwnd;               /* This is the handle for our window */
    MSG msg;            /* Here messages to the application are saved */
    WNDCLASS wndclass;        /* Data structure for the windowclass */
	
	static char szClassName[ ] = "WindowsApp";
	
    /* 第一步:注册窗口类 */
    wndclass.hInstance = hInstance;
    wndclass.lpszClassName = szClassName;
    wndclass.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;                 /* Catch double-clicks */
    wndclass.cbWndExtra = 0;
	wndclass.cbClsExtra = 0;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wndclass.lpszMenuName = NULL;
	
    /* Register the window class, and if it fails quit the program */
    if (!RegisterClass (&wndclass))
        return 0;
	
    /* 第二步:创建窗口 */
	hwnd = CreateWindow(
		szClassName, 
		TEXT("MyApp"), 
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,// initial x position
		CW_USEDEFAULT,// initial y position       
		600,// initial x size       
		600,// initial y size
		NULL,
		NULL,
		hInstance,
		NULL
		);
	
    /* 第三步:显示窗口 */
    ShowWindow (hwnd, iCmdShow);
	UpdateWindow(hwnd);
	
    /* 第四步:消息循环 */
    while (GetMessage (&msg, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&msg);
        /* Send message to WindowProcedure */
        DispatchMessage(&msg);
    }
	
    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return msg.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
    switch (message)                  /* handle the messages */
    {
	case WM_CREATE:
		Game_Init(hwnd);
		return 0;
	case WM_DESTROY:
		Game_Exit();
		PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
		break;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		Game_Render(hdc);
		EndPaint(hwnd, &ps);
		return 0;
	case WM_TIMER:
		if(wParam == 1) {
			if(!Game_Update(hwnd)) {
				KillTimer(hwnd, 1);
				MessageBox(hwnd, "Game Over!", NULL, 0);
				DestroyWindow(hwnd);
			}
		}
		return 0;
	case WM_KEYDOWN:
		if(wParam == VK_LEFT && direction != RIGHT) direction = LEFT;
		if(wParam == VK_RIGHT && direction != LEFT) direction = RIGHT;
		if(wParam == VK_UP && direction != DOWN) direction = UP;
		if(wParam == VK_DOWN && direction != UP) direction = DOWN;
		return 0;
	default:                      /* for messages that we don't deal with */
		return DefWindowProc (hwnd, message, wParam, lParam);
    }
	
    return 0;
}

void Game_Init(HWND hwmd)
{
	srand(time(NULL));

	direction = LEFT;

	Block b;
	b.row = 9;
	b.col = 9;
	
	int i;
	for (i = 0; i < 7; i++)
	{
		snake.push_back(b);
		b.col++;
	}

	SetFood();

	//create some brushes
	HDC hdc = GetDC(hwmd);
	redBrush = (HBRUSH)CreateSolidBrush(RGB(255, 0, 0));
	orangeBrush = (HBRUSH)CreateSolidBrush(RGB(255, 102, 0));
	whiteBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
	ReleaseDC(hwmd, hdc);

	SetTimer(hwmd, 1, 200, NULL);
}

void SetFood()
{
	while(true) {
		food.row = rand() % ROW_NUM;
		food.col = rand() % COL_NUM;

		//if food is not in snake position, quit loop
		std::list<Block>::iterator it = snake.begin();
		for (; it != snake.end(); it++)
		{
			if(food == (*it)) {
				break;
			}
		}

		if(it == snake.end()) break;
	}
}

void Game_Exit()
{
	DeleteObject(redBrush);
	DeleteObject(orangeBrush);
}

void Game_Render(HDC hdc)
{
	SelectObject(hdc, (HPEN)GetStockObject(NULL_PEN));
	//draw white area
	SelectObject(hdc, whiteBrush);
	Rectangle(hdc, 0, 0, COL_NUM * BLOCK_SIZE, ROW_NUM * BLOCK_SIZE);

	//draw snake
	SelectObject(hdc, redBrush);	
	std::list<Block>::iterator it = snake.begin();
	for (; it != snake.end(); it++)
	{
		Fill_Block(hdc, *it);
	}

	//draw food
	SelectObject(hdc, orangeBrush);
	Fill_Block(hdc, food);
}

void Fill_Block(HDC hdc, Block& b)
{
	Rectangle(hdc, b.col * BLOCK_SIZE, b.row * BLOCK_SIZE, (b.col + 1) * BLOCK_SIZE, (b.row + 1) * BLOCK_SIZE);
}

bool Game_Update(HWND hwmd)
{
	Block head = snake.front();
	switch(direction){
	case LEFT:
		head.col--;
		if(head.col < 0) return false;
		break;
	case RIGHT:
		head.col++;
		if(head.col >= COL_NUM) return false;
		break;
	case UP:
		head.row--;
		if(head.row < 0) return false;
		break;
	case DOWN:
		head.row++;
		if(head.row >= ROW_NUM) return false;
		break;
	}

	std::list<Block>::iterator it = snake.begin();
	for (; it != snake.end(); it++)
	{
		if(head == *it)
			return false;
	}

	snake.push_front(head);

	HDC hdc = GetDC(hwmd);
	SelectObject(hdc, (HPEN)GetStockObject(NULL_PEN));
	SelectObject(hdc, redBrush);
	Fill_Block(hdc, head);
	
	if(head == food) {
		SetFood();
		SelectObject(hdc, orangeBrush);
		Fill_Block(hdc, food);
	}
	else {
		SelectObject(hdc, whiteBrush);
		Block tail = snake.back();
		snake.pop_back();
		Fill_Block(hdc, tail);
	}

	ReleaseDC(hwmd, hdc);
	return true;
}


有一个类block

定义了每个小block的位置(row和col)

下面又为snake定义了<block>list,为food定义了一个block

 

WinMain函数里面没有什么大变动

主要看窗口过程函数里面的操作。

 

首先理解几个函数的意思:

void Game_Init(HWND hwmd)
{
	srand(time(NULL));

	direction = LEFT;

	Block b;
	b.row = 9;
	b.col = 9;
	
	int i;
	for (i = 0; i < 7; i++)
	{
		snake.push_back(b);
		b.col++;
	}

	SetFood();

	//create some brushes
	HDC hdc = GetDC(hwmd);
	redBrush = (HBRUSH)CreateSolidBrush(RGB(255, 0, 0));
	orangeBrush = (HBRUSH)CreateSolidBrush(RGB(255, 102, 0));
	whiteBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
	ReleaseDC(hwmd, hdc);

	SetTimer(hwmd, 1, 200, NULL);
}


 

大抵上是游戏刚开始的时候定义了一条在位置坐标(9,9)的方向向左的一条长度为7个格子的snake

和setfood放置食物(另一个函数)

还定义了各种刷子brush的RGB颜色值

 

ReleaseDC是释放窗口

使用GetDC或者GetWindowDC等API时,会向系统检索设备上下文环境,换句话说,就是系统动态分配了资源让你可以拥有对这个设备(这个设备可以是屏幕、窗口、客户区域等)一定的控制权,比如绘图。

使用完毕后,这个动态分配的资源应该还给系统,于是要ReleaseDC。有借有还,再借不难。有借无还,系统玩完。

函数原型:int ReleaseDC(HWND hWnd, HDC hdc);
参数:
hWnd:指向要释放的设备上下文环境所在的窗口的句柄。
hDC:指向要释放的设备上下文环境的句柄。
返回值:返回值说明了设备上下文环境是否释放;如果释放成功,则返回值为1;如果没有释放成功,则返回值为0。

 

还设置了一个时钟

 

SetTimer函数用于创建一个计时器,KillTimer函数用于销毁一个计时器。计时器属于系统资源,使用完应及时销毁。

 

SetTimer的函数原型如下:
UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc ) ;


其中:
hWnd是和timer关联的窗口句柄,此窗口必须为调用SetTimer的线程所有;如果hWnd为NULL,没有窗口和timer相关联并且nIDEvent参数被忽略


nIDEvent是timer的标识,为非零值;如果hWnd为NULL则被忽略;如果hWnd非NULL而且与timer相关联的窗口已经存在一个为此标识的timer,则此次SetTimer调用将用新的timer代替原来的timer。timer标识和窗口相关,两个不同的窗口可以拥有nIDEvent相同的tiemr


uElapse是以毫秒指定的计时间隔值,范围为1毫秒到4,294,967,295毫秒(将近50天),这个值指示Windows每隔多久时间给程序发送WM_TIMER消息。

 
lpTimerFunc是一个回调函数的指针,俗称TimerFunc;如果lpTimerFunc为NULL,系统将向应用程序队列发送WM_TIMER消息;如果lpTimerFunc指定了一个值,DefWindowProc将在处理WM_TIMER消息时调用这个lpTimerFunc所指向的回调函数,因此即使使用TimerProc代替处理WM_TIMER也需要向窗口分发消息。

关于SetTimer的返回值:如果hWnd为NULL,返回值为新建立的timer的ID,如果hWnd非NULL,返回一个非0整数,如果SetTimer调用失败则返回0

 

KillTimer的函数原型为:BOOL KillTimer( HWND hWnd, UINT_PTR uIDEvent ) ; 参数意义同SetTimer。

 

void SetFood()
{
	while(true) {
		food.row = rand() % ROW_NUM;
		food.col = rand() % COL_NUM;

		//if food is not in snake position, quit loop
		std::list<Block>::iterator it = snake.begin();
		for (; it != snake.end(); it++)
		{
			if(food == (*it)) {
				break;
			}
		}

		if(it == snake.end()) break;
	}
}


 

一起来看SetFood()

即随机找个位置个food,但是要满足不在snake体内的条件。

若在snake体内则跳出这个循环。

 

void Game_Exit()即为游戏退出

销毁格子

 

void Game_Render(HDC hdc)
{
	SelectObject(hdc, (HPEN)GetStockObject(NULL_PEN));
	//draw white area
	SelectObject(hdc, whiteBrush);
	Rectangle(hdc, 0, 0, COL_NUM * BLOCK_SIZE, ROW_NUM * BLOCK_SIZE);

	//draw snake
	SelectObject(hdc, redBrush);	
	std::list<Block>::iterator it = snake.begin();
	for (; it != snake.end(); it++)
	{
		Fill_Block(hdc, *it);
	}

	//draw food
	SelectObject(hdc, orangeBrush);
	Fill_Block(hdc, food);
}


 

这个看名字看不出来干嘛的- -

看备注是构造这个游戏世界的意思么

刚刚是构造了snake和food

这个是把snake和food都用特定颜色填充到世界里

 

void Fill_Block(HDC hdc, Block& b)
{
	Rectangle(hdc, b.col * BLOCK_SIZE, b.row * BLOCK_SIZE, (b.col + 1) * BLOCK_SIZE, (b.row + 1) * BLOCK_SIZE);
}


 

Rectangle是画矩形函数

函数名: rectangle
功 能: 画一个矩形
用 法: void far rectangle(int left, int top, int right, int bottom);
参数说明:(left ,top )为矩形的左上坐标,(right,bottom)为矩形的右下坐标,两者可确定一个矩形的大小

 

bool Game_Update(HWND hwmd)
{
	Block head = snake.front();
	switch(direction){
	case LEFT:
		head.col--;
		if(head.col < 0) return false;
		break;
	case RIGHT:
		head.col++;
		if(head.col >= COL_NUM) return false;
		break;
	case UP:
		head.row--;
		if(head.row < 0) return false;
		break;
	case DOWN:
		head.row++;
		if(head.row >= ROW_NUM) return false;
		break;
	}

	std::list<Block>::iterator it = snake.begin();
	for (; it != snake.end(); it++)
	{
		if(head == *it)
			return false;
	}

	snake.push_front(head);

	HDC hdc = GetDC(hwmd);
	SelectObject(hdc, (HPEN)GetStockObject(NULL_PEN));
	SelectObject(hdc, redBrush);
	Fill_Block(hdc, head);
	
	if(head == food) {
		SetFood();
		SelectObject(hdc, orangeBrush);
		Fill_Block(hdc, food);
	}
	else {
		SelectObject(hdc, whiteBrush);
		Block tail = snake.back();
		snake.pop_back();
		Fill_Block(hdc, tail);
	}

	ReleaseDC(hwmd, hdc);
	return true;
}


 

Game_Update应该是贪吃蛇的核心。即snake的移动。

键盘上的上下左右定义成0,1,2,3来识别方向。

当吃到food的时候,尾巴的那一格red格子不变成白色的格子。

 

===============================================

 

分析完了

但是自己写还是不会啊怎么破!!!!

一个上午了分析了一章ppt

还有许许多多的ppt

今早7点半起~

果然比睡到12点然后再捶胸敦子心情舒畅一些

刚宿舍还合力杀死了一只超级大的蟑螂QAQ吓死

 

总之今天可以奖励自己一下么

肚子还疼着TAT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值