最近写了一个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元),毕竟要恰饭。