使用 C89 标准和 Windows API 编写的完整 GUI 俄罗斯方块游戏
#include <windows.h>
#include <stdio.h>
#include <time.h>
#define BOARD_WIDTH 10
#define BOARD_HEIGHT 20
#define BLOCK_SIZE 25
#define WINDOW_WIDTH (BOARD_WIDTH * BLOCK_SIZE + 200)
#define WINDOW_HEIGHT (BOARD_HEIGHT * BLOCK_SIZE + 40)
// 方块形状定义
const int SHAPES[7][4][4] = {
{{0,0,0,0}, {1,1,1,1}, {0,0,0,0}, {0,0,0,0}}, // I
{{0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0}}, // O
{{0,0,0,0}, {0,1,1,1}, {0,1,0,0}, {0,0,0,0}}, // L
{{0,0,0,0}, {0,1,1,1}, {0,0,0,1}, {0,0,0,0}}, // J
{{0,0,0,0}, {0,1,1,1}, {0,0,1,0}, {0,0,0,0}}, // T
{{0,0,0,0}, {0,1,1,0}, {0,0,1,1}, {0,0,0,0}}, // S
{{0,0,0,0}, {0,0,1,1}, {0,1,1,0}, {0,0,0,0}} // Z
};
// 颜色定义
const COLORREF COLORS[8] = {
RGB(0, 0, 0), // 黑色 - 背景
RGB(0, 255, 255), // 青色 - I
RGB(255, 255, 0), // 黄色 - O
RGB(255, 165, 0), // 橙色 - L
RGB(0, 0, 255), // 蓝色 - J
RGB(128, 0, 128), // 紫色 - T
RGB(0, 255, 0), // 绿色 - S
RGB(255, 0, 0) // 红色 - Z
};
// 游戏状态
int board[BOARD_HEIGHT][BOARD_WIDTH];
int currentPiece[4][4];
int nextPiece[4][4];
int currentX, currentY;
int currentType, nextType;
int score;
int level;
int linesCleared;
BOOL gameOver;
BOOL isPaused;
// 函数声明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void InitializeGame(void);
void NewPiece(void);
void RotatePiece(void);
int CheckCollision(void);
void LockPiece(void);
void ClearLines(void);
void DrawGame(HDC hdc);
void DrawBlock(HDC hdc, int x, int y, int colorIndex);
void DrawNextPiece(HDC hdc);
void DrawScore(HDC hdc);
void GameLoop(HWND hwnd);
void ResetGame(void);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
const char CLASS_NAME[] = "TetrisClass";
WNDCLASS wc = {0};
HWND hwnd;
MSG msg;
// 注册窗口类
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClass(&wc)) {
MessageBox(NULL, "窗口注册失败!", "错误", MB_ICONERROR);
return 0;
}
// 创建窗口
hwnd = CreateWindowEx(
0,
CLASS_NAME,
"俄罗斯方块 - C89 Windows API",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT,
WINDOW_WIDTH, WINDOW_HEIGHT,
NULL, NULL, hInstance, NULL
);
if (hwnd == NULL) {
MessageBox(NULL, "窗口创建失败!", "错误", MB_ICONERROR);
return 0;
}
// 初始化游戏
InitializeGame();
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 设置定时器用于游戏循环
SetTimer(hwnd, 1, 500, NULL);
// 消息循环
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
KillTimer(hwnd, 1);
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
switch (uMsg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
DrawGame(hdc);
EndPaint(hwnd, &ps);
break;
case WM_TIMER:
if (!gameOver && !isPaused) {
GameLoop(hwnd);
}
break;
case WM_KEYDOWN:
if (!gameOver && !isPaused) {
switch (wParam) {
case VK_LEFT:
currentX--;
if (CheckCollision()) currentX++;
InvalidateRect(hwnd, NULL, TRUE);
break;
case VK_RIGHT:
currentX++;
if (CheckCollision()) currentX--;
InvalidateRect(hwnd, NULL, TRUE);
break;
case VK_DOWN:
currentY++;
if (CheckCollision()) {
currentY--;
LockPiece();
ClearLines();
NewPiece();
if (CheckCollision()) {
gameOver = TRUE;
}
}
InvalidateRect(hwnd, NULL, TRUE);
break;
case VK_UP:
RotatePiece();
InvalidateRect(hwnd, NULL, TRUE);
break;
case VK_SPACE: // 硬降到底部
while (!CheckCollision()) {
currentY++;
}
currentY--;
LockPiece();
ClearLines();
NewPiece();
if (CheckCollision()) {
gameOver = TRUE;
}
InvalidateRect(hwnd, NULL, TRUE);
break;
}
}
// 游戏控制键
switch (wParam) {
case 'P': case 'p':
isPaused = !isPaused;
InvalidateRect(hwnd, NULL, TRUE);
break;
case 'R': case 'r':
ResetGame();
InvalidateRect(hwnd, NULL, TRUE);
break;
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
void InitializeGame(void) {
int i, j;
// 初始化随机数种子
srand((unsigned)time(NULL));
// 清空游戏板
for (i = 0; i < BOARD_HEIGHT; i++) {
for (j = 0; j < BOARD_WIDTH; j++) {
board[i][j] = 0;
}
}
// 初始化游戏状态
score = 0;
level = 1;
linesCleared = 0;
gameOver = FALSE;
isPaused = FALSE;
// 生成第一个和下一个方块
currentType = rand() % 7;
nextType = rand() % 7;
NewPiece();
}
void NewPiece(void) {
int i, j;
// 复制当前类型
currentType = nextType;
nextType = rand() % 7;
// 复制方块形状
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
currentPiece[i][j] = SHAPES[currentType][i][j];
nextPiece[i][j] = SHAPES[nextType][i][j];
}
}
// 设置初始位置
currentX = BOARD_WIDTH / 2 - 2;
currentY = 0;
// 检查游戏是否结束
if (CheckCollision()) {
gameOver = TRUE;
}
}
void RotatePiece(void) {
int temp[4][4];
int i, j;
// 创建旋转后的临时方块
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
temp[j][3 - i] = currentPiece[i][j];
}
}
// 复制回当前方块
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
currentPiece[i][j] = temp[i][j];
}
}
// 检查旋转后是否碰撞,如果碰撞则恢复
if (CheckCollision()) {
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
temp[3 - j][i] = currentPiece[i][j];
}
}
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
currentPiece[i][j] = temp[i][j];
}
}
}
}
int CheckCollision(void) {
int i, j;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (currentPiece[i][j]) {
int boardX = currentX + j;
int boardY = currentY + i;
if (boardX < 0 || boardX >= BOARD_WIDTH ||
boardY >= BOARD_HEIGHT ||
(boardY >= 0 && board[boardY][boardX])) {
return 1;
}
}
}
}
return 0;
}
void LockPiece(void) {
int i, j;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (currentPiece[i][j]) {
int boardX = currentX + j;
int boardY = currentY + i;
if (boardY >= 0) {
board[boardY][boardX] = currentType + 1;
}
}
}
}
}
void ClearLines(void) {
int i, j, k;
int linesToClear = 0;
for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
int lineFull = 1;
for (j = 0; j < BOARD_WIDTH; j++) {
if (!board[i][j]) {
lineFull = 0;
break;
}
}
if (lineFull) {
linesToClear++;
// 移动上面的行下来
for (k = i; k > 0; k--) {
for (j = 0; j < BOARD_WIDTH; j++) {
board[k][j] = board[k - 1][j];
}
}
// 清空顶行
for (j = 0; j < BOARD_WIDTH; j++) {
board[0][j] = 0;
}
i++; // 重新检查当前行
}
}
// 计算得分
if (linesToClear > 0) {
linesCleared += linesToClear;
switch (linesToClear) {
case 1: score += 100; break;
case 2: score += 300; break;
case 3: score += 500; break;
case 4: score += 800; break;
}
level = linesCleared / 10 + 1;
}
}
void GameLoop(HWND hwnd) {
currentY++;
if (CheckCollision()) {
currentY--;
LockPiece();
ClearLines();
NewPiece();
}
InvalidateRect(hwnd, NULL, TRUE);
}
void ResetGame(void) {
InitializeGame();
}
void DrawGame(HDC hdc) {
int i, j;
RECT rect;
// 绘制背景
GetClientRect(WindowFromDC(hdc), &rect);
FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));
// 绘制游戏板边框
rect.left = 0;
rect.top = 0;
rect.right = BOARD_WIDTH * BLOCK_SIZE;
rect.bottom = BOARD_HEIGHT * BLOCK_SIZE;
FrameRect(hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
// 绘制已固定的方块
for (i = 0; i < BOARD_HEIGHT; i++) {
for (j = 0; j < BOARD_WIDTH; j++) {
if (board[i][j] > 0) {
DrawBlock(hdc, j, i, board[i][j]);
}
}
}
// 绘制当前方块
if (!gameOver) {
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (currentPiece[i][j]) {
int x = currentX + j;
int y = currentY + i;
if (y >= 0) {
DrawBlock(hdc, x, y, currentType + 1);
}
}
}
}
}
// 绘制下一个方块预览
DrawNextPiece(hdc);
// 绘制分数和信息
DrawScore(hdc);
// 绘制游戏状态
SetBkMode(hdc, TRANSPARENT);
if (gameOver) {
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 150, "游戏结束!", 8);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 170, "按R重新开始", 11);
} else if (isPaused) {
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 150, "游戏暂停", 8);
}
}
void DrawBlock(HDC hdc, int x, int y, int colorIndex) {
HBRUSH brush = CreateSolidBrush(COLORS[colorIndex]);
HBRUSH oldBrush = SelectObject(hdc, brush);
HPEN pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
HPEN oldPen = SelectObject(hdc, pen);
Rectangle(hdc,
x * BLOCK_SIZE,
y * BLOCK_SIZE,
(x + 1) * BLOCK_SIZE,
(y + 1) * BLOCK_SIZE);
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(brush);
DeleteObject(pen);
}
void DrawNextPiece(HDC hdc) {
int i, j;
RECT rect;
// 绘制下一个方块区域
rect.left = BOARD_WIDTH * BLOCK_SIZE + 20;
rect.top = 20;
rect.right = rect.left + 100;
rect.bottom = rect.top + 100;
FrameRect(hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 5, "下一个方块:", 10);
// 绘制下一个方块
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (nextPiece[i][j]) {
int x = (BOARD_WIDTH * BLOCK_SIZE + 45) + j * BLOCK_SIZE;
int y = 45 + i * BLOCK_SIZE;
HBRUSH brush = CreateSolidBrush(COLORS[nextType + 1]);
HBRUSH oldBrush = SelectObject(hdc, brush);
HPEN pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
HPEN oldPen = SelectObject(hdc, pen);
Rectangle(hdc, x, y, x + BLOCK_SIZE, y + BLOCK_SIZE);
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(brush);
DeleteObject(pen);
}
}
}
}
void DrawScore(HDC hdc) {
char buffer[50];
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 200, "分数:", 4);
sprintf(buffer, "%d", score);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 70, 200, buffer, strlen(buffer));
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 220, "等级:", 4);
sprintf(buffer, "%d", level);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 70, 220, buffer, strlen(buffer));
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 240, "消除行数:", 8);
sprintf(buffer, "%d", linesCleared);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 90, 240, buffer, strlen(buffer));
// 绘制控制说明
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 280, "控制:", 4);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 300, "←→: 移动", 8);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 320, "↑: 旋转", 6);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 340, "↓: 加速下落", 10);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 360, "空格: 硬降", 8);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 380, "P: 暂停", 6);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 400, "R: 重新开始", 10);
TextOut(hdc, BOARD_WIDTH * BLOCK_SIZE + 20, 420, "ESC: 退出", 8);
}
----------------------------------------------------------------------------------
cmd编译命令: gcc tetris_winapi.c -o tetris.exe -lgdi32 -luser32
----------------------------------------------------------------------------------
运行界面:

更多推荐



所有评论(0)