#include <SDL2/SDL.h>#include <SDL2/SDL_ttf.h>#include <iostream>#include <cstdlib>#include <ctime>#include <cstring>#include <string>// 全局常量const int BLOCK_SIZE = 30;const int WIDTH = 10;const int HEIGHT = 20;const int WINDOW_WIDTH = WIDTH * BLOCK_SIZE + 220;const int WINDOW_HEIGHT = HEIGHT * BLOCK_SIZE;const int PREVIEW_X = WIDTH * BLOCK_SIZE + 30;const int PREVIEW_Y = 80;const int INIT_DROP_INTERVAL = 500;const int MIN_DROP_INTERVAL = 100;const int DROP_SPEED_STEP = 50;const int SPEED_UP_LINES = 10;const int GOLD_PER_LINE = 100;const int LOOP_DELAY = 10;// 方块形状定义(7种×4旋转)const int shapes[7][4] = { {0x000F, 0x2222, 0x000F, 0x2222}, // I {0x0066, 0x0066, 0x0066, 0x0066}, // O {0x00E2, 0x2620, 0x00E2, 0x2620}, // T {0x0071, 0x2260, 0x0071, 0x2260}, // L {0x0074, 0x6220, 0x0074, 0x6220}, // J {0x006C, 0x2320, 0x006C, 0x2320}, // S {0x00C6, 0x3220, 0x00C6, 0x3220} // Z};// 皮肤结构体struct Skin { std::string name; int price; bool isOwned; SDL_Color color;} skins[3] = { {"默认皮肤", 0, true, {255, 255, 255, 255}}, {"方块皮肤", 300, false, {0, 255, 0, 255}}, {"钻石皮肤", 500, false, {0, 255, 255, 255}}};int currSkinIdx = 0; SDL_Color currSkinColor = skins[0].color;// SDL全局资源SDL_Window* window = nullptr;SDL_Renderer* renderer = nullptr;TTF_Font* font = nullptr;// 游戏数据struct GameData { int gameArea[HEIGHT][WIDTH] = {{0}}; int currShape; int currRot; int currX; int currY; int nextShape; int nextRot; int gold = 0; int lineTotal = 0; int dropInterval = INIT_DROP_INTERVAL;} gameData;// 函数声明bool initSDL();void closeSDL();void drawText(int x, int y, const std::string& text, SDL_Color color);void showMainMenuSDL();void showShopMenuSDL();void initGame();void createBlock();bool checkCollision(int x, int y, int rot, int shape);void drawGameSDL();void rotateBlock();void moveBlock(int dx, int dy);void clearLines();bool isGameOver();void startGameSDL();bool exchangeSkin(int skinIdx);void useSkin(int skinIdx);// -------------------------- 主函数 --------------------------int main() { srand(static_cast<unsigned int>(time(nullptr))); if (!initSDL()) { return 1; } showMainMenuSDL(); closeSDL(); return 0;}// -------------------------- 1. SDL初始化与释放(修复字体加载) --------------------------bool initSDL() { // 初始化SDL视频和字体 if (SDL_Init(SDL_INIT_VIDEO) < 0 || TTF_Init() == -1) { return false; } // 创建窗口 window = SDL_CreateWindow( "俄罗斯方块(图形版)", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN ); if (!window) { TTF_Quit(); SDL_Quit(); return false; } // 创建渲染器 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if (!renderer) { SDL_DestroyWindow(window); TTF_Quit(); SDL_Quit(); return false; } // 依次尝试常见系统字体路径(适配多设备) font = TTF_OpenFont("/system/fonts/Roboto-Regular.ttf", 18); if (!font) font = TTF_OpenFont("/system/fonts/DroidSans.ttf", 18); if (!font) font = TTF_OpenFont("/storage/emulated/0/ansystem/fonts/simhei.ttf", 18); font = TTF_OpenFont("/storage/emulated/0/ansystem/fonts/simhei.ttf", 18);if (!font) { SDL_Log("字体加载失败!错误原因:%s", TTF_GetError()); // 打印错误原因} else { SDL_Log("字体加载成功!");} if (!font) { // 字体加载失败则报错 SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); TTF_Quit(); SDL_Quit(); return false; } return true;}void closeSDL() { TTF_CloseFont(font); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); TTF_Quit(); SDL_Quit();}// -------------------------- 2. SDL文字绘制 --------------------------void drawText(int x, int y, const std::string& text, SDL_Color color) { SDL_Surface* textSurface = TTF_RenderUTF8_Solid(font, text.c_str(), color); if (!textSurface) return; SDL_Texture* textTexture = SDL_CreateTextureFromSurface(renderer, textSurface); if (!textTexture) { SDL_FreeSurface(textSurface); return; } SDL_Rect renderRect = {x, y, textSurface->w, textSurface->h}; SDL_RenderCopy(renderer, textTexture, nullptr, &renderRect); SDL_DestroyTexture(textTexture); SDL_FreeSurface(textSurface);}// -------------------------- 3. 主菜单(SDL图形化) --------------------------void showMainMenuSDL() { const std::string menuItems[3] = {"进入游戏", "皮肤商店", "退出游戏"}; int selectedIdx = 0; bool running = true; SDL_Event e; while (running) { // 事件处理 while (SDL_PollEvent(&e) != 0) { if (e.type == SDL_QUIT) running = false; else if (e.type == SDL_KEYDOWN) { switch (e.key.keysym.sym) { case SDLK_UP: selectedIdx = (selectedIdx - 1 + 3) % 3; break; case SDLK_DOWN: selectedIdx = (selectedIdx + 1) % 3; break; case SDLK_SPACE: if (selectedIdx == 0) { startGameSDL(); } else if (selectedIdx == 1) { showShopMenuSDL(); } else { running = false; } break; } } } // 绘制菜单 SDL_SetRenderDrawColor(renderer, 15, 15, 15, 255); SDL_RenderClear(renderer); drawText(WINDOW_WIDTH / 2 - 80, 60, "俄罗斯方块", {255, 200, 0, 255}); drawText(WINDOW_WIDTH / 2 - 100, 100, "操作:↑下选 | ↓下选 | 空格确认", {200, 200, 200, 255}); int menuY = 180; for (int i = 0; i < 3; i++) { SDL_Color textColor = (i == selectedIdx) ? SDL_Color{255, 0, 0, 255} : SDL_Color{255, 255, 255, 255}; drawText(WINDOW_WIDTH / 2 - 30, menuY + i * 60, menuItems[i], textColor); } std::string goldText = "当前金币:" + std::to_string(gameData.gold) + " 枚"; drawText(WINDOW_WIDTH / 2 - 60, menuY + 180, goldText, {0, 255, 255, 255}); SDL_RenderPresent(renderer); SDL_Delay(LOOP_DELAY); }}// -------------------------- 4. 皮肤商店(SDL图形化) --------------------------void showShopMenuSDL() { const std::string shopItems[3] = {"兑换皮肤", "使用皮肤", "返回主菜单"}; int selectedIdx = 0; bool running = true; SDL_Event e; while (running) { // 事件处理 while (SDL_PollEvent(&e) != 0) { if (e.type == SDL_QUIT) running = false; else if (e.type == SDL_KEYDOWN) { switch (e.key.keysym.sym) { case SDLK_UP: selectedIdx = (selectedIdx - 1 + 3) % 3; break; case SDLK_DOWN: selectedIdx = (selectedIdx + 1) % 3; break; case SDLK_SPACE: if (selectedIdx == 0) { // 兑换皮肤提示 drawText(WINDOW_WIDTH / 2 - 80, 300, "请按 1/2/3 选择兑换的皮肤", {255, 255, 0, 255}); SDL_RenderPresent(renderer); SDL_Delay(500); bool wait = true; while (wait) { if (SDL_PollEvent(&e) != 0 && e.type == SDL_KEYDOWN) { int skinChoice = e.key.keysym.sym - SDLK_1 + 1; std::string tip = (skinChoice >= 1 && skinChoice <= 3 && exchangeSkin(skinChoice - 1)) ? "兑换成功!" : "兑换失败(金币不足/已拥有)!"; drawText(WINDOW_WIDTH / 2 - 60, 350, tip, {0, 255, 0, 255}); SDL_RenderPresent(renderer); SDL_Delay(1000); wait = false; } } } else if (selectedIdx == 1) { // 使用皮肤提示 drawText(WINDOW_WIDTH / 2 - 80, 300, "请按 1/2/3 选择使用的皮肤", {255, 255, 0, 255}); SDL_RenderPresent(renderer); SDL_Delay(500); bool wait = true; while (wait) { if (SDL_PollEvent(&e) != 0 && e.type == SDL_KEYDOWN) { int skinChoice = e.key.keysym.sym - SDLK_1 + 1; std::string tip; if (skinChoice >= 1 && skinChoice <= 3 && skins[skinChoice - 1].isOwned) { useSkin(skinChoice - 1); tip = "皮肤已切换为:" + skins[skinChoice - 1].name; } else { tip = "无效选择(未拥有该皮肤)!"; } drawText(WINDOW_WIDTH / 2 - 100, 350, tip, {0, 255, 0, 255}); SDL_RenderPresent(renderer); SDL_Delay(1000); wait = false; } } } else { running = false; // 返回主菜单 } break; } } } // 绘制商店界面 SDL_SetRenderDrawColor(renderer, 15, 15, 15, 255); SDL_RenderClear(renderer); drawText(WINDOW_WIDTH / 2 - 60, 30, "皮肤商店", {255, 200, 0, 255}); std::string goldText = "当前金币:" + std::to_string(gameData.gold) + " 枚"; drawText(WINDOW_WIDTH / 2 - 60, 70, goldText, {0, 255, 255, 255}); // 皮肤列表表头 drawText(50, 120, "编号 | 名称 | 价格 | 状态 | 颜色预览", {200, 200, 200, 255}); drawText(50, 140, "----------------------------------------", {200, 200, 200, 255}); // 绘制皮肤信息 int skinY = 170; for (int i = 0; i < 3; i++) { drawText(50, skinY, std::to_string(i + 1), {255, 255, 255, 255}); drawText(90, skinY, skins[i].name, {255, 255, 255, 255}); drawText(180, skinY, std::to_string(skins[i].price) + " 金币", {255, 255, 255, 255}); drawText(280, skinY, skins[i].isOwned ? "已拥有" : "未拥有", skins[i].isOwned ? SDL_Color{0, 255, 0, 255} : SDL_Color{255, 0, 0, 255}); // 颜色预览方块 SDL_SetRenderDrawColor(renderer, skins[i].color.r, skins[i].color.g, skins[i].color.b, skins[i].color.a); SDL_Rect colorRect = {380, skinY - 5, 20, 20}; SDL_RenderFillRect(renderer, &colorRect); skinY += 40; } // 绘制商店操作选项 drawText(WINDOW_WIDTH / 2 - 100, 300, "================ 商店操作 ================", {255, 255, 255, 255}); drawText(WINDOW_WIDTH / 2 - 100, 330, "操作:↑上选 | ↓下选 | 空格确认", {200, 200, 200, 255}); int shopOptY = 370; for (int i = 0; i < 3; i++) { SDL_Color textColor = (i == selectedIdx) ? SDL_Color{255, 0, 0, 255} : SDL_Color{255, 255, 255, 255}; drawText(WINDOW_WIDTH / 2 - 40, shopOptY + i * 50, shopItems[i], textColor); } SDL_RenderPresent(renderer); SDL_Delay(LOOP_DELAY); }}// -------------------------- 5. 皮肤兑换与使用 --------------------------bool exchangeSkin(int skinIdx) { if (skins[skinIdx].isOwned || gameData.gold < skins[skinIdx].price) return false; gameData.gold -= skins[skinIdx].price; skins[skinIdx].isOwned = true; return true;}void useSkin(int skinIdx) { if (skinIdx >= 0 && skinIdx < 3 && skins[skinIdx].isOwned) { currSkinIdx = skinIdx; currSkinColor = skins[skinIdx].color; }}// -------------------------- 6. 游戏核心逻辑 --------------------------void initGame() { memset(gameData.gameArea, 0, sizeof(gameData.gameArea)); gameData.gold = 0; gameData.lineTotal = 0; gameData.dropInterval = INIT_DROP_INTERVAL; gameData.nextShape = rand() % 7; createBlock();}void createBlock() { gameData.currShape = gameData.nextShape; gameData.currRot = 0; gameData.currX = WIDTH / 2 - 2; gameData.currY = 0; gameData.nextShape = rand() % 7; gameData.nextRot = 0;}bool checkCollision(int x, int y, int rot, int shape) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (shapes[shape][rot] & (1 << (15 - (i * 4 + j)))) { int nx = x + j; int ny = y + i; if (nx < 0 || nx >= WIDTH || ny >= HEIGHT) return true; if (ny >= 0 && gameData.gameArea[ny][nx] != 0) return true; } } } return false;}void rotateBlock() { int newRot = (gameData.currRot + 1) % 4; if (!checkCollision(gameData.currX, gameData.currY, newRot, gameData.currShape)) { gameData.currRot = newRot; }}void moveBlock(int dx, int dy) { int newX = gameData.currX + dx; int newY = gameData.currY + dy; if (!checkCollision(newX, newY, gameData.currRot, gameData.currShape)) { gameData.currX = newX; gameData.currY = newY; } else if (dy == 1) { // 下落碰撞则固定方块 for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (shapes[gameData.currShape][gameData.currRot] & (1 << (15 - (i * 4 + j)))) { int ny = gameData.currY + i; int nx = gameData.currX + j; if (ny >= 0) gameData.gameArea[ny][nx] = 1; } } } clearLines(); createBlock(); }}void clearLines() { int lines = 0; for (int i = HEIGHT - 1; i >= 0; i--) { bool full = true; for (int j = 0; j < WIDTH; j++) { if (gameData.gameArea[i][j] == 0) { full = false; break; } } if (full) { lines++; for (int k = i; k > 0; k--) { memcpy(gameData.gameArea[k], gameData.gameArea[k - 1], sizeof(int) * WIDTH); } memset(gameData.gameArea[0], 0, sizeof(int) * WIDTH); i++; // 重新检查当前行 } } gameData.gold += lines * GOLD_PER_LINE; gameData.lineTotal += lines; // 加速逻辑 int level = gameData.lineTotal / SPEED_UP_LINES; gameData.dropInterval = INIT_DROP_INTERVAL - level * DROP_SPEED_STEP; if (gameData.dropInterval < MIN_DROP_INTERVAL) gameData.dropInterval = MIN_DROP_INTERVAL;}bool isGameOver() { return checkCollision(gameData.currX, gameData.currY, gameData.currRot, gameData.currShape);}void drawGameSDL() { // 清屏 SDL_SetRenderDrawColor(renderer, 15, 15, 15, 255); SDL_RenderClear(renderer); // 绘制游戏区边框 SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255); SDL_Rect gameRect = {10, 10, WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE}; SDL_RenderDrawRect(renderer, &gameRect); // 绘制已固定的方块 SDL_SetRenderDrawColor(renderer, currSkinColor.r, currSkinColor.g, currSkinColor.b, 200); for (int i = 0; i < HEIGHT; i++) { for (int j = 0; j < WIDTH; j++) { if (gameData.gameArea[i][j] == 1) { SDL_Rect blockRect = {10 + j * BLOCK_SIZE, 10 + i * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1}; SDL_RenderFillRect(renderer, &blockRect); } } } // 绘制当前下落的方块 SDL_SetRenderDrawColor(renderer, currSkinColor.r, currSkinColor.g, currSkinColor.b, 255); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (shapes[gameData.currShape][gameData.currRot] & (1 << (15 - (i * 4 + j)))) { int x = 10 + (gameData.currX + j) * BLOCK_SIZE; int y = 10 + (gameData.currY + i) * BLOCK_SIZE; SDL_Rect blockRect = {x, y, BLOCK_SIZE - 1, BLOCK_SIZE - 1}; SDL_RenderFillRect(renderer, &blockRect); } } } // 绘制下一个方块预览 drawText(PREVIEW_X, 30, "下一个方块", {200, 200, 200, 255}); SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255); SDL_Rect previewRect = {PREVIEW_X - 5, PREVIEW_Y - 5, 4 * BLOCK_SIZE + 10, 4 * BLOCK_SIZE + 10}; SDL_RenderDrawRect(renderer, &previewRect); SDL_SetRenderDrawColor(renderer, currSkinColor.r, currSkinColor.g, currSkinColor.b, 255); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (shapes[gameData.nextShape][gameData.nextRot] & (1 << (15 - (i * 4 + j)))) { int x = PREVIEW_X + j * BLOCK_SIZE; int y = PREVIEW_Y + i * BLOCK_SIZE; SDL_Rect blockRect = {x, y, BLOCK_SIZE - 1, BLOCK_SIZE - 1}; SDL_RenderFillRect(renderer, &blockRect); } } } // 绘制信息面板 drawText(PREVIEW_X, PREVIEW_Y + 5 * BLOCK_SIZE, "金币:" + std::to_string(gameData.gold), {0, 255, 255, 255}); drawText(PREVIEW_X, PREVIEW_Y + 6 * BLOCK_SIZE, "消行:" + std::to_string(gameData.lineTotal), {0, 255, 255, 255}); drawText(PREVIEW_X, PREVIEW_Y + 8 * BLOCK_SIZE, "操作说明:", {200, 200, 200, 255}); drawText(PREVIEW_X, PREVIEW_Y + 9 * BLOCK_SIZE, "W=旋转 A=左移 D=右移", {200, 200, 200, 255}); drawText(PREVIEW_X, PREVIEW_Y + 10 * BLOCK_SIZE, "S=加速下落 Q=返回菜单", {200, 200, 200, 255});}void startGameSDL() { initGame(); Uint32 lastDropTime = SDL_GetTicks(); bool backToMenu = false; SDL_Event e; while (!backToMenu) { // 事件处理 while (SDL_PollEvent(&e) != 0) { if (e.type == SDL_QUIT) backToMenu = true; else if (e.type == SDL_KEYDOWN) { switch (e.key.keysym.sym) { case SDLK_w: rotateBlock(); break; case SDLK_a: moveBlock(-1, 0); break; case SDLK_d: moveBlock(1, 0); break; case SDLK_s: moveBlock(0, 1); lastDropTime = SDL_GetTicks(); break; case SDLK_q: backToMenu = true; break; } } } // 自动下落 Uint32 currTime = SDL_GetTicks(); if (currTime - lastDropTime >= gameData.dropInterval) { moveBlock(0, 1); lastDropTime = currTime; } // 绘制与游戏结束处理 if (isGameOver()) { drawGameSDL(); drawText(WINDOW_WIDTH / 2 - 80, WINDOW_HEIGHT / 2, "游戏结束!", {255, 0, 0, 255}); drawText(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2 + 30, "最终金币:" + std::to_string(gameData.gold), {255, 255, 255, 255}); drawText(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2 + 60, "按Q返回菜单 | 其他键重启游戏", {255, 255, 255, 255}); SDL_RenderPresent(renderer); // 等待按键 bool waitForKey = true; while (waitForKey) { if (SDL_PollEvent(&e) != 0 && e.type == SDL_KEYDOWN) { if (e.key.keysym.sym == SDLK_q) backToMenu = true; else initGame(); waitForKey = false; } } } else { drawGameSDL(); SDL_RenderPresent(renderer); } SDL_Delay(LOOP_DELAY); }}