C++桌面图标游戏系列之二【俄罗斯方块】

最近写了一个C++桌面图标游戏系列之一【贪吃蛇】。

https://blog.csdn.net/Yang9325/article/details/111031297

没想到还挺受欢迎,那么系列之二【俄罗斯方块】就顺理成章的出现了。

先看看视频:

C++桌面图标玩游戏系列之二【俄罗斯方块】

俄罗斯方块本身的逻辑我直接用这位兄弟的代码了,就是里面的变量名和一些定义实在是一言难尽,有不少冗余的地方可以优化,最重要的是很少注释,属于自己看的懂,别人看的头疼的那种。

类似游戏的宽高,第几行第几列这种都是直接用数字写死的 。

为了方便自己和大家查看,很多地方我改了变量名字和定义,加上了注释,例如map[21][12] => map[MAP_ROW][MAP_COL]。

当然这里没有diss的意思,这兄弟应该只是打算写给自己看的,下面是他的文章链接。

https://blog.csdn.net/qq_42846735/article/details/100046164

我在这里的基础上,将逻辑同步到桌面图标。

以下是控制图标的代码:

发送消息,改变图标的位置

::SendMessage(hwndSysListView32, LVM_SETITEMPOSITION, i, MAKELPARAM(-100, -100)); 

查找图标层的句柄

HWND  hwndParent = ::FindWindow(“Progman”, “Program Manager”);
HWND  hwndSHELLDLL_DefView = ::FindWindowEx(hwndParent, NULL, “SHELLDLL_DefView”, NULL);
 hwndSysListView32 = ::FindWindowEx(hwndSHELLDLL_DefView, NULL, “SysListView32”, “FolderView”);

贴出核心代码。

enum eMap
{
	MAP_COL = 12,
	MAP_ROW = 21,
	GRID_COL = MAP_COL - 2,//除去左右2列墙壁
	GRID_ROW = MAP_ROW - 1,//除去最下方一行墙壁
};

HWND  hwndSysListView32;
void initialWindow(HANDLE hOut);//初始化窗口
void initialPrint(HANDLE hOut);//初始化界面
void gotoXY(HANDLE hOut, int x, int y);//移动光标
void randomBlock(HANDLE hOut, int block[4][4]);//随机生成方块并打印到下一个方块位置
bool collisionDetection(int block[4][4], int map[MAP_ROW][MAP_COL], int x, int y);//检测碰撞
void printBlock(HANDLE hOut, int block[4][4], int x, int y);//打印方块
void clearBlock(HANDLE hOut, int block[4][4], int x, int y);//消除方块
void myLeft(HANDLE hOut, int block[4][4], int map[MAP_ROW][MAP_COL], int x, int& y);//左移
void myRight(HANDLE hOut, int block[4][4], int map[MAP_ROW][MAP_COL], int x, int& y);//右移
void myUp(HANDLE hOut, int block[4][4], int map[MAP_ROW][MAP_COL], int x, int& y);//顺时针旋转90度
int myDown(HANDLE hOut, int block[4][4], int map[MAP_ROW][MAP_COL], int& x, int y);//加速下落
void myStop(HANDLE hOut, int block[4][4]);//游戏暂停
void gameOver(HANDLE hOut, int block[4][4], int map[MAP_ROW][MAP_COL]);//游戏结束
void eliminateRow(HANDLE hOut, int map[MAP_ROW][MAP_COL], int& val, int& fraction, int& checkpoint);//判断是否能消行并更新分值

 WIN_ICON ///
//已经被使用的图标索引
list<int> listUsedIcons;
list<int> listFreeIcons;

int nGridWidth = 40;//格子的宽度
int nLeftGap = 500;//左边的空白

int iconMap[MAP_ROW][MAP_COL];//网格对应图标索引

//查找桌面图标所在的界面句柄,win7不需要调用,win10可能需要。
void FindDTWindow();



int main()
{
	int map[MAP_ROW][MAP_COL];//地图阵列,包含墙壁 
	int waitingBlock[4][4];//候选区的方块
	int curMovingBlock[4][4];//下落中的方块
	int positionX, positionY;//方块左上角的坐标  注:positionX:在map里行数的索引 positionY:在map里的列数的索引
	bool isNewBlockCanMoveDown;//检查方块还能不能下落
	char key;//用来存储按键
	int downSpeed;//用来控制下落速度
	int myPoint;//用来存储得分
	int checkpoint;//用来存储关卡
	int times;
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出设备句柄
	initialWindow(hOut);

	HWND  hwndParent = ::FindWindow("Progman", "Program Manager");
	HWND  hwndSHELLDLL_DefView = ::FindWindowEx(hwndParent, NULL, "SHELLDLL_DefView", NULL);
	hwndSysListView32 = ::FindWindowEx(hwndSHELLDLL_DefView, NULL, "SysListView32", "FolderView");

	if (hwndSysListView32 == NULL)
	{
		//***注意,某些情况柄有空的,尝试用下面的函数来查找句柄***
		//***也可以自行使用vs的工具spy++工具来查看层级是否准确*****
		FindDTWindow();
		if (hwndSysListView32 == NULL)
			return 0;
	}

	int iconCount = ListView_GetItemCount(hwndSysListView32);
	if (iconCount < 1)
	{
		return 0;
	}

	if (iconCount < MAP_COL * MAP_ROW)
	{
		cout << "\n!!!!!!!!!!!!图标不够用,修改行列数量或者复制粘贴多整点图标,现在有:" << iconCount << " 需要:" << MAP_COL * MAP_ROW;
		return 0;
	}

	int nCurRow = 0, nCurCol = 0;
	//这里会将所有桌面图标放到接们看不见的位置,后面可以右键桌面设置对齐网格还原图标位置
	for (int i = 0; i < iconCount; i++)
	{
		::SendMessage(hwndSysListView32, LVM_SETITEMPOSITION, i, MAKELPARAM(-100, -100));
		//同步填充图标网格
		iconMap[nCurRow][nCurCol++] = i;
		if (nCurCol >= MAP_COL)
		{
			nCurCol = 0;
			++nCurRow;
		}

	}



initial:
	gotoXY(hOut, 0, 0);
	initialPrint(hOut);
	isNewBlockCanMoveDown = true;
	downSpeed = 50;
	myPoint = 0;
	checkpoint = 1;
	times = downSpeed;

	//中间位置空出来
	for (int i = 0; i < MAP_ROW - 1; ++i)
	{
		for (int j = 1; j < MAP_COL - 1; ++j)
		{
			map[i][j] = 0;
		}
	}

	//左右两边的墙壁
	for (int i = 0; i < MAP_ROW; ++i)
	{
		map[i][0] = map[i][MAP_COL - 1] = 1;

		int index = iconMap[i][0];
		::SendMessage(hwndSysListView32, LVM_SETITEMPOSITION, index, MAKELPARAM(nLeftGap, i * nGridWidth));

		index = iconMap[i][MAP_COL - 1];
		::SendMessage(hwndSysListView32, LVM_SETITEMPOSITION, index, MAKELPARAM((MAP_COL - 1) * nGridWidth + nLeftGap, i * nGridWidth));
	}
	//底边墙壁
	for (int i = 0; i < MAP_COL; ++i)
	{
		map[MAP_ROW - 1][i] = 1;

		int index = iconMap[MAP_ROW - 1][i];
		::SendMessage(hwndSysListView32, LVM_SETITEMPOSITION, index, MAKELPARAM((i * nGridWidth) + nLeftGap, (MAP_ROW - 1) * nGridWidth));
	}


	//设置随机数种子
	srand((unsigned)time(NULL));
	//随机出第一个方块
	randomBlock(hOut, waitingBlock);
	while (true)
	{
		if (isNewBlockCanMoveDown)
		{
			eliminateRow(hOut, map, downSpeed, myPoint, checkpoint);
			isNewBlockCanMoveDown = false;
			positionX = -3;
			positionY = (int)(MAP_COL / 2.0f);
			if (collisionDetection(waitingBlock, map, positionX, positionY))
			{
				for (int i = 0; i < 4; ++i)
				{
					for (int j = 0; j < 4; ++j)
					{
						curMovingBlock[i][j] = waitingBlock[i][j];
					}
				}
				randomBlock(hOut, waitingBlock);
			}
			else
			{
				gameOver(hOut, waitingBlock, map);
				goto initial;
			}
		}
		printBlock(hOut, curMovingBlock, positionX, positionY);
		if (_kbhit())
		{
			//按键操作
			key = _getch();
			switch (key)
			{
			case 72:
				myUp(hOut, curMovingBlock, map, positionX, positionY);
				break;
			case 75:
				myLeft(hOut, curMovingBlock, map, positionX, positionY);
				break;
			case 77:
				myRight(hOut, curMovingBlock, map, positionX, positionY);
				break;
			case 80:
				switch (myDown(hOut, curMovingBlock, map, positionX, positionY))
				{
				case 0:
					isNewBlockCanMoveDown = false;
					break;
				case 1:
					isNewBlockCanMoveDown = true;
					break;
				case 2:
					gameOver(hOut, curMovingBlock, map);
					goto initial;
				default:
					break;
				}
				break;
			case 32:
				myStop(hOut, waitingBlock);
				break;
			case 27:
				exit(0);
			default:
				break;
			}
		}
		Sleep(20);
		if (0 == --times)
		{
			//定时下落
			switch (myDown(hOut, curMovingBlock, map, positionX, positionY))
			{
			case 0:
				isNewBlockCanMoveDown = false;
				break;
			case 1:
				isNewBlockCanMoveDown = true;
				break;
			case 2:
				gameOver(hOut, curMovingBlock, map);
				goto initial;
			default:
				break;
			}
			times = downSpeed;
		}
	}
	cin.get();
	return 0;
}

如果你想隐藏控制台,网上方法挺多的,或者评论问我也可以。

如果你愿意看到这或者点收藏,证明我写的东西对你有点用,那么顺手点个赞吧,赠人玫瑰手留余香,先行谢过。

有需要的话,下面是完整的代码文件,用vs创建控制台程序再复制到主文件就行。需要花点小钱(1.9元),毕竟要恰饭。

https://download.csdn.net/download/Yang9325/13724660

    原文作者:猫叔大鸭梨
    原文地址: https://blog.csdn.net/Yang9325/article/details/111355836
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞