游戏概述
这是一款基于Qt框架的坦克对战游戏,玩家操控绿色坦克,在17×17的网格战场上与红色敌方坦克作战,目标是通关10个难度递增的关
卡。
🎛️ 操作方式
键盘控制
- ↑ 方向键:向上移动/转向
- ↓ 方向键:向下移动/转向
- ← 方向键:向左移动/转向
- → 方向键:向右移动/转向
- 空格键:发射子弹
- R键:游戏结束后重新开始
操作特点
- 即时转向:方向键即时改变坦克朝向
- 持续移动:按住方向键坦克会持续移动
- 稳定射击:空格键有15帧冷却时间,防止连射
🗺️游戏界面
战场布局
┌─────────────────────────────────────┐
│ 第 3 / 10 关 🟢 │ ← 关卡进度
│ 「崭露头角」 🟢 │ ← 关卡名称
│ │
│ 🟤🟤🟤 🟢🔴🔴🟢 │ ← 游戏区域
│ 🟤🟡🟤 🟢🔴🟢🔴🟢 🟢🟢🟢 │
│ 🟤🟤🟤 🟢🔴🔴🟢 │
│ │
│ 分数: 1200 生命: 3 │ ← 状态信息
│ 第 3 / 10 关 🟢 │ ← 关卡进度
└─────────────────────────────────────┘
🗺️ 游戏元素
| 元素 | 颜色 | 说明 |
|---|---|---|
| 玩家坦克 | 🟢 绿色 | 生命值100,3条命 |
| 敌方坦克 | 🔴 红色 | 每关数量递增,速度递增 |
| 玩家基地 | 🟡 黄色 | 被敌人击中则游戏失败 |
| 砖块 | 🟤 棕色 | 可被子弹摧毁 |
| 钢铁墙 | ⚪ 银色 | 不可摧毁 |
| 水域 | 🔵 蓝色 | 坦克无法通过 |
| 子弹 | 🟡 黄色 | 移动速度快 |
🎯 游戏规则
胜利条件
- 关卡胜利
- 主要目标:消灭当前关卡所有敌方坦克
- 胜利奖励:+1000分 × 关卡数
- 后续选择:
- ✅ 继续:进入下一关
- ❌ 结束:保存分数,游戏胜利
- 最终通关
- 通关条件:完成所有10个关卡
- 特殊奖励:显示🏆 游戏通关!
- 最终得分:所有关卡得分总和
失败条件
- 基地被摧毁
- 触发条件:敌方子弹击中玩家基地
- 失败结果:游戏结束,可重新开始
- 玩家子弹:可以安全摧毁自己的基地(无惩罚)
- 生命值耗尽
- 初始生命: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 | ✅ | 最终挑战 |
难度递增机制
- 敌人数量:每关+2个敌人
- 敌人速度:每3关+1点速度
- 敌人智能:第3关开始能射击
- 障碍物:随关卡增加而增多
⚔️ 游戏机制
移动机制
- 网格系统:所有元素对齐32像素网格
- 移动速度:玩家移动冷却3帧,敌人按关卡配置
- 碰撞检测:
- 坦克不能穿过障碍物、水域、钢铁墙
- 子弹可以摧毁砖块
- 坦克之间不能重叠
射击机制
- 玩家子弹:速度4格/步,冷却15帧
- 敌人子弹:速度4格/步,冷却30帧
- 伤害值:所有子弹造成25点伤害
- 弹道:直线飞行,碰到障碍物消失
AI行为
- 移动模式:随机移动,每20步改变方向
- 射击模式:每50步有几率射击(第3关开始)
- 目标选择:无特定目标,随机射击
🎮 游戏策略
基础策略
- 位置控制:保持在战场中央,避免被困角落
- 移动射击:边移动边射击,增加生存率
- 障碍物利用:利用砖块阻挡敌人子弹
- 基地保护:优先消灭接近基地的敌人
进阶策略
- 诱敌深入:将敌人引到有利位置
- 区域控制:控制关键通道和位置
- 时机把握:在敌人射击间隙行动
- 资源管理:合理使用子弹和移动
关卡策略
- 第1-2关:练习基本操作,熟悉控制
- 第3-5关:学习躲避敌人子弹
- 第6-8关:提高反应速度和射击精度
- 第9-10关:全力以赴,使用所有技巧
📈 分数系统
得分方式
- 消灭敌人:+100分/个
- 关卡完成:+1000分 × 关卡数
- 连击奖励:无连击系统
- 通关奖励:无额外奖励,分数累积
分数用途
- 进度展示:显示当前游戏进度
- 成就感:激励玩家挑战更高分数
- 比较基准:与历史最高分比较
🔄 游戏流程
单局游戏流程
- 初始化 → 生成关卡、敌人、障碍物
- 游戏进行 → 玩家操作、AI响应、碰撞检测
- 关卡完成 → 消灭所有敌人,显示询问对话框
- 继续/结束 → 玩家选择继续下一关或结束游戏
- 游戏结束 → 基地被摧毁或生命耗尽
- 重新开始 → 按R键重新开始游戏
完整通关流程
- 第1关 → 熟悉操作
- 第2关 → 练习走位
- 第3-5关 → 学习应对敌人射击
- 第6-8关 → 提高技巧
- 第9关 → 接近最终挑战
- 第10关 → 一代宗师,完成通关
💡 游戏技巧
新手技巧
- 先移动后射击:确保安全再射击
- 利用障碍物:砖块是你的朋友
- 保持移动:静止的坦克更容易被击中
- 观察敌人:注意敌人的移动模式
进阶技巧
- 预判射击:提前瞄准敌人移动方向
- 诱敌战术:引诱敌人到有利位置
- 节奏控制:掌握移动和射击的节奏
- 心理战术:通过走位影响AI行为
🎯 游戏目标
短期目标
- 完成当前关卡
- 获得更高分数
- 解锁下一关
长期目标
- 通关所有10个关卡
- 获得最高分数
- 成为"一代宗师"
终极挑战
- 在最高难度下生存
- 挑战完美通关(无伤)
- 创造最高分数记录
🎮 游戏特色总结
- 经典玩法:类似经典坦克大战
- 关卡设计:10个难度递增的关卡
- 中文界面:完全中文化的UI和提示
- 智能AI:会移动和射击的敌人
- 策略性强:需要技巧和规划
- 成就感强:清晰的进度和奖励系统
这款游戏融合了经典玩法和现代设计,适合各个年龄段的玩家,既有挑战性又有成就感!
源码展示
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();
}