编译参数设置:-std=c++14 -luser32 -lgdi32 -static-libgcc -static-libstdc++

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <vector>
#include <random>
#include <iostream>

// 手动实现clamp(显式处理LONG类型,兼容C++14)
template<typename T>
T clamp(T val, T min_val, T max_val) {
    if (val < min_val) return min_val;
    if (val > max_val) return max_val;
    return val;
}

// 重载clamp,专门处理LONG类型(解决类型推导问题)
LONG clamp(LONG val, int min_val, int max_val) {
    return clamp<LONG>(val, static_cast<LONG>(min_val), static_cast<LONG>(max_val));
}

// C++14 编译期常量
constexpr int WINDOW_WIDTH = 800;
constexpr int WINDOW_HEIGHT = 600;
constexpr int PADDLE_WIDTH = 100;
constexpr int PADDLE_HEIGHT = 20;
constexpr int BALL_RADIUS = 10;
constexpr int BRICK_WIDTH = 80;
constexpr int BRICK_HEIGHT = 30;
constexpr int BRICK_ROWS = 5;
constexpr int BRICK_COLS = 10;
constexpr int GAME_SPEED = 16; // 约60帧/秒 (1000/60≈16)

// 全局游戏状态
struct GameState {
    // 球拍(显式指定LONG类型,避免类型歧义)
    RECT paddle{0L, static_cast<LONG>(WINDOW_HEIGHT - 50), static_cast<LONG>(PADDLE_WIDTH), static_cast<LONG>(WINDOW_HEIGHT - 30)};
    // 小球(改为LONG类型,统一坐标精度)
    POINTL ball{static_cast<LONG>(WINDOW_WIDTH/2), static_cast<LONG>(WINDOW_HEIGHT/2)};
    POINT ballVel{4, -4};
    // 砖块
    struct Brick {
        RECT rect;
        bool alive = true;
        COLORREF color;
    };
    std::vector<Brick> bricks;
    // 游戏状态
    bool gameOver = false;
    bool gameWin = false;
} g_game;

// 随机数生成器(C++14 标准)
std::mt19937 rng(std::random_device{}());

// 初始化砖块
void InitBricks() {
    g_game.bricks.clear();
    std::uniform_int_distribution<int> colorDist(0, 255);
    
    for (int row = 0; row < BRICK_ROWS; ++row) {
        for (int col = 0; col < BRICK_COLS; ++col) {
            GameState::Brick brick;
            // 计算砖块位置(留2px间隙,显式转换为LONG)
            brick.rect.left = static_cast<LONG>(col * BRICK_WIDTH + 10);
            brick.rect.top = static_cast<LONG>(row * BRICK_HEIGHT + 50);
            brick.rect.right = static_cast<LONG>(brick.rect.left + BRICK_WIDTH - 2);
            brick.rect.bottom = static_cast<LONG>(brick.rect.top + BRICK_HEIGHT - 2);
            // 随机颜色
            brick.color = RGB(colorDist(rng), colorDist(rng), colorDist(rng));
            brick.alive = true;
            g_game.bricks.emplace_back(brick);
        }
    }
}

// 精准检测小球与矩形的碰撞(区分上下左右)
bool CheckBallRectCollision(const RECT& rect, const POINTL& ball, int radius, POINT& outVel) {
    // 小球中心坐标
    LONG ballX = ball.x;
    LONG ballY = ball.y;
    
    // 1. 先判断是否在矩形范围内(扩大检测范围,避免穿透)
    if (ballX + radius < rect.left || ballX - radius > rect.right ||
        ballY + radius < rect.top || ballY - radius > rect.bottom) {
        return false;
    }

    // 2. 计算小球与矩形的碰撞方向
    LONG distLeft = ballX - (rect.left - radius);
    LONG distRight = (rect.right + radius) - ballX;
    LONG distTop = ballY - (rect.top - radius);
    LONG distBottom = (rect.bottom + radius) - ballY;

    // 3. 找到最近的碰撞边(优先垂直/水平)
    bool collideTop = (distTop < distBottom) && (distTop < distLeft) && (distTop < distRight);
    bool collideBottom = (distBottom < distTop) && (distBottom < distLeft) && (distBottom < distRight);
    bool collideLeft = (distLeft < distRight) && (distLeft < distTop) && (distLeft < distBottom);
    bool collideRight = (distRight < distLeft) && (distRight < distTop) && (distRight < distBottom);

    // 4. 根据碰撞边修正速度
    if (collideTop || collideBottom) {
        outVel.y = -outVel.y; // 垂直反弹(关键:球拍碰撞主要是顶部)
    }
    if (collideLeft || collideRight) {
        outVel.x = -outVel.x; // 水平反弹
    }

    // 5. 修正小球位置,避免卡在矩形内
    if (collideTop) {
        const_cast<POINTL&>(ball).y = rect.top - radius;
    } else if (collideBottom) {
        const_cast<POINTL&>(ball).y = rect.bottom + radius;
    }
    if (collideLeft) {
        const_cast<POINTL&>(ball).x = rect.left - radius;
    } else if (collideRight) {
        const_cast<POINTL&>(ball).x = rect.right + radius;
    }

    return true;
}

// 更新游戏逻辑
void UpdateGame() {
    if (g_game.gameOver || g_game.gameWin) return;

    // 1. 球拍控制(键盘左右箭头)
    if (GetAsyncKeyState(VK_LEFT) & 0x8000) {
        LONG newLeft = g_game.paddle.left - 8L;
        g_game.paddle.left = clamp(newLeft, 0, WINDOW_WIDTH - PADDLE_WIDTH);
        g_game.paddle.right = g_game.paddle.left + static_cast<LONG>(PADDLE_WIDTH);
    }
    if (GetAsyncKeyState(VK_RIGHT) & 0x8000) {
        LONG newLeft = g_game.paddle.left + 8L;
        g_game.paddle.left = clamp(newLeft, 0, WINDOW_WIDTH - PADDLE_WIDTH);
        g_game.paddle.right = g_game.paddle.left + static_cast<LONG>(PADDLE_WIDTH);
    }

    // 2. 小球移动(统一LONG类型,避免精度丢失)
    g_game.ball.x += static_cast<LONG>(g_game.ballVel.x);
    g_game.ball.y += static_cast<LONG>(g_game.ballVel.y);

    // 3. 小球边界碰撞检测
    // 左边界
    if (g_game.ball.x - BALL_RADIUS < 0) {
        g_game.ballVel.x = abs(g_game.ballVel.x);
        g_game.ball.x = BALL_RADIUS; // 防止卡边界
    }
    // 右边界
    if (g_game.ball.x + BALL_RADIUS > WINDOW_WIDTH) {
        g_game.ballVel.x = -abs(g_game.ballVel.x);
        g_game.ball.x = WINDOW_WIDTH - BALL_RADIUS;
    }
    // 上边界
    if (g_game.ball.y - BALL_RADIUS < 0) {
        g_game.ballVel.y = abs(g_game.ballVel.y);
        g_game.ball.y = BALL_RADIUS;
    }
    // 下边界(游戏结束)
    if (g_game.ball.y + BALL_RADIUS > WINDOW_HEIGHT) {
        g_game.gameOver = true;
        MessageBoxA(nullptr, "游戏结束!小球掉落边界", "打砖块", MB_OK | MB_ICONINFORMATION);
        return;
    }

    // 4. 小球与球拍碰撞(精准检测,必反弹)
    POINT tempVel = g_game.ballVel; // 临时速度,用于碰撞计算
    if (CheckBallRectCollision(g_game.paddle, g_game.ball, BALL_RADIUS, tempVel)) {
        // 优化球拍碰撞的水平速度(根据碰撞位置调整,可选)
        LONG paddleCenter = g_game.paddle.left + PADDLE_WIDTH / 2;
        LONG offset = g_game.ball.x - paddleCenter;
        tempVel.x = static_cast<int>(offset / (PADDLE_WIDTH / 2.0f) * 5); // 浮点计算,更平滑
        g_game.ballVel = tempVel; // 应用修正后的速度
    }

    // 5. 小球与砖块碰撞
    for (auto& brick : g_game.bricks) {
        if (!brick.alive) continue;
        if (CheckBallRectCollision(brick.rect, g_game.ball, BALL_RADIUS, g_game.ballVel)) {
            brick.alive = false;
            break; // 一次只碰撞一个砖块
        }
    }

    // 6. 检查是否所有砖块被消除(胜利)
    bool allDestroyed = true;
    for (const auto& brick : g_game.bricks) {
        if (brick.alive) {
            allDestroyed = false;
            break;
        }
    }
    if (allDestroyed) {
        g_game.gameWin = true;
        MessageBoxA(nullptr, "恭喜!所有砖块已消除,游戏胜利!", "打砖块", MB_OK | MB_ICONINFORMATION);
    }
}

// 绘制游戏画面
void DrawGame(HDC hdc) {
    // 1. 清空背景(黑色)
    RECT clientRect{0L, 0L, static_cast<LONG>(WINDOW_WIDTH), static_cast<LONG>(WINDOW_HEIGHT)};
    HBRUSH hBlackBrush = CreateSolidBrush(RGB(0, 0, 0));
    FillRect(hdc, &clientRect, hBlackBrush);
    DeleteObject(hBlackBrush);

    if (g_game.gameOver || g_game.gameWin) return;

    // 2. 绘制球拍(绿色)
    HBRUSH hPaddleBrush = CreateSolidBrush(RGB(0, 255, 0));
    FillRect(hdc, &g_game.paddle, hPaddleBrush);
    DeleteObject(hPaddleBrush);

    // 3. 绘制小球(红色)
    HBRUSH hBallBrush = CreateSolidBrush(RGB(255, 0, 0));
    // 转换为int绘制(GDI函数兼容int)
    int ballX = static_cast<int>(g_game.ball.x);
    int ballY = static_cast<int>(g_game.ball.y);
    Ellipse(hdc, 
            ballX - BALL_RADIUS, ballY - BALL_RADIUS,
            ballX + BALL_RADIUS, ballY + BALL_RADIUS);
    DeleteObject(hBallBrush);

    // 4. 绘制砖块
    for (const auto& brick : g_game.bricks) {
        if (!brick.alive) continue;
        HBRUSH hBrickBrush = CreateSolidBrush(brick.color);
        FillRect(hdc, &brick.rect, hBrickBrush);
        // 绘制砖块边框(白色)
        HPEN hWhitePen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
        HPEN hOldPen = (HPEN)SelectObject(hdc, hWhitePen);
        Rectangle(hdc, brick.rect.left, brick.rect.top, brick.rect.right, brick.rect.bottom);
        SelectObject(hdc, hOldPen);
        DeleteObject(hWhitePen);
        DeleteObject(hBrickBrush);
    }
}

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
            InitBricks();
            SetTimer(hwnd, 1, GAME_SPEED, nullptr);
            break;
        
        case WM_TIMER:
            UpdateGame();
            InvalidateRect(hwnd, nullptr, FALSE);
            break;
        
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            DrawGame(hdc);
            EndPaint(hwnd, &ps);
            break;
        }

        case WM_KEYDOWN:
            if (wParam == VK_ESCAPE) {
                DestroyWindow(hwnd);
            }
            break;

        case WM_DESTROY:
            KillTimer(hwnd, 1);
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProcA(hwnd, msg, wParam, lParam);
    }
    return 0;
}

// 程序入口
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) {
    const char CLASS_NAME[] = "BrickBreakerClass";
    WNDCLASSA wc = {0};
    wc.lpfnWndProc   = WndProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.hCursor       = LoadCursorA(nullptr, IDC_ARROW);

    if (!RegisterClassA(&wc)) {
        MessageBoxA(nullptr, "窗口类注册失败!", "错误", MB_OK | MB_ICONERROR);
        return 1;
    }

    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = GetSystemMetrics(SM_CYSCREEN);
    int windowX = (screenWidth - WINDOW_WIDTH) / 2;
    int windowY = (screenHeight - WINDOW_HEIGHT) / 2;

    HWND hwnd = CreateWindowExA(
        0,
        CLASS_NAME,
        "打砖块游戏 (Windows API/C++14)",
        WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX,
        windowX, windowY, WINDOW_WIDTH, WINDOW_HEIGHT,
        nullptr, nullptr, hInstance, nullptr
    );

    if (!hwnd) {
        MessageBoxA(nullptr, "窗口创建失败!", "错误", MB_OK | MB_ICONERROR);
        return 1;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg = {0};
    while (GetMessageA(&msg, nullptr, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }

    return 0;
}

0 comments

No comments so far...