游戏成品概览
无UI,命令行形式,初学C++游戏制作以游戏逻辑的学习为主。
七种俄罗斯方块的设计
//创建资源
tetromino[0].append(L"..X.");
tetromino[0].append(L"..X.");
tetromino[0].append(L"..X.");
tetromino[0].append(L"..X.");
tetromino[1].append(L"..X.");
tetromino[1].append(L".XX.");
tetromino[1].append(L".X..");
tetromino[1].append(L"....");
tetromino[2].append(L".X..");
tetromino[2].append(L".XX.");
tetromino[2].append(L"..X.");
tetromino[2].append(L"....");
tetromino[3].append(L"....");
tetromino[3].append(L".XX.");
tetromino[3].append(L".XX.");
tetromino[3].append(L"....");
tetromino[4].append(L"..X.");
tetromino[4].append(L".XX.");
tetromino[4].append(L"..X.");
tetromino[4].append(L"....");
tetromino[5].append(L"....");
tetromino[5].append(L".XX.");
tetromino[5].append(L"..X.");
tetromino[5].append(L"..X.");
tetromino[6].append(L"....");
tetromino[6].append(L".XX.");
tetromino[6].append(L".X..");
tetromino[6].append(L".X..");
游戏碰撞检测机制
俄罗斯方块不能叠到一起,在距离相近时产生碰撞,不再下落
bool DoesPieceFit(int nTetromino, int nRotation, int nPosX, int nPosY)//碰撞检测机制
{
for (int px = 0; px < 4; px++)
for (int py = 0; py < 4; py++)
{
//获取块内索引
int pi = Rotate(px, py, nRotation);
//获取区域索引
int fi = (nPosY + py) * nFieldWidth + (nPosX + px);
if (nPosX + px >= 0 && nPosX + px < nFieldWidth)
{
if (nPosY + py >= 0 && nPosY + py < nFieldHeight)
{
if (tetromino[nTetromino][pi] == L'X' && pField[fi] != 0)
return false;//产生碰撞,检测不合格
}
}
}
}
游戏核心逻辑
旋转
int Rotate(int px, int py, int r)
{
switch (r % 4)//根据旋转进行坐标变换
{
case 0: return py * 4 + px; // 0度
case 1: return 12 + py - (px * 4); // 90度
case 2: return 15 - (py * 4) - px; // 180度
case 3: return 3 - py + (px * 4); // 270度
}
}
根据矩阵不同索引进行旋转
按键控制
//输入
for (int k = 0; k < 4; k++) //R L D Z
bKey[k] = (0x8000 & GetAsyncKeyState((unsigned char)("\x27\x25\x28\Z"[k]))) != 0;//按下这一系列按键bKey为真,其它则无响应为假(异步判断)
//游戏逻辑
nCurrentX += (bKey[0] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX + 1, nCurrentY)) ? 1 : 0;
nCurrentX -= (bKey[1] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX - 1, nCurrentY)) ? 1 : 0;
nCurrentY += (bKey[2] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY + 1)) ? 1 : 0;
游戏流程
强制下移
if (bForceDown)
{
if (DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY + 1))
nCurrentY++;//能下来
else
{
//锁住当前方块
for (int px = 0; px < 4; px++)
for (int py = 0; py < 4; py++)
if (tetromino[nCurrentPiece][Rotate(px, py, nCurrentRotation)] == L'X')
pField[(nCurrentY + py) * nFieldWidth + (nCurrentX + px)] = nCurrentPiece + 1;
nPieceCount++;
if (nPieceCount % 10 == 0)
if (nSpeed >= 10) nSpeed--;//随着游戏下落方块的增加,游戏难度不断上升
//检查一行是否满
for(int py=0;py<4;py++)
if (nCurrentY + py < nFieldHeight - 1)
{
bool bLine = true;
for (int px = 1; px < nFieldWidth - 1; px++)
bLine &= (pField[(nCurrentY + py) * nFieldWidth + px]) != 0;//这一行若是没有空隙,按位与运算为真
if (bLine)
{
//这一行消失,设置为 =
for (int px = 1; px < nFieldWidth - 1; px++)
pField[(nCurrentY + py) * nFieldWidth + px] = 8;
vLines.push_back(nCurrentY + py);//将改行存储到数组中
}
}
nScore += 25;
if (!vLines.empty()) nScore += (1 << vLines.size()) * 100;//给线条打分
//选择下一个块
nCurrentX = nFieldWidth / 2;
nCurrentY = 0;
nCurrentRotation = 0;
nCurrentPiece = rand() % 7;
//无法下落情况且方块形状产生碰撞检测,游戏结束
bGameOver = !DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY);
}
nSpeedCounter = 0;//每下落一次清零一次
}
游戏滴答(速度控制)
//处理时机
this_thread::sleep_for(50ms);//一个游戏“滴答”
nSpeedCounter ++;
bForceDown = (nSpeedCounter == nSpeed);//设置方块自由下落的速度(学到的一个有趣的逻辑)
分数获取
nScore += 25;
if (!vLines.empty()) nScore += (1 << vLines.size()) * 100;//给线条打分
共有两种逻辑的获取方式、
1:下落方块数量,每个加25分
2:根据消除线条数量给分(危险奖励机制)
线条消除
if (!vLines.empty())
{
//显示窗口
WriteConsoleOutputCharacter(hConsole, screen, nScreenHeight * nScreenWidth, { 0,0 }, &dwBytesWritten);
this_thread::sleep_for(400ms);//产生一些延迟,为了保证消除的顺利,游戏的流畅
for (auto& v : vLines)
for (int px = 1; px < nFieldWidth - 1; px++)
{
for (int py = v; py > 0; py--)
pField[py * nFieldWidth + px] = pField[(py - 1) * nFieldWidth + px];//把上一行的移到下一行
pField[px] = 0;
}
vLines.clear();//清除存储的行
}
至此,俄罗斯方块游戏的核心逻辑全部结束