win32 俄罗斯方块

效果

在这里插入图片描述

实现细节

所有的数据都存放在地图数组中,种类有三种:

class Tetris
{
private:
	// 背景数组 0:无方块 1:在下落的方块 2:底部固定的方块
	char background[20][10];
}

地图坐标系:
在这里插入图片描述
7种方块:

class Tetris
{
private:
	// 方块数组
	char sqare[2][4];
	// 标记产生随机方块的种类
	int sqareId = 0;
	// 记录判断能否旋转的点的位置x
	int line = 0;
	// 记录判断能否旋转的点的位置y
	int list = 0;
}

// 创建一个随机的方块
int Tetris::createRandSqare()
{
	sqareId = rand() % 7;           // 总共七种方块类型
	switch (sqareId)
	{
	case 0:
		// ■ ■
		//   ■ ■
		sqare[0][0] = 1, sqare[0][1] = 1, sqare[0][2] = 0, sqare[0][3] = 0;
		sqare[1][0] = 0, sqare[1][1] = 1, sqare[1][2] = 1, sqare[1][3] = 0;
		line = 0;
		list = 3;
		break;
	case 1:
		//   ■ ■
		// ■ ■
		sqare[0][0] = 0, sqare[0][1] = 1, sqare[0][2] = 1, sqare[0][3] = 0;
		sqare[1][0] = 1, sqare[1][1] = 1, sqare[1][2] = 0, sqare[1][3] = 0;
		line = 0;
		list = 3;
		break;
	case 2:
		//     ■
		// ■ ■ ■
		sqare[0][0] = 0, sqare[0][1] = 0, sqare[0][2] = 1, sqare[0][3] = 0;
		sqare[1][0] = 1, sqare[1][1] = 1, sqare[1][2] = 1, sqare[1][3] = 0;
		line = 0;
		list = 3;
		break;
	case 3:
		// ■
		// ■ ■ ■
		sqare[0][0] = 1, sqare[0][1] = 0, sqare[0][2] = 0, sqare[0][3] = 0;
		sqare[1][0] = 1, sqare[1][1] = 1, sqare[1][2] = 1, sqare[1][3] = 0;
		line = 0;
		list = 3;
		break;
	case 4:
		//   ■
		// ■ ■ ■
		sqare[0][0] = 0, sqare[0][1] = 1, sqare[0][2] = 0, sqare[0][3] = 0;
		sqare[1][0] = 1, sqare[1][1] = 1, sqare[1][2] = 1, sqare[1][3] = 0;
		line = 0;
		list = 3;
		break;
	case 5:
		// ■ ■
		// ■ ■
		sqare[0][0] = 1, sqare[0][1] = 1, sqare[0][2] = 0, sqare[0][3] = 0;
		sqare[1][0] = 1, sqare[1][1] = 1, sqare[1][2] = 0, sqare[1][3] = 0;
		line = 0;
		list = 4;
		break;
	case 6:
		// ■ ■ ■ ■
		sqare[0][0] = 1, sqare[0][1] = 1, sqare[0][2] = 1, sqare[0][3] = 1;
		sqare[1][0] = 0, sqare[1][1] = 0, sqare[1][2] = 0, sqare[1][3] = 0;
		line = 0;
		list = 4;
		break;
	}
	return sqareId;
}

判断能否移动,碰到地图边缘不能移动,碰到已经固定的方块不能移动:

// 判断方块是否能下落(落到最后一行就会停住)
bool Tetris::canSqareDown()
{
	for (int i = 0; i < 10; ++i)
	{
		if (1 == background[19][i])
		{
			return false;
		}
	}
	return true;
}

// 判断方块是否能下落(遇到已经固定的方块也会停住)
bool Tetris::canSqareDown2()
{
	for (int i = 19; i >= 0; --i)
	{
		for (int j = 0; j < 10; ++j)
		{
			if (1 == background[i][j])
			{
				if (2 == background[i + 1][j])
				{
					return false;
				}
			}
		}
	}
	return true;
}

bool Tetris::canSqareLeft();
bool Tetris::canSqareLeft2();
bool Tetris::canSqareRight();
bool Tetris::canSqareRight2();

判断是否能旋转:

// 判断方块是否能旋转
bool Tetris::canSqarechangeSqare()
{
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			if (2 == background[line + i][list + j])
			{
				return false;
			}
		}
	}
	if (list - 1 < 0)
	{
		list = 0;	// 重置判断点
	}
	else if (list + 2 > 9)
	{
		list = 7;
	}
	return true;
}

// 判断直线是否能旋转
bool Tetris::canLineSqareChange()
{
	// 太靠上或者太靠下都不让旋转
	if (line >= 17 || line <= 1)
	{
		return false;
	}

	int i = 0,					// 判断点与左右静止方块或边界的距离
		j = 0;
	for (i = 1; i < 4; ++i)		// 获取判断点与右静止方块或边界的距离
	{
		if (2 == background[line][list + i] || list + i > 9)
		{
			break;
		}
	}
	for (j = 1; j < 4; ++j)		// 获取判断点与左静止方块或边界的距离
	{
		if (2 == background[line][list - j] || list - j < 0)
		{
			break;
		}
	}
	if ((i - 1 + j - 1) < 3)	// 判断是否有空间旋转
	{
		return false;
	}
	return true;
}

方块移动和旋转:

// 方块下落
void Tetris::sqareDown()
{
	for (int i = 19; i >= 0; --i)
	{
		for (int j = 0; j < 10; ++j)
		{
			if (1 == background[i][j])
			{
				background[i + 1][j] = background[i][j];
				background[i][j] = 0;
			}
		}
	}
}

void Tetris::sqareLeft();
void Tetris::sqareRight();

// 旋转方块
void Tetris::changeSqare()
{
	char arrSqare[3][3] = { {0} };	// 创建一个3*3的方块数组

	// 把背景里的方块放进3*3的方块数组中
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			arrSqare[i][j] = background[line + i][list + j];
		}
	}

	// 把3*3的方块数组旋转之后放进背景
	int temp = 2;
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			background[line + i][list + j] = arrSqare[temp][i];
			--temp;
		}
		temp = 2;
	}
}

// 旋转直线
void Tetris::changeLineSqare()
{
	if (1 == background[line][list - 1])	// 横着
	{
		background[line][list - 1] = 0;
		background[line][list + 1] = 0;
		background[line][list + 2] = 0;
		if (2 == background[list + 1][list])
		{
			background[line - 1][list] = 0;
			background[line - 2][list] = 0;
			background[line - 3][list] = 0;
		}
		else if (2 == background[line + 2][list])
		{
			background[line + 1][list] = 0;
			background[line - 1][list] = 0;
			background[line - 2][list] = 0;
		}
		else
		{
			background[line - 1][list] = 1;
			background[line + 1][list] = 1;
			background[line + 2][list] = 1;
		}
	}
	else	// 竖着
	{
		background[line - 1][list] = 0;
		background[line + 1][list] = 0;
		background[line + 2][list] = 0;
		if (2 == background[line][list + 1] || 9 == list)
		{
			background[line][list - 1] = 1;
			background[line][list - 2] = 1;
			background[line][list - 3] = 1;
			list -= 2;
		}
		else if (2 == background[line][list + 2] || 8 == list)
		{
			background[line][list + 1] = 1;
			background[line][list - 1] = 1;
			background[line][list - 2] = 1;
			--list;
		}
		else if (2 == background[line][list - 1] || 0 == list)
		{
			background[line][list + 1] = 1;
			background[line][list + 2] = 1;
			background[line][list + 3] = 1;
			++list;

		}
		else
		{
			background[line][list - 1] = 1;
			background[line][list + 1] = 1;
			background[line][list + 2] = 1;
		}
	}
}

消除方块:

void Tetris::destoryOneLine()
{
	int nsum = 0;           // 一行的和
	for (int i = 19; i >= 0; --i)
	{
		for (int j = 0; j < 10; ++j)
		{
			nsum += background[i][j];
		}
		if (20 == nsum)
		{
			for (int j = i - 1; j >= 0; --j)
			{
				for (int k = 0; k < 10; ++k)
				{
					background[j + 1][k] = background[j][k];
				}
			}
			score += 10;
			i = 20;         // 再次从底部遍历
		}
		nsum = 0;
	}
}

实现思路

  1. 按回车键开始游戏 创建一个定时器

    LRESULT CALLBACK CallBack(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
    {
    	。。。
        switch (nMsg)
        {
        	case VK_RETURN:	control.onReturn(hWnd);	break;	// 回车
            。。。
        }
        。。。
    }
    
    void Control::onReturn(HWND hWnd)
    {
    	SetTimer(hWnd, DEF_TIME_ID, 500, NULL);
    	gameStart = true;
    }
    
  2. 每隔0.5s重绘界面,用户有输入则移动方块,如果方块能下落则下落,否则固定方块,判断得分以及gameover,重新生成方块。

    LRESULT CALLBACK CallBack(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
    {
    	。。。
        switch (nMsg)
        {
        	case VK_RETURN:	control.onReturn(hWnd);	break;	// 回车
            case VK_LEFT:	control.onLeft(hWnd);	break;	// 左键
            case VK_RIGHT:	control.onRight(hWnd);	break;	// 右键
            case VK_DOWN:	control.onDown(hWnd);	break;	// 下键
            case VK_UP:		control.onUp(hWnd);		break;	// 上键
        }
        。。。
    }
    
    void Control::onTimer(HWND hWnd)
    {
    	HDC hdc = GetDC(hWnd);
    	if (true == tetris.canSqareDown() && true == tetris.canSqareDown2())
    	{
    		tetris.sqareDown();
    		tetris.addLine();
    	}
    	else
    	{
    		tetris.change1To2();
    		tetris.destoryOneLine();
    		if (true == tetris.ifGameOver(hWnd))
    		{
    			onClose(hWnd);
    			Init();
    			return;
    		}
    		tetris.createRandSqare();
    		tetris.copySqareToBack();
    	}
    	onPrint(hdc);
    	ReleaseDC(hWnd, hdc);
    }
    

链接

百度云链接:https://pan.baidu.com/s/1SZDI5GhOcxDRrHZxnMec2g
提取码:p5mg

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值