C语言俄罗斯方块(简易版)

本俄罗斯方块全部用绘图函数实现方块的绘制,没有一点资源,因此源代码很小巧,整个程序编译链接好,也就10K多吧。非常小巧的俄罗斯方块。

设计思想:
1、将游戏区域划分为18行*10列的棋盘,设立一个布尔型的二维数组变量,以表示棋盘上各个地方是否有方块。
2、用4个顶点代表各种不同形状的方块,一旦方块在游戏区域中,就把对应的布尔型二维数组变量置为真,表示该方格已经有方块了。
3、如上做方便方块移动是否会碰撞的判断。
4、代码已经修正了一个小BUG。
5、压缩包中的文件是未经修改的源代码,此处的代码为最新。
6、方向键上为改变形状,下为直接落到底部。p键为暂停(或者Pause键)

#include  //为了使用API函数
#include 	//为了使用定时器
#include   //为了使用随机数

#define BLOCKWIDTH		20  //单个方块大小
#define NUMLINEBLOCKS	18  //行数
#define NUMCOLUMNBLOCKS	10  //列数
#define ID_TIMER		1	//定时器ID
#define BLOCKSTYLES		(sizeof (Blocks) / sizeof (Blocks[0]))  //方块的种类数

//游戏区各方格顶点布尔值,代表该方格是否有方块
bool	GameClient[NUMCOLUMNBLOCKS][NUMLINEBLOCKS];
static int		F, S, cF, cS;	//随机方块图形对应的第一、二纬
static int		Score;  //得分

//定义各方块形状,以点表示
struct  
{
	POINT	pt[4];
}
Blocks[][4] = 
{
	//正7
	0, 0, 1, 0, 1, 1, 1, 2,  2, 0, 0, 1, 1, 1, 2, 1,  1, 0, 1, 1, 1, 2, 2, 2,  0, 1, 1, 1, 2, 1, 0, 2,
	//反7
	1, 0, 2, 0, 1, 1, 1, 2,  0, 1, 1, 1, 2, 1, 2, 2,  1, 0, 1, 1, 0, 2, 1, 2,  0, 0, 0, 1, 1, 1, 2, 1,
	//1
	1, 0, 1, 1, 1, 2, 1, 3,  0, 1, 1, 1, 2, 1, 3, 1,  1, 0, 1, 1, 1, 2, 1, 3,  0, 1, 1, 1, 2, 1, 3, 1,
	//Z
	0, 0, 1, 0, 1, 1, 2, 1,  2, 0, 1, 1, 2, 1, 1, 2,  0, 0, 1, 0, 1, 1, 2, 1,  2, 0, 1, 1, 2, 1, 1, 2,
	//反Z
	1, 0, 2, 0, 0, 1, 1, 1,  1, 0, 1, 1, 2, 1, 2, 2,  1, 0, 2, 0, 0, 1, 1, 1,  1, 0, 1, 1, 2, 1, 2, 2,
	//田字
	0, 0, 1, 0, 0, 1, 1, 1,  0, 0, 1, 0, 0, 1, 1, 1,  0, 0, 1, 0, 0, 1, 1, 1,  0, 0, 1, 0, 0, 1, 1, 1,
	//尖头
	1, 0, 0, 1, 1, 1, 2, 1,  0, 0, 0, 1, 1, 1, 0, 2,  0, 0, 1, 0, 2, 0, 1, 1,  1, 0, 0, 1, 1, 1, 1, 2
};

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASS wndcls;
	TCHAR szClassName[] = TEXT("Terics"),
		  szWindowName[] = TEXT("Aka's Terics");
	static POINT Block[4];

	wndcls.cbClsExtra		= 0;
	wndcls.cbWndExtra		= 0;
	wndcls.hbrBackground	= static_cast(GetStockObject(WHITE_BRUSH));
	wndcls.hCursor			= LoadCursor(hInstance, IDC_ARROW);
	wndcls.hIcon			= LoadIcon(hInstance, IDI_APPLICATION);
	wndcls.hInstance		= hInstance;
	wndcls.lpfnWndProc		= WndProc;
	wndcls.lpszClassName	= szClassName;
	wndcls.lpszMenuName		= NULL;
	wndcls.style			= CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wndcls);

	HWND hwnd = CreateWindow(szClassName, szWindowName, WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX,
		CW_USEDEFAULT, CW_USEDEFAULT, (NUMCOLUMNBLOCKS + 10) * BLOCKWIDTH,
		(NUMLINEBLOCKS + 3) * BLOCKWIDTH,
		NULL, NULL, hInstance, NULL);
	ShowWindow(hwnd, SW_SHOWNORMAL);
	UpdateWindow(hwnd);

	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

//随机数函数定制版,用于随机出现的方块
unsigned Random(int n);

//判断是否可以下落,可以则返回true
bool CanDown(POINT pt[]);

//下落实现
void Down(POINT pt[]);

//判断是否可以左移
bool CanLeft(POINT pt[]);

//实现左移
void Left(POINT pt[]);

//判断是否可以右移
bool CanRight(POINT pt[]);

//实现右移
void Right(POINT pt[]);

//判断是否可以变形
bool CanChange(POINT pt[]);

//实现变形
void Change(POINT pt[]);

//消行处理以及分数结算
void DelSqure(HWND);

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	POINT		TericsBorder[] = {-1, -1, 
		NUMCOLUMNBLOCKS * BLOCKWIDTH + 1, NUMLINEBLOCKS * BLOCKWIDTH + 1};
	HDC			hdc;
	PAINTSTRUCT	ps;
	TEXTMETRIC	tm;
	TCHAR		szNextTerics[] = TEXT("下一个:"),
				szSCore[] = TEXT("得分:");
	static TCHAR szBufferScore[5];
	static int	cxChar, cyChar;
	static POINT Block[4], NextBlock[4];
	int			x, y;
	static bool		pause = false;  //暂停

	switch(message)
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);

		GetTextMetrics(hdc, &tm);
		cxChar = tm.tmAveCharWidth * 2;
		cyChar = tm.tmExternalLeading + tm.tmHeight;
		SetTimer(hwnd, ID_TIMER, 600, NULL);
		//初始化第一个出现的方块
		cS = Random(4);
		cF = Random(BLOCKSTYLES);
		for(int i = 0; i < 4; ++i)
		{
			Block[i].x = Blocks[cF][cS].pt[i].x + 4;
			Block[i].y = Blocks[cF][cS].pt[i].y;
			GameClient[Block[i].x][Block[i].y] = true;
		}
		S = Random(4);
		F = Random(BLOCKSTYLES);
		for(int i = 0; i < 4; ++i)
		{
			NextBlock[i].x = Blocks[F][S].pt[i].x;
			NextBlock[i].y = Blocks[F][S].pt[i].y;
		}

		ReleaseDC(hwnd, hdc);
		return 0;

	case WM_TIMER:
		if(pause) return 0;
		if(CanDown(Block))
		{
			Down(Block);
		}
		//不能下移,需要处理消行判断(结合分数),还需要处理下一个显示,和当前显示的方块
		else
		{
			DelSqure(hwnd);
			for(int i = 0; i < 4; ++i)
			{
				Block[i].x = NextBlock[i].x + 4;
				Block[i].y = NextBlock[i].y;
				if(GameClient[Block[i].x][Block[i].y])
				{
					KillTimer(hwnd, ID_TIMER);
				}
				else
					GameClient[Block[i].x][Block[i].y] = true;
			}
			cS = S;  cF = F;
			S = Random(4);
			F = Random(BLOCKSTYLES);
			for(int i = 0; i < 4; ++i)
			{
				NextBlock[i].x = Blocks[F][S].pt[i].x;
				NextBlock[i].y = Blocks[F][S].pt[i].y;
			}
		}
		InvalidateRect(hwnd, NULL, TRUE);
		return 0;

	case WM_KEYDOWN:
		if(pause && wParam != VK_PAUSE) return 0;
		switch(wParam)
		{
		case VK_LEFT:
			if(CanLeft(Block))
				Left(Block);
			InvalidateRect(hwnd, NULL, TRUE);
			break;

		case VK_RIGHT:
			if(CanRight(Block))
				Right(Block);
			InvalidateRect(hwnd, NULL, TRUE);
			break;

		case VK_UP:
			if(CanChange(Block))
				Change(Block);
			InvalidateRect(hwnd, NULL, TRUE);
			break;

		case VK_DOWN:
			while(CanDown(Block))
				Down(Block);
			InvalidateRect(hwnd, NULL, TRUE);
			break;
		case VK_PAUSE:
			pause = !pause;
			break;

		default:
			break;
		}
		return 0;

	case WM_CHAR:
		if(wParam == 'p')
			pause = !pause;
		else if(wParam == 'r')
		{
			Score = 0;
			for(int x = 0; x < NUMCOLUMNBLOCKS; ++x)
			{
				for(int y = 0; y < NUMLINEBLOCKS; ++y)
					GameClient[x][y] = false;
			}
			cS = Random(4);
			cF = Random(BLOCKSTYLES);
			for(int i = 0; i < 4; ++i)
			{
				Block[i].x = Blocks[cF][cS].pt[i].x + 4;
				Block[i].y = Blocks[cF][cS].pt[i].y;
				GameClient[Block[i].x][Block[i].y] = true;
			}
			S = Random(4);
			F = Random(BLOCKSTYLES);
			for(int i = 0; i < 4; ++i)
			{
				NextBlock[i].x = Blocks[F][S].pt[i].x;
				NextBlock[i].y = Blocks[F][S].pt[i].y;
			}
			pause = false;
			InvalidateRect(hwnd, NULL, TRUE);
		}
		return 0;
		
	case WM_PAINT:
		//if(pause) return 0;
		hdc = BeginPaint(hwnd, &ps);
		SetViewportOrgEx(hdc, BLOCKWIDTH, BLOCKWIDTH, NULL);
		SelectObject(hdc, GetStockObject(WHITE_BRUSH));
		SelectObject(hdc, GetStockObject(BLACK_PEN));

		//画俄罗斯方块游戏的边框
		Rectangle(hdc, TericsBorder[0].x, TericsBorder[0].y,
			TericsBorder[1].x, TericsBorder[1].y);
		//输出“下一个”字符串
		TextOut(hdc, (NUMCOLUMNBLOCKS + 1) * BLOCKWIDTH, 0, szNextTerics, lstrlen(szNextTerics));

		//输出“得分”字符串
		TextOut(hdc, (NUMCOLUMNBLOCKS + 1) * BLOCKWIDTH, cyChar + 5 * BLOCKWIDTH,
			szSCore, lstrlen(szSCore));
		//
		SetTextAlign(hdc, TA_RIGHT | TA_TOP);
		TextOut(hdc, (NUMCOLUMNBLOCKS + 1) * BLOCKWIDTH + 3 * cxChar, 2 * cyChar + 5 * BLOCKWIDTH,
			szBufferScore, wsprintf(szBufferScore, TEXT("%d"), Score));
		SetTextAlign(hdc, TA_LEFT | TA_TOP);

		SelectObject(hdc, GetStockObject(BLACK_BRUSH));
		SelectObject(hdc, GetStockObject(WHITE_PEN));
		//显示游戏区的方块
		for(x = 0; x < NUMCOLUMNBLOCKS; ++x)
		{
			for(y = 0; y < NUMLINEBLOCKS; ++y)
			{
				if(GameClient[x][y])
				{
					Rectangle(hdc, x * BLOCKWIDTH, y * BLOCKWIDTH,
						(x + 1) * BLOCKWIDTH, (y + 1) * BLOCKWIDTH);
				}
			}
		}
		//显示下一个方块区域的方块
		for(int i = 0; i < 4; ++i)
		{
			Rectangle(hdc, (NextBlock[i].x + NUMCOLUMNBLOCKS + 2) * BLOCKWIDTH, NextBlock[i].y * BLOCKWIDTH + cyChar,
				(NextBlock[i].x + NUMCOLUMNBLOCKS + 3) * BLOCKWIDTH, (NextBlock[i].y + 1) * BLOCKWIDTH + cyChar);
		}

		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

//判断方块是否可以下落
bool CanDown(POINT pt[])
{
	bool result = true;
	//将方块所在格子先假设指定为无方块
	for(int i = 0; i < 4; ++i)
		GameClient[pt[i].x][pt[i].y] = false;
	for(int i = 0; i < 4; ++i)
	{
		//假如继续落下超过下底边界,返回false;或者假如该小方块下落一格已经有方块,结果为false
		if(pt[i].y + 1 == NUMLINEBLOCKS || GameClient[pt[i].x][pt[i].y + 1])
		{
			result = false;
			break;
		}
	}
	//恢复方块所在格子为有方块
	for(int i = 0; i < 4; ++i)
		GameClient[pt[i].x][pt[i].y] = true;
	return result;
}


//判断是否可以左移
bool CanLeft(POINT pt[])
{
	bool result = true;
	//将方块所在格子先假设指定为无方块
	for(int i = 0; i < 4; ++i)
		GameClient[pt[i].x][pt[i].y] = false;
	for(int i = 0; i < 4; ++i)
	{
		//假如继续左移超过左边边界,返回false;或者假如该小方块左移一格已经有方块,结果为false
		if(!pt[i].x || GameClient[pt[i].x - 1][pt[i].y])
		{
			result = false;
			break;
		}
	}
	//恢复方块所在格子为有方块
	for(int i = 0; i < 4; ++i)
		GameClient[pt[i].x][pt[i].y] = true;
	return result;
}

//判断是否可以右移
bool CanRight(POINT pt[])
{
	bool result = true;
	//将方块所在格子先假设指定为无方块
	for(int i = 0; i < 4; ++i)
		GameClient[pt[i].x][pt[i].y] = false;
	for(int i = 0; i < 4; ++i)
	{
		//假如继续左移超过左边边界,返回false;或者假如该小方块左移一格已经有方块,结果为false
		if(pt[i].x + 1 == NUMCOLUMNBLOCKS || GameClient[pt[i].x + 1][pt[i].y])
		{
			result = false;
			break;
		}
	}
	//恢复方块所在格子为有方块
	for(int i = 0; i < 4; ++i)
		GameClient[pt[i].x][pt[i].y] = true;
	return result;
}

//判断是否可以旋转
bool CanChange(POINT pt[])
{
	bool result = true;
	//将方块所在格子先假设指定为无方块
	for(int i = 0; i < 4; ++i)
		GameClient[pt[i].x][pt[i].y] = false;
	int t = (cS + 1) % 4;
	for(int k = 0; k < 4; ++k)
	{
		int x = Blocks[cF][t].pt[k].x - Blocks[cF][cS].pt[k].x,
			y = Blocks[cF][t].pt[k].y - Blocks[cF][cS].pt[k].y;
		if(GameClient[pt[k].x + x][pt[k].y + y] ||  //该方格已经有方块
			pt[k].x + x > NUMCOLUMNBLOCKS - 1 ||  //x坐标超越了右边界
			pt[k].x + x < 0 ||   //x坐标超越了左边界
			pt[k].y + y > NUMLINEBLOCKS - 1)  //y坐标超越了下底边界
		{
			result = false;
			break;
		}
	}

	//恢复方块所在格子为有方块
	for(int i = 0; i < 4; ++i)
		GameClient[pt[i].x][pt[i].y] = true;
	return result;
}

//实现旋转
void Change(POINT pt[])
{
	int t = (cS + 1) % 4;
	for(int i = 0; i < 4; ++i)
	{
		int x = Blocks[cF][t].pt[i].x - Blocks[cF][cS].pt[i].x,
			y = Blocks[cF][t].pt[i].y - Blocks[cF][cS].pt[i].y;
		GameClient[pt[i].x][pt[i].y] = false;
		pt[i].x += x;
		pt[i].y += y;
		GameClient[pt[i].x][pt[i].y] = true;
	}
	cS = t;
}

//实现右移
void Right(POINT pt[])
{
	for(int i = 0; i < 4; ++i)
	{
		GameClient[pt[i].x][pt[i].y] = false;
		++pt[i].x;
	}
	for(int k = 0; k < 4; ++k)
		GameClient[pt[k].x][pt[k].y] = true;
}

//实现左移
void Left(POINT pt[])
{
	for(int i = 0; i < 4; ++i)
	{
		GameClient[pt[i].x][pt[i].y] = false;
		--pt[i].x;
	}
	for(int k = 0; k < 4; ++k)
		GameClient[pt[k].x][pt[k].y] = true;
}

//实现方块的下落
void Down(POINT pt[])
{
	for(int i = 0; i < 4; ++i)
	{
		GameClient[pt[i].x][pt[i].y] = false;
		++pt[i].y;
	}
	for(int k = 0; k < 4; ++k)
		GameClient[pt[k].x][pt[k].y] = true;
}

//随机数函数定制版
inline unsigned Random(int n)
{
	SYSTEMTIME st;
	GetLocalTime(&st);
	srand(st.wMilliseconds);
	return rand() % n;
}

//消行处理以及分数结算
void DelSqure(HWND hwnd)
{
	int line = 0, temp;
	for(int x = NUMLINEBLOCKS - 1; x >= 0; --x)
	{
		bool result = true;
		for(int y = 0; y < NUMCOLUMNBLOCKS; ++y)
		{
			if(!GameClient[y][x])
			{
				result = false;
				break;
			}
		}
		//判断是否可以消行
		if(result)
		{
			temp = x;
			++line;
			while(x > 0)
			{
				for(int y = 0; y < NUMCOLUMNBLOCKS; ++y)
				{
					GameClient[y][x] = GameClient[y][x - 1];
				}
				--x;
			}
			for(int y = 0; y < NUMCOLUMNBLOCKS; ++y)
				GameClient[y][0] = false;
			x = temp + 1;
		}
	}
	if(line)
		Score += (line - 1) * 2 + 1;
	InvalidateRect(hwnd, NULL, TRUE);
}


C语言俄罗斯方块(简易版)_第1张图片

你可能感兴趣的:(vc++)