qt小游戏——坦克大作战

游戏概述

这是一款基于Qt框架的坦克对战游戏,玩家操控绿色坦克,在17×17的网格战场上与红色敌方坦克作战,目标是通关10个难度递增的关
卡。

🎛️ 操作方式

键盘控制

  • ↑ 方向键:向上移动/转向
  • ↓ 方向键:向下移动/转向
  • ← 方向键:向左移动/转向
  • → 方向键:向右移动/转向
  • 空格键:发射子弹
  • R键:游戏结束后重新开始

操作特点

  • 即时转向:方向键即时改变坦克朝向
  • 持续移动:按住方向键坦克会持续移动
  • 稳定射击:空格键有15帧冷却时间,防止连射

🗺️游戏界面

战场布局

┌─────────────────────────────────────┐

│ 第 3 / 10 关 🟢 │ ← 关卡进度

│ 「崭露头角」 🟢 │ ← 关卡名称

│ │

│ 🟤🟤🟤 🟢🔴🔴🟢 │ ← 游戏区域

│ 🟤🟡🟤 🟢🔴🟢🔴🟢 🟢🟢🟢 │

│ 🟤🟤🟤 🟢🔴🔴🟢 │

│ │

│ 分数: 1200 生命: 3 │ ← 状态信息

│ 第 3 / 10 关 🟢 │ ← 关卡进度

└─────────────────────────────────────┘


🗺️ 游戏元素

元素 颜色 说明
玩家坦克 🟢 绿色 生命值100,3条命
敌方坦克 🔴 红色 每关数量递增,速度递增
玩家基地 🟡 黄色 被敌人击中则游戏失败
砖块 🟤 棕色 可被子弹摧毁
钢铁墙 ⚪ 银色 不可摧毁
水域 🔵 蓝色 坦克无法通过
子弹 🟡 黄色 移动速度快

🎯 游戏规则

胜利条件

  1. 关卡胜利
  • 主要目标:消灭当前关卡所有敌方坦克
  • 胜利奖励:+1000分 × 关卡数
  • 后续选择:
    • ✅ 继续:进入下一关
    • ❌ 结束:保存分数,游戏胜利
  1. 最终通关
  • 通关条件:完成所有10个关卡
  • 特殊奖励:显示🏆 游戏通关!
  • 最终得分:所有关卡得分总和

失败条件

  1. 基地被摧毁
  • 触发条件:敌方子弹击中玩家基地
  • 失败结果:游戏结束,可重新开始
  • 玩家子弹:可以安全摧毁自己的基地(无惩罚)
  1. 生命值耗尽
  • 初始生命:3条命
  • 伤害机制:
    • 被敌方子弹击中:-25生命值
    • 生命值归零:失去一条命
  • 重生机制:在基地位置复活,生命值恢复100
  • 生命耗尽:游戏结束

📊 关卡系统

关卡配置表

关卡 名称 敌人 速度 射击 特色
1 初出茅庐 5 1 新手入门
2 小试牛刀 7 1 练习走位
3 崭露头角 9 2 敌人开始射击
4 初露锋芒 11 2 实力展现
5 游刃有余 13 2 技巧熟练
6 炉火纯青 15 2 高手阶段
7 登峰造极 17 3 挑战极限
8 举世无双 19 3 接近最强
9 傲视群雄 21 3 接近通关
10 一代宗师 23 4 最终挑战

难度递增机制

  1. 敌人数量:每关+2个敌人
  2. 敌人速度:每3关+1点速度
  3. 敌人智能:第3关开始能射击
  4. 障碍物:随关卡增加而增多

⚔️ 游戏机制

移动机制

  • 网格系统:所有元素对齐32像素网格
  • 移动速度:玩家移动冷却3帧,敌人按关卡配置
  • 碰撞检测:
    • 坦克不能穿过障碍物、水域、钢铁墙
    • 子弹可以摧毁砖块
    • 坦克之间不能重叠

射击机制

  • 玩家子弹:速度4格/步,冷却15帧
  • 敌人子弹:速度4格/步,冷却30帧
  • 伤害值:所有子弹造成25点伤害
  • 弹道:直线飞行,碰到障碍物消失

AI行为

  • 移动模式:随机移动,每20步改变方向
  • 射击模式:每50步有几率射击(第3关开始)
  • 目标选择:无特定目标,随机射击

🎮 游戏策略

基础策略

  1. 位置控制:保持在战场中央,避免被困角落
  2. 移动射击:边移动边射击,增加生存率
  3. 障碍物利用:利用砖块阻挡敌人子弹
  4. 基地保护:优先消灭接近基地的敌人

进阶策略

  1. 诱敌深入:将敌人引到有利位置
  2. 区域控制:控制关键通道和位置
  3. 时机把握:在敌人射击间隙行动
  4. 资源管理:合理使用子弹和移动

关卡策略

  • 第1-2关:练习基本操作,熟悉控制
  • 第3-5关:学习躲避敌人子弹
  • 第6-8关:提高反应速度和射击精度
  • 第9-10关:全力以赴,使用所有技巧

📈 分数系统

得分方式

  • 消灭敌人:+100分/个
  • 关卡完成:+1000分 × 关卡数
  • 连击奖励:无连击系统
  • 通关奖励:无额外奖励,分数累积

分数用途

  • 进度展示:显示当前游戏进度
  • 成就感:激励玩家挑战更高分数
  • 比较基准:与历史最高分比较

🔄 游戏流程

单局游戏流程

  1. 初始化 → 生成关卡、敌人、障碍物
  2. 游戏进行 → 玩家操作、AI响应、碰撞检测
  3. 关卡完成 → 消灭所有敌人,显示询问对话框
  4. 继续/结束 → 玩家选择继续下一关或结束游戏
  5. 游戏结束 → 基地被摧毁或生命耗尽
  6. 重新开始 → 按R键重新开始游戏

完整通关流程

  1. 第1关 → 熟悉操作
  2. 第2关 → 练习走位
  3. 第3-5关 → 学习应对敌人射击
  4. 第6-8关 → 提高技巧
  5. 第9关 → 接近最终挑战
  6. 第10关 → 一代宗师,完成通关

💡 游戏技巧

新手技巧

  1. 先移动后射击:确保安全再射击
  2. 利用障碍物:砖块是你的朋友
  3. 保持移动:静止的坦克更容易被击中
  4. 观察敌人:注意敌人的移动模式

进阶技巧

  1. 预判射击:提前瞄准敌人移动方向
  2. 诱敌战术:引诱敌人到有利位置
  3. 节奏控制:掌握移动和射击的节奏
  4. 心理战术:通过走位影响AI行为

🎯 游戏目标

短期目标

  • 完成当前关卡
  • 获得更高分数
  • 解锁下一关

长期目标

  • 通关所有10个关卡
  • 获得最高分数
  • 成为"一代宗师"

终极挑战

  • 在最高难度下生存
  • 挑战完美通关(无伤)
  • 创造最高分数记录

🎮 游戏特色总结

  1. 经典玩法:类似经典坦克大战
  2. 关卡设计:10个难度递增的关卡
  3. 中文界面:完全中文化的UI和提示
  4. 智能AI:会移动和射击的敌人
  5. 策略性强:需要技巧和规划
  6. 成就感强:清晰的进度和奖励系统

这款游戏融合了经典玩法和现代设计,适合各个年龄段的玩家,既有挑战性又有成就感!


源码展示

pro文件

cpp 复制代码
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++14

TARGET = tank_battle
TEMPLATE = app

SOURCES += \
    main.cpp \
    tankwidget.cpp

HEADERS += \
    tankwidget.h \
    tank.h \
    bullet.h \
    obstacle.h

头文件

bullet.h

cpp 复制代码
#ifndef BULLET_H
#define BULLET_H

#include <QRect>

struct Bullet
{
    int x; // 网格坐标
    int y; // 网格坐标
    int direction; // 0: 上, 1: 右, 2: 下, 3: 左
    int speed; // 每步移动的格子数
    bool active;
    bool isPlayerBullet;
    int damage;

    Bullet(int x = 0, int y = 0, int direction = 0, bool isPlayerBullet = true)
        : x(x), y(y), direction(direction), speed(4), active(true),
          isPlayerBullet(isPlayerBullet), damage(25) {}

    QRect rect() const {
        return QRect(x * 32 + 12, y * 32 + 12, 8, 8); // Centered in cell
    }
};

#endif // BULLET_H

obstacle.h

cpp 复制代码
#ifndef OBSTACLE_H
#define OBSTACLE_H

#include <QRect>
#include <QColor>

enum ObstacleType {
    BRICK = 0,
    STEEL = 1,
    WATER = 2,
    TREE = 3,
    BASE = 4
};

struct Obstacle
{
    int x;
    int y;
    ObstacleType type;
    int durability;

    Obstacle(int x = 0, int y = 0, ObstacleType type = BRICK, int durability = 1)
        : x(x), y(y), type(type), durability(durability) {}

    QRect rect() const {
        return QRect(x * 32, y * 32, 32, 32);
    }

    QColor color() const {
        switch (type) {
            case BRICK: return QColor(139, 69, 19); // Brown
            case STEEL: return QColor(192, 192, 192); // Silver
            case WATER: return QColor(0, 0, 255); // Blue
            case TREE: return QColor(34, 139, 34); // Green
            case BASE: return QColor(255, 255, 0); // Yellow
            default: return QColor(128, 128, 128); // Gray
        }
    }
};

#endif // OBSTACLE_H

tank.h

cpp 复制代码
#ifndef TANK_H
#define TANK_H

#include <QColor>
#include <QRect>

struct Tank
{
    int x;
    int y;
    int direction; // 0: 上, 1: 右, 2: 下, 3: 左
    int health;
    int speed;
    int shootCooldown;
    int moveCooldown;
    int moveSpeed; // 每次移动的格子数
    QColor color;
    bool isPlayer;
    int lives;

    Tank(int x = 0, int y = 0, int direction = 0, bool isPlayer = false)
        : x(x), y(y), direction(direction), health(100), speed(1),
          shootCooldown(0), moveCooldown(0), moveSpeed(1), isPlayer(isPlayer), lives(3)
    {
        if (isPlayer) {
            color = QColor(0, 255, 0); // 玩家绿色
            speed = 2; // 玩家移动更快
        } else {
            color = QColor(255, 0, 0); // 敌人红色
            speed = 1; // 敌人移动较慢
        }
    }

    QRect rect() const {
        return QRect(x * 32, y * 32, 32, 32);
    }

    int getSpeed() const {
        return speed;
    }
};

#endif // TANK_H

tankwidget.h

cpp 复制代码
#ifndef TANKWIDGET_H
#define TANKWIDGET_H

#include <QWidget>
#include <QTimer>
#include <QKeyEvent>
#include <QPainter>
#include <QVector>
#include "tank.h"
#include "bullet.h"
#include "obstacle.h"

class TankWidget : public QWidget
{
    Q_OBJECT

public:
    explicit TankWidget(QWidget *parent = nullptr);
    ~TankWidget();

    void updatePlayerTank();
    void updateBullets();

protected:
    void paintEvent(QPaintEvent *event) override;
    void keyPressEvent(QKeyEvent *event) override;
    void keyReleaseEvent(QKeyEvent *event) override;
    void timerEvent(QTimerEvent *event) override;
    void showEvent(QShowEvent *event) override;

private:
    void initializeGame();
    void updateGame();
    void checkCollisions();
    void drawGame(QPainter &painter);
    void drawBackground(QPainter &painter);
    void drawTanks(QPainter &painter);
    void drawBullets(QPainter &painter);
    void drawObstacles(QPainter &painter);
    void drawUI(QPainter &painter);
    void drawGameOver(QPainter &painter);
    void spawnBullet(const Tank &tank);
    bool canTankMove(const Tank &tank, int dx, int dy) const;
    void checkWinLoseConditions();
    void initializeLevelConfigs();

    // Game objects
    QVector<Tank> tanks;
    QVector<Bullet> bullets;
    QVector<Obstacle> obstacles;

    // Player tank
    Tank playerTank;

    // Game state
    bool gameOver;
    bool gameWon;
    int score;
    int level;
    int lives;
    bool gameCleared;  // 通关标志

    // Level configuration
    static const int MAX_LEVELS = 10;
    struct LevelConfig {
        int enemyCount;
        int enemySpeed;
        bool enemiesCanShoot;
        int winScore;
        const char* name;  // 关卡名称
    };
    LevelConfig levelConfigs[MAX_LEVELS];

    // Timers
    int gameTimerId;
    int aiTimerId;

    // Key states
    bool keyUpPressed;
    bool keyDownPressed;
    bool keyLeftPressed;
    bool keyRightPressed;
    bool keySpacePressed;

    // Constants
    static const int CELL_SIZE = 32;
    static const int BOARD_WIDTH = 17;
    static const int BOARD_HEIGHT = 17;
    static const int GRID_SIZE = 32; // Grid cell size in pixels

private slots:
    void updateAI();
};

#endif // TANKWIDGET_H

cpp文件

tankwidget.cpp

cpp 复制代码
#include "tankwidget.h"
#include <QKeyEvent>
#include <QPainter>
#include <QDebug>
#include <cstdlib>
#include <ctime>
#include <QMessageBox>

TankWidget::TankWidget(QWidget *parent)
    : QWidget(parent), gameOver(false), gameWon(false), gameCleared(false), score(0), level(1), lives(3),
      keyUpPressed(false), keyDownPressed(false), keyLeftPressed(false),
      keyRightPressed(false), keySpacePressed(false)
{
    // 初始化关卡配置
    initializeLevelConfigs();

    setFocusPolicy(Qt::StrongFocus);
    initializeGame();
    resize(BOARD_WIDTH * GRID_SIZE + 16, BOARD_HEIGHT * GRID_SIZE + 16); // 调整窗口大小确保完整显示
    setFocus(); // 初始获取焦点
}

TankWidget::~TankWidget()
{
    killTimer(gameTimerId);
    killTimer(aiTimerId);
}

void TankWidget::initializeLevelConfigs()
{
    // 初始化关卡配置 - 难度逐级递增,每个关卡有独特的名称
    const char* levelNames[MAX_LEVELS] = {
        "初出茅庐",      // 第1关:新手入门
        "小试牛刀",      // 第2关:开始熟悉
        "崭露头角",      // 第3关:敌人开始射击
        "初露锋芒",      // 第4关:实力展现
        "游刃有余",      // 第5关:技巧熟练
        "炉火纯青",      // 第6关:高手阶段
        "登峰造极",      // 第7关:挑战极限
        "举世无双",      // 第8关:接近最强
        "傲视群雄",      // 第9关:接近通关
        "一代宗师"       // 第10关:最终挑战
    };

    for (int i = 0; i < MAX_LEVELS; ++i) {
        int actualLevel = i + 1;
        levelConfigs[i].enemyCount = 3 + actualLevel * 2;  // 5, 7, 9, ...
        levelConfigs[i].enemySpeed = 1 + actualLevel / 3;   // 1, 1, 2, 2, ...
        levelConfigs[i].enemiesCanShoot = actualLevel >= 3; // 第3关开始敌人能射击
        levelConfigs[i].winScore = actualLevel * 1000;     // 1000, 2000, 3000, ...
        levelConfigs[i].name = levelNames[i];              // 关卡名称
    }
}

void TankWidget::initializeGame()
{
    // 检查是否通关
    if (level > MAX_LEVELS) {
        gameCleared = true;
        gameWon = true;
        gameOver = true;
        return;
    }

    // 初始化随机数生成器
    std::srand(static_cast<unsigned int>(std::time(nullptr)));

    // 清除现有对象
    tanks.clear();
    bullets.clear();
    obstacles.clear();

    // 初始化玩家坦克
    playerTank = Tank(8, 13, 0, true);
    // 不添加到坦克列表,单独处理玩家控制

    // 创建地图障碍物
    // 边界墙
    for (int i = 0; i < BOARD_WIDTH; ++i) {
        obstacles.append(Obstacle(i, 0, STEEL));
        obstacles.append(Obstacle(i, BOARD_HEIGHT - 1, STEEL));
    }
    for (int i = 0; i < BOARD_HEIGHT; ++i) {
        obstacles.append(Obstacle(0, i, STEEL));
        obstacles.append(Obstacle(BOARD_WIDTH - 1, i, STEEL));
    }

    // 内部障碍物 - 随关卡增加
    for (int i = 3; i < BOARD_WIDTH - 3; i += 2) {
        for (int j = 2; j < BOARD_HEIGHT - 2; j += 2) {
            if (rand() % (4 - level / 3) == 0 && !(i == 8 && j == 8)) { // 难度越高障碍物越多
                obstacles.append(Obstacle(i, j, BRICK, rand() % 2 + 1));
            }
        }
    }

    // 玩家基地
    obstacles.append(Obstacle(7, 15, BASE));
    obstacles.append(Obstacle(8, 15, BASE));
    obstacles.append(Obstacle(9, 15, BASE));
    obstacles.append(Obstacle(7, 16, BASE));
    obstacles.append(Obstacle(8, 16, BASE));
    obstacles.append(Obstacle(9, 16, BASE));

    // 根据关卡配置生成敌人
    const LevelConfig &config = levelConfigs[level - 1];
    for (int i = 0; i < config.enemyCount; ++i) {
        // 分散生成位置
        int x = 2 + (i % 5) * 3;
        int y = 2 + (i / 5) * 2;
        Tank enemy(x, y, 2, false);
        enemy.speed = config.enemySpeed;
        tanks.append(enemy);
    }

    // 启动定时器
    gameTimerId = startTimer(16); // ~60 FPS
    aiTimerId = startTimer(100);  // AI 每100ms更新一次
}

void TankWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    // 不缩放 - 使用像素坐标绘制
    drawBackground(painter);
    drawObstacles(painter);
    drawTanks(painter);
    drawBullets(painter);
    drawUI(painter);

    if (gameOver) {
        drawGameOver(painter);
    }
}

void TankWidget::showEvent(QShowEvent *event)
{
    this->activateWindow();
    this->setFocus();
    QWidget::showEvent(event);
}

void TankWidget::keyPressEvent(QKeyEvent *event)
{
    if (gameOver) {
        if (event->key() == Qt::Key_R) {
            initializeGame();
            gameOver = false;
            gameWon = false;
            update();
        }
        return;
    }

    // 设置按键状态
    switch (event->key()) {
        case Qt::Key_Up:    keyUpPressed = true;    break;
        case Qt::Key_Down:  keyDownPressed = true;  break;
        case Qt::Key_Left:  keyLeftPressed = true;  break;
        case Qt::Key_Right: keyRightPressed = true; break;
        case Qt::Key_Space: keySpacePressed = true; break;
        default: QWidget::keyPressEvent(event); return;
    }

    event->accept();
}

void TankWidget::keyReleaseEvent(QKeyEvent *event)
{
    // 清除按键状态
    switch (event->key()) {
        case Qt::Key_Up:    keyUpPressed = false;    break;
        case Qt::Key_Down:  keyDownPressed = false;  break;
        case Qt::Key_Left:  keyLeftPressed = false;  break;
        case Qt::Key_Right: keyRightPressed = false; break;
        case Qt::Key_Space: keySpacePressed = false; break;
        default: QWidget::keyReleaseEvent(event); return;
    }

    event->accept();
}

void TankWidget::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == gameTimerId) {
        updateGame();
        checkCollisions();
        checkWinLoseConditions();
    } else if (event->timerId() == aiTimerId) {
        updateAI();
    }
}

void TankWidget::updateGame()
{
    if (gameOver) return;

    // 更新玩家坦克移动
    updatePlayerTank();

    // 更新子弹 - 使用网格坐标
    updateBullets();

    update();
}

void TankWidget::updatePlayerTank()
{
    Tank &tank = playerTank;
    if (tank.shootCooldown > 0) tank.shootCooldown--;
    if (tank.moveCooldown > 0) tank.moveCooldown--;

    // 处理按键状态
    if (tank.moveCooldown <= 0) {
        int dx = 0, dy = 0;
        bool moving = false;

        if (keyUpPressed)    { dy = -1; moving = true; tank.direction = 0; }
        if (keyDownPressed)  { dy = 1;  moving = true; tank.direction = 2; }
        if (keyLeftPressed)  { dx = -1; moving = true; tank.direction = 3; }
        if (keyRightPressed) { dx = 1;  moving = true; tank.direction = 1; }

        if (moving && canTankMove(tank, dx, dy)) {
            tank.x += dx;
            tank.y += dy;
            tank.moveCooldown = 3; // 固定冷却时间
        }
    }

    // 处理射击
    if (keySpacePressed && tank.shootCooldown <= 0) {
        spawnBullet(tank);
        tank.shootCooldown = 15;
        keySpacePressed = false; // 防止连射
    }
}

void TankWidget::updateBullets()
{
    for (auto it = bullets.begin(); it != bullets.end();) {
        Bullet &bullet = *it;

        if (!bullet.active) {
            it = bullets.erase(it);
            continue;
        }

        // 子弹按网格步长移动
        int bulletDx = 0, bulletDy = 0;
        switch (bullet.direction) {
            case 0: bulletDy = -1; break;
            case 1: bulletDx = 1; break;
            case 2: bulletDy = 1; break;
            case 3: bulletDx = -1; break;
        }

        // 子弹一步一步移动
        int newX = bullet.x + bulletDx;
        int newY = bullet.y + bulletDy;

        // 检查边界
        if (newX < 0 || newX >= BOARD_WIDTH || newY < 0 || newY >= BOARD_HEIGHT) {
            bullet.active = false;
        } else {
            bullet.x = newX;
            bullet.y = newY;
        }

        ++it;
    }
}

void TankWidget::updateAI()
{
    if (gameOver) return;

    // Update enemy tanks
    for (auto it = tanks.begin(); it != tanks.end(); ++it) {
        Tank &enemy = *it;
        if (enemy.health <= 0) continue;

        if (enemy.shootCooldown > 0) enemy.shootCooldown--;
        if (enemy.moveCooldown > 0) enemy.moveCooldown--;

        // 简单AI:移动并偶尔射击
        if (enemy.moveCooldown <= 0) {
            // 随机移动
            if (rand() % 20 == 0) {
                enemy.direction = rand() % 4;
            }

            int dx = 0, dy = 0;
            switch (enemy.direction) {
                case 0: dy = -1; break;
                case 1: dx = 1; break;
                case 2: dy = 1; break;
                case 3: dx = -1; break;
            }

            if (canTankMove(enemy, dx, dy)) {
                enemy.x += dx;
                enemy.y += dy;
                enemy.moveCooldown = 5; // Fixed cooldown for enemies
            }

            // 随机射击
            if (rand() % 50 == 0 && enemy.shootCooldown <= 0) {
                spawnBullet(enemy);
                enemy.shootCooldown = 30;
            }
        }
    }
}

void TankWidget::checkCollisions()
{
    // Check bullet-player tank collisions (enemy bullets hitting player)
    for (auto bulletIt = bullets.begin(); bulletIt != bullets.end(); ++bulletIt) {
        Bullet &bullet = *bulletIt;

        if (!bullet.active) continue;
        if (bullet.isPlayerBullet) continue; // Only check enemy bullets

        // Check collision with player tank
        if (bullet.x == playerTank.x && bullet.y == playerTank.y) {
            playerTank.health -= bullet.damage;
            bullet.active = false;

            if (playerTank.health <= 0) {
                lives--;
                if (lives <= 0) {
                    gameOver = true;
                } else {
                    // Respawn player
                    playerTank.x = 8;
                    playerTank.y = 15;
                    playerTank.health = 100;
                    playerTank.direction = 0;
                }
            }
        }
    }

    // Check bullet-enemy tank collisions (player bullets hitting enemies)
    for (auto bulletIt = bullets.begin(); bulletIt != bullets.end(); ++bulletIt) {
        Bullet &bullet = *bulletIt;

        if (!bullet.active) continue;
        if (!bullet.isPlayerBullet) continue; // Only check player bullets

        // Check collision with enemy tanks
        for (auto tankIt = tanks.begin(); tankIt != tanks.end(); ++tankIt) {
            Tank &tank = *tankIt;

            // Simple grid-based collision
            if (bullet.x == tank.x && bullet.y == tank.y) {
                tank.health -= bullet.damage;
                bullet.active = false;

                if (tank.health <= 0) {
                    score += 100;
                    tank.health = 0; // Mark for removal
                }
            }
        }
    }

    // Remove dead enemy tanks
    for (auto it = tanks.begin(); it != tanks.end();) {
        if (it->health <= 0) {
            it = tanks.erase(it);
        } else {
            ++it;
        }
    }

    // Check bullet-obstacle collisions
    for (auto bulletIt = bullets.begin(); bulletIt != bullets.end(); ++bulletIt) {
        Bullet &bullet = *bulletIt;

        if (!bullet.active) continue;

        for (auto obstacleIt = obstacles.begin(); obstacleIt != obstacles.end(); ++obstacleIt) {
            Obstacle &obstacle = *obstacleIt;

            // Grid-based collision
            if (bullet.x == obstacle.x && bullet.y == obstacle.y) {
                bullet.active = false;

                if (obstacle.type == BRICK) {
                    obstacle.durability--;
                    if (obstacle.durability <= 0) {
                        obstacleIt = obstacles.erase(obstacleIt);
                        if (obstacleIt == obstacles.end()) break;
                    }
                } else if (obstacle.type == BASE) {
                    // 只有敌人子弹击中基地才结束游戏
                    if (!bullet.isPlayerBullet) {
                        gameOver = true;
                    }
                }
                break;
            }
        }
    }

    // Spawn new enemies if needed
    int enemyCount = tanks.size(); // All remaining tanks are enemies

    if (enemyCount == 0 && !gameOver) {
        // Spawn new wave
        for (int i = 0; i < 3 + level; ++i) {
            Tank enemy(2 + i * 3, 2, 2, false);
            enemy.speed = 1 + level / 2;
            tanks.append(enemy);
        }
        level++;
    }
}

void TankWidget::checkWinLoseConditions()
{
    if (gameOver) return;

    // 检查玩家是否摧毁了所有敌人
    bool hasEnemies = false;
    for (const Tank &tank : tanks) {
        if (!tank.isPlayer) {
            hasEnemies = true;
            break;
        }
    }

    if (!hasEnemies) {
        // 关卡完成
        int completedLevel = level;
        level++;

        if (level > MAX_LEVELS) {
            // 所有关卡完成,真正通关
            gameCleared = true;
            gameWon = true;
            gameOver = true;
            QMessageBox::information(nullptr, "游戏通关",
                QString("恭喜!你已通关所有%1个关卡!\n最终得分: %2").arg(MAX_LEVELS).arg(score));
        } else {
            // 询问是否继续下一关
            QMessageBox::StandardButton btn = QMessageBox::question(nullptr, "关卡完成",
                QString("恭喜完成第%1关!\n当前得分: %2\n\n是否继续进入第%3关?")
                    .arg(completedLevel).arg(score).arg(level),
                QMessageBox::Yes | QMessageBox::No);

            if (btn == QMessageBox::Yes) {
                // 继续下一关
                initializeGame(); // 重新初始化下一关
            } else {
                // 结束游戏
                gameOver = true;
                gameWon = true; // 标记为胜利,但不是通关
            }
        }
    }
}

void TankWidget::spawnBullet(const Tank &tank)
{
    Bullet bullet(tank.x, tank.y, tank.direction, tank.isPlayer);
    bullets.append(bullet);
}

bool TankWidget::canTankMove(const Tank &tank, int dx, int dy) const
{
    int newX = tank.x + dx;
    int newY = tank.y + dy;

    // Check boundaries
    if (newX < 1 || newX >= BOARD_WIDTH - 1 ||
        newY < 1 || newY >= BOARD_HEIGHT - 1) {
        return false;
    }

    // Check obstacles
    QRect newRect((newX) * 32, (newY) * 32, 32, 32);
    for (const Obstacle &obstacle : obstacles) {
        if (obstacle.type == WATER || obstacle.type == BRICK || obstacle.type == STEEL) {
            if (newRect.intersects(obstacle.rect())) {
                return false;
            }
        }
    }

    // Check other tanks
    for (const Tank &other : tanks) {
        if (&other != &tank && other.health > 0) {
            if (other.rect().intersects(newRect)) {
                return false;
            }
        }
    }

    return true;
}

void TankWidget::drawBackground(QPainter &painter)
{
    painter.fillRect(QRect(0, 0, BOARD_WIDTH * GRID_SIZE, BOARD_HEIGHT * GRID_SIZE), QColor(0, 0, 0));
}

void TankWidget::drawTanks(QPainter &painter)
{
    // Draw player tank
    if (playerTank.health > 0) {
        int playerPixelX = playerTank.x * GRID_SIZE;
        int playerPixelY = playerTank.y * GRID_SIZE;

        // Draw tank body (slightly smaller to show grid)
        painter.setBrush(QBrush(playerTank.color));
        painter.drawRect(playerPixelX + 3, playerPixelY + 3, 26, 26);

        // Draw tank direction indicator (turret)
        painter.setBrush(QBrush(Qt::white));
        switch (playerTank.direction) {
            case 0: painter.drawRect(playerPixelX + 14, playerPixelY, 4, 10); break; // Up
            case 1: painter.drawRect(playerPixelX + 22, playerPixelY + 14, 10, 4); break; // Right
            case 2: painter.drawRect(playerPixelX + 14, playerPixelY + 22, 4, 10); break; // Down
            case 3: painter.drawRect(playerPixelX, playerPixelY + 14, 10, 4); break; // Left
        }

        // Draw health bar
        if (playerTank.health < 100) {
            painter.setBrush(QBrush(Qt::red));
            painter.drawRect(playerPixelX, playerPixelY - 4, GRID_SIZE, 2);
            painter.setBrush(QBrush(Qt::green));
            painter.drawRect(playerPixelX, playerPixelY - 4, GRID_SIZE * playerTank.health / 100, 2);
        }
    }

    // Draw enemy tanks
    for (const Tank &tank : tanks) {
        if (tank.health <= 0) continue;

        int pixelX = tank.x * GRID_SIZE;
        int pixelY = tank.y * GRID_SIZE;

        // Draw tank body (slightly smaller to show grid)
        painter.setBrush(QBrush(tank.color));
        painter.drawRect(pixelX + 3, pixelY + 3, 26, 26);

        // Draw tank direction indicator (turret)
        painter.setBrush(QBrush(Qt::white));
        switch (tank.direction) {
            case 0: painter.drawRect(pixelX + 14, pixelY, 4, 10); break; // Up
            case 1: painter.drawRect(pixelX + 22, pixelY + 14, 10, 4); break; // Right
            case 2: painter.drawRect(pixelX + 14, pixelY + 22, 4, 10); break; // Down
            case 3: painter.drawRect(pixelX, pixelY + 14, 10, 4); break; // Left
        }

        // Draw health bar
        if (tank.health < 100) {
            painter.setBrush(QBrush(Qt::red));
            painter.drawRect(pixelX, pixelY - 4, GRID_SIZE, 2);
            painter.setBrush(QBrush(Qt::green));
            painter.drawRect(pixelX, pixelY - 4, GRID_SIZE * tank.health / 100, 2);
        }
    }
}

void TankWidget::drawBullets(QPainter &painter)
{
    painter.setBrush(QBrush(Qt::yellow));
    for (const Bullet &bullet : bullets) {
        if (bullet.active) {
            int pixelX = bullet.x * GRID_SIZE + GRID_SIZE / 2 - 4;
            int pixelY = bullet.y * GRID_SIZE + GRID_SIZE / 2 - 4;
            painter.drawRect(pixelX, pixelY, 8, 8);
        }
    }
}

void TankWidget::drawObstacles(QPainter &painter)
{
    for (const Obstacle &obstacle : obstacles) {
        int pixelX = obstacle.x * GRID_SIZE;
        int pixelY = obstacle.y * GRID_SIZE;
        painter.setBrush(QBrush(obstacle.color()));
        painter.drawRect(pixelX, pixelY, GRID_SIZE, GRID_SIZE);
    }
}

void TankWidget::drawUI(QPainter &painter)
{
    // 绘制关卡进度 - 用绿色显示当前关卡和总关卡数,放在最上方
    painter.setPen(QPen(Qt::green));
    painter.drawText(QRect(0, 5, BOARD_WIDTH * GRID_SIZE, 20), Qt::AlignCenter,
                     tr("第 %1 / %2 关").arg(level).arg(MAX_LEVELS));

    // 绘制关卡名称 - 加粗深绿色,放在关卡进度下方
    QFont font = painter.font();
    font.setBold(true);  // 加粗
    font.setPointSize(12);  // 稍微大一点
    painter.setFont(font);
    painter.setPen(QPen(Qt::darkGreen));  // 深绿色
    painter.drawText(QRect(0, 25, BOARD_WIDTH * GRID_SIZE, 30), Qt::AlignCenter,
                     tr("「%1」").arg(levelConfigs[level - 1].name));

    // 绘制分数
    font.setBold(false);  // 恢复普通字体
    font.setPointSize(9);  // 恢复默认大小
    painter.setFont(font);
    painter.setPen(QPen(Qt::white));
    painter.drawText(QRect(0, 5, BOARD_WIDTH * GRID_SIZE, 20), Qt::AlignLeft,
                     tr("分数: %1").arg(score));

    // 绘制生命值
    painter.drawText(QRect(0, 5, BOARD_WIDTH * GRID_SIZE, 20), Qt::AlignRight,
                     tr("生命: %1").arg(lives));

    // 如果通关,显示通关状态
    if (gameCleared) {
        painter.setPen(QPen(Qt::green));
        painter.drawText(QRect(0, BOARD_HEIGHT * GRID_SIZE / 2 - 60, BOARD_WIDTH * GRID_SIZE, 30),
                         Qt::AlignCenter, "🏆 游戏通关!");
    }
}

void TankWidget::drawGameOver(QPainter &painter)
{
    painter.setPen(QPen(Qt::red));
    painter.drawText(QRect(0, BOARD_HEIGHT * GRID_SIZE / 2 - 30, BOARD_WIDTH * GRID_SIZE, 60),
                     Qt::AlignCenter, gameWon ? "YOU WIN!\nPress R to restart" : "GAME OVER\nPress R to restart");
}

main.cpp

cpp 复制代码
#include <QApplication>
#include "tankwidget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TankWidget w;
    w.show();
    return a.exec();
}
相关推荐
冉佳驹2 小时前
Qt 开发【第四篇】——— 常用基础、显示及输入控件核心特性概述
qt·qwidget·table widget·tree widget·text edit·boxlayout·radio button
一晌小贪欢2 小时前
Web 自动化指南:如何用 Python 和 Selenium 解放双手
开发语言·前端·图像处理·python·自动化·python办公
问水っ2 小时前
Qt Creator快速入门 第三版 第7章 Qt对象模型与容器类
开发语言·qt
zhangren024682 小时前
Laravel6.x核心特性全解析
开发语言·c++·php
菜鸟中的拖拉机2 小时前
Python之conda创建虚拟环境
开发语言·python·conda
格林威2 小时前
Baumer相机芯片引脚共面性检测:保障电子装配精度的 5 个实用方案,附 OpenCV+Halcon 实战代码!
开发语言·人工智能·opencv·计算机视觉·c#·视觉检测·工业相机
钰衡大师2 小时前
邮件头信息修改工具开发技术文档
开发语言·python
小碗羊肉2 小时前
【从零开始学Java | 第二十四篇】泛型的继承和通配符
java·开发语言·新手入门
wefly20172 小时前
jsontop.cn使用全攻略:免费无广告的在线工具站,电脑手机通用
开发语言·安全·json·ecmascript·json在线转换