C++代码

0 点赞
地板是熔岩

#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);

}

}