- 分享
GUI打砖块(AI生成)
- @ 2025-11-28 19:49:44
编译参数设置:-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...