移除旧的GameOver判定并统一顶部溢出失败逻辑
This commit is contained in:
@@ -49,7 +49,6 @@ void DropDown();
|
|||||||
void Fixing();
|
void Fixing();
|
||||||
void DeleteOneLine(int number);
|
void DeleteOneLine(int number);
|
||||||
void DeleteLines();
|
void DeleteLines();
|
||||||
bool GameOver();
|
|
||||||
void ComputeTarget();
|
void ComputeTarget();
|
||||||
void Restart();
|
void Restart();
|
||||||
|
|
||||||
|
|||||||
+12
-6
@@ -188,8 +188,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Fixing();
|
Fixing();
|
||||||
DeleteLines();
|
if (!gameOverFlag)
|
||||||
gameOverFlag = GameOver();
|
{
|
||||||
|
DeleteLines();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gameOverFlag)
|
if (!gameOverFlag)
|
||||||
@@ -255,8 +257,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Fixing();
|
Fixing();
|
||||||
DeleteLines();
|
if (!gameOverFlag)
|
||||||
gameOverFlag = GameOver();
|
{
|
||||||
|
DeleteLines();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case VK_UP:
|
case VK_UP:
|
||||||
@@ -266,8 +270,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
case VK_SPACE:
|
case VK_SPACE:
|
||||||
DropDown();
|
DropDown();
|
||||||
Fixing();
|
Fixing();
|
||||||
DeleteLines();
|
if (!gameOverFlag)
|
||||||
gameOverFlag = GameOver();
|
{
|
||||||
|
DeleteLines();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
+96
-46
@@ -69,6 +69,77 @@ COLORREF BrickColor[7] =
|
|||||||
RGB(197, 170, 255)
|
RGB(197, 170, 255)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算指定方块在指定旋转状态下的最小包围盒边界。
|
||||||
|
*
|
||||||
|
* 该函数会遍历 4x4 形状矩阵,找出所有非空单元的上下左右边界,
|
||||||
|
* 供后续统一计算生成位置和对齐方式时使用。
|
||||||
|
*
|
||||||
|
* @param brickType 方块类型编号。
|
||||||
|
* @param brickState 方块旋转状态编号。
|
||||||
|
* @param minRow 返回最上方非空行号。
|
||||||
|
* @param maxRow 返回最下方非空行号。
|
||||||
|
* @param minCol 返回最左侧非空列号。
|
||||||
|
* @param maxCol 返回最右侧非空列号。
|
||||||
|
*/
|
||||||
|
static void GetBrickBounds(int brickType, int brickState, int& minRow, int& maxRow, int& minCol, int& maxCol)
|
||||||
|
{
|
||||||
|
minRow = 4;
|
||||||
|
maxRow = -1;
|
||||||
|
minCol = 4;
|
||||||
|
maxCol = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
if (bricks[brickType][brickState][i][j] != 0)
|
||||||
|
{
|
||||||
|
if (i < minRow)
|
||||||
|
{
|
||||||
|
minRow = i;
|
||||||
|
}
|
||||||
|
if (i > maxRow)
|
||||||
|
{
|
||||||
|
maxRow = i;
|
||||||
|
}
|
||||||
|
if (j < minCol)
|
||||||
|
{
|
||||||
|
minCol = j;
|
||||||
|
}
|
||||||
|
if (j > maxCol)
|
||||||
|
{
|
||||||
|
maxCol = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算指定方块的统一生成位置。
|
||||||
|
*
|
||||||
|
* 该函数会根据方块在初始旋转状态下的最小包围盒,
|
||||||
|
* 自动把方块水平居中到游戏区附近,并将顶部非空行对齐到可视区域顶部。
|
||||||
|
* 这样不同形状的方块在生成时看起来会更加统一。
|
||||||
|
*
|
||||||
|
* @param brickType 方块类型编号。
|
||||||
|
* @return Point 计算得到的生成坐标。
|
||||||
|
*/
|
||||||
|
static Point GetSpawnPoint(int brickType)
|
||||||
|
{
|
||||||
|
int minRow, maxRow, minCol, maxCol;
|
||||||
|
GetBrickBounds(brickType, 0, minRow, maxRow, minCol, maxCol);
|
||||||
|
|
||||||
|
int brickWidth = maxCol - minCol + 1;
|
||||||
|
int brickHeight = maxRow - minRow + 1;
|
||||||
|
Point spawnPoint;
|
||||||
|
spawnPoint.x = (nGameWidth - brickWidth) / 2 - minCol;
|
||||||
|
spawnPoint.y = -brickHeight;
|
||||||
|
|
||||||
|
return spawnPoint;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 判断当前方块是否可以继续向下移动。
|
* @brief 判断当前方块是否可以继续向下移动。
|
||||||
*
|
*
|
||||||
@@ -98,7 +169,7 @@ bool CanMoveDown()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查下方是否有其他固定方块
|
// 检查下方是否有其他固定方块
|
||||||
if (workRegion[nextY][nextX] != 0)
|
if (nextY >= 0 && workRegion[nextY][nextX] != 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -138,7 +209,7 @@ bool CanMoveLeft()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查左侧是否有其他固定方块
|
// 检查左侧是否有其他固定方块
|
||||||
if (workRegion[nextY][nextX] != 0)
|
if (nextY >= 0 && workRegion[nextY][nextX] != 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -178,7 +249,7 @@ bool CanMoveRight()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查右侧是否有其他固定方块
|
// 检查右侧是否有其他固定方块
|
||||||
if (workRegion[nextY][nextX] != 0)
|
if (nextY >= 0 && workRegion[nextY][nextX] != 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -247,14 +318,14 @@ void Rotate()
|
|||||||
int checkX = point.x + j;
|
int checkX = point.x + j;
|
||||||
|
|
||||||
// 旋转后若越界,则恢复原状态
|
// 旋转后若越界,则恢复原状态
|
||||||
if (checkX < 0 || checkX >= nGameWidth || checkY < 0 || checkY >= nGameHeight)
|
if (checkX < 0 || checkX >= nGameWidth || checkY >= nGameHeight)
|
||||||
{
|
{
|
||||||
state = oldState;
|
state = oldState;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 旋转后若与固定方块重叠,则恢复原状态
|
// 旋转后若与固定方块重叠,则恢复原状态
|
||||||
if (workRegion[checkY][checkX] != 0)
|
if (checkY >= 0 && workRegion[checkY][checkX] != 0)
|
||||||
{
|
{
|
||||||
state = oldState;
|
state = oldState;
|
||||||
return;
|
return;
|
||||||
@@ -284,11 +355,15 @@ void DropDown()
|
|||||||
*
|
*
|
||||||
* 遍历当前方块 4x4 形状矩阵,把其中所有非空单元写入工作区数组,
|
* 遍历当前方块 4x4 形状矩阵,把其中所有非空单元写入工作区数组,
|
||||||
* 表示该方块已经落地并转为固定状态。
|
* 表示该方块已经落地并转为固定状态。
|
||||||
* 随后将“下一方块”切换为新的当前方块,重置旋转状态,
|
* 如果固定时仍有任意非空单元位于可视区域顶部之外,则判定游戏结束。
|
||||||
|
* 此时当前方块在可视区域内的部分仍会保留在工作区中。
|
||||||
|
* 若未超出顶部,再将“下一方块”切换为新的当前方块,重置旋转状态,
|
||||||
* 并把新方块生成到工作区上方的初始位置,同时刷新预测落点。
|
* 并把新方块生成到工作区上方的初始位置,同时刷新预测落点。
|
||||||
*/
|
*/
|
||||||
void Fixing()
|
void Fixing()
|
||||||
{
|
{
|
||||||
|
bool overflowTop = false;
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < 4; j++)
|
for (int j = 0; j < 4; j++)
|
||||||
@@ -298,7 +373,13 @@ void Fixing()
|
|||||||
int fixY = point.y + i;
|
int fixY = point.y + i;
|
||||||
int fixX = point.x + j;
|
int fixX = point.x + j;
|
||||||
|
|
||||||
// 将当前方块的非空单元写入工作区
|
// 只要当前方块任意非空单元仍超出顶部,就标记为结束
|
||||||
|
if (fixY < 0)
|
||||||
|
{
|
||||||
|
overflowTop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将当前方块在可视区域内的部分写入工作区
|
||||||
if (fixY >= 0 && fixY < nGameHeight && fixX >= 0 && fixX < nGameWidth)
|
if (fixY >= 0 && fixY < nGameHeight && fixX >= 0 && fixX < nGameWidth)
|
||||||
{
|
{
|
||||||
workRegion[fixY][fixX] = bricks[type][state][i][j];
|
workRegion[fixY][fixX] = bricks[type][state][i][j];
|
||||||
@@ -307,12 +388,17 @@ void Fixing()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (overflowTop)
|
||||||
|
{
|
||||||
|
gameOverFlag = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 生成下一个活动方块
|
// 生成下一个活动方块
|
||||||
type = nType;
|
type = nType;
|
||||||
nType = rand() % 7;
|
nType = rand() % 7;
|
||||||
state = 0;
|
state = 0;
|
||||||
point.x = 3;
|
point = GetSpawnPoint(type);
|
||||||
point.y = 0;
|
|
||||||
target = point;
|
target = point;
|
||||||
ComputeTarget();
|
ComputeTarget();
|
||||||
}
|
}
|
||||||
@@ -374,41 +460,6 @@ void DeleteLines()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 判断游戏是否结束。
|
|
||||||
*
|
|
||||||
* 当新的活动方块生成到初始位置后,如果它的任意一个非空单元
|
|
||||||
* 与工作区中已经固定的方块发生重叠,则说明顶部已被占满,
|
|
||||||
* 当前局面无法继续生成新方块,游戏结束。
|
|
||||||
*
|
|
||||||
* @return bool 若游戏结束返回 true,否则返回 false。
|
|
||||||
*/
|
|
||||||
bool GameOver()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
if (bricks[type][state][i][j] != 0)
|
|
||||||
{
|
|
||||||
int checkY = point.y + i;
|
|
||||||
int checkX = point.x + j;
|
|
||||||
|
|
||||||
// 检查新方块生成位置是否与固定方块重叠
|
|
||||||
if (checkY >= 0 && checkY < nGameHeight && checkX >= 0 && checkX < nGameWidth)
|
|
||||||
{
|
|
||||||
if (workRegion[checkY][checkX] != 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 计算当前活动方块的预测落点位置。
|
* @brief 计算当前活动方块的预测落点位置。
|
||||||
*
|
*
|
||||||
@@ -460,8 +511,7 @@ void Restart()
|
|||||||
type = rand() % 7;
|
type = rand() % 7;
|
||||||
nType = rand() % 7;
|
nType = rand() % 7;
|
||||||
state = 0;
|
state = 0;
|
||||||
point.x = 3;
|
point = GetSpawnPoint(type);
|
||||||
point.y = 0;
|
|
||||||
target = point;
|
target = point;
|
||||||
|
|
||||||
ComputeTarget();
|
ComputeTarget();
|
||||||
|
|||||||
+27
-24
@@ -217,7 +217,7 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 绘制预测落点
|
// 绘制预测落点
|
||||||
if (targetFlag)
|
if (targetFlag && !gameOverFlag)
|
||||||
{
|
{
|
||||||
HPEN targetPen = CreatePen(PS_DOT, SS(2), RGB(255, 240, 245));
|
HPEN targetPen = CreatePen(PS_DOT, SS(2), RGB(255, 240, 245));
|
||||||
oldPen = (HPEN)SelectObject(hdc, targetPen);
|
oldPen = (HPEN)SelectObject(hdc, targetPen);
|
||||||
@@ -252,34 +252,37 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 绘制当前活动方块
|
// 绘制当前活动方块
|
||||||
for (int i = 0; i < 4; i++)
|
if (!gameOverFlag)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < 4; j++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
if (bricks[type][state][i][j] != 0)
|
for (int j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
int drawY = point.y + i;
|
if (bricks[type][state][i][j] != 0)
|
||||||
int drawX = point.x + j;
|
|
||||||
|
|
||||||
if (drawY >= 0 && drawY < nGameHeight && drawX >= 0 && drawX < nGameWidth)
|
|
||||||
{
|
{
|
||||||
RECT brickRect =
|
int drawY = point.y + i;
|
||||||
{
|
int drawX = point.x + j;
|
||||||
gameRect.left + drawX * grid + SS(2),
|
|
||||||
gameRect.top + drawY * grid + SS(2),
|
|
||||||
gameRect.left + (drawX + 1) * grid - SS(2),
|
|
||||||
gameRect.top + (drawY + 1) * grid - SS(2)
|
|
||||||
};
|
|
||||||
|
|
||||||
HBRUSH brickBrush = CreateSolidBrush(BrickColor[type]);
|
if (drawY >= 0 && drawY < nGameHeight && drawX >= 0 && drawX < nGameWidth)
|
||||||
HPEN brickPen = CreatePen(PS_SOLID, 1, RGB(255, 250, 252));
|
{
|
||||||
oldPen = (HPEN)SelectObject(hdc, brickPen);
|
RECT brickRect =
|
||||||
oldBrush = (HBRUSH)SelectObject(hdc, brickBrush);
|
{
|
||||||
RoundRect(hdc, brickRect.left, brickRect.top, brickRect.right, brickRect.bottom, SS(12), SS(12));
|
gameRect.left + drawX * grid + SS(2),
|
||||||
SelectObject(hdc, oldBrush);
|
gameRect.top + drawY * grid + SS(2),
|
||||||
SelectObject(hdc, oldPen);
|
gameRect.left + (drawX + 1) * grid - SS(2),
|
||||||
DeleteObject(brickBrush);
|
gameRect.top + (drawY + 1) * grid - SS(2)
|
||||||
DeleteObject(brickPen);
|
};
|
||||||
|
|
||||||
|
HBRUSH brickBrush = CreateSolidBrush(BrickColor[type]);
|
||||||
|
HPEN brickPen = CreatePen(PS_SOLID, 1, RGB(255, 250, 252));
|
||||||
|
oldPen = (HPEN)SelectObject(hdc, brickPen);
|
||||||
|
oldBrush = (HBRUSH)SelectObject(hdc, brickBrush);
|
||||||
|
RoundRect(hdc, brickRect.left, brickRect.top, brickRect.right, brickRect.bottom, SS(12), SS(12));
|
||||||
|
SelectObject(hdc, oldBrush);
|
||||||
|
SelectObject(hdc, oldPen);
|
||||||
|
DeleteObject(brickBrush);
|
||||||
|
DeleteObject(brickPen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user