#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <random>
#include <chrono>
#include <thread>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
// 游戏配置
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const std::string WINDOW_TITLE = "高质量C++射击游戏示例";
const int FPS = 60;
const int FRAME_DELAY = 1000 / FPS;
// 数学工具
struct Vector2 {
float x, y;
Vector2(float x = 0, float y = 0) : x(x), y(y) {}
Vector2 operator+(const Vector2& other) const {
return Vector2(x + other.x, y + other.y);
}
Vector2 operator-(const Vector2& other) const {
return Vector2(x - other.x, y - other.y);
}
Vector2 operator*(float scalar) const {
return Vector2(x * scalar, y * scalar);
}
float magnitude() const {
return std::sqrt(x * x + y * y);
}
Vector2 normalize() const {
float mag = magnitude();
if (mag > 0) {
return Vector2(x / mag, y / mag);
}
return Vector2();
}
};
// 资源管理器
class ResourceManager {
private:
std::unordered_map<std::string, SDL_Texture*> textures;
std::unordered_map<std::string, TTF_Font*> fonts;
SDL_Renderer* renderer;
public:
ResourceManager(SDL_Renderer* renderer) : renderer(renderer) {}
~ResourceManager() { clear(); }
SDL_Texture* loadTexture(const std::string& path) {
if (textures.find(path) != textures.end()) {
return textures[path];
}
SDL_Surface* surface = IMG_Load(path.c_str());
if (!surface) {
std::cerr << "无法加载图像: " << path << " - " << IMG_GetError() << std::endl;
return nullptr;
}
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
std::cerr << "无法创建纹理: " << path << " - " << SDL_GetError() << std::endl;
}
SDL_FreeSurface(surface);
textures[path] = texture;
return texture;
}
TTF_Font* loadFont(const std::string& path, int size) {
std::string key = path + "_" + std::to_string(size);
if (fonts.find(key) != fonts.end()) {
return fonts[key];
}
TTF_Font* font = TTF_OpenFont(path.c_str(), size);
if (!font) {
std::cerr << "无法加载字体: " << path << " - " << TTF_GetError() << std::endl;
return nullptr;
}
fonts[key] = font;
return font;
}
void clear() {
for (auto& pair : textures) {
SDL_DestroyTexture(pair.second);
}
textures.clear();
for (auto& pair : fonts) {
TTF_CloseFont(pair.second);
}
fonts.clear();
}
};
// 游戏对象基类
class GameObject {
protected:
Vector2 position;
Vector2 velocity;
float rotation;
std::string texturePath;
SDL_Rect srcRect, destRect;
bool active;
public:
GameObject(float x = 0, float y = 0, const std::string& texturePath = "")
: position(x, y), velocity(0, 0), rotation(0), texturePath(texturePath), active(true) {
srcRect = {0, 0, 0, 0};
destRect = {static_cast<int>(x), static_cast<int>(y), 0, 0};
}
virtual ~GameObject() {}
virtual void update(float deltaTime) {
position = position + velocity * deltaTime;
destRect.x = static_cast<int>(position.x);
destRect.y = static_cast<int>(position.y);
}
virtual void render(SDL_Renderer* renderer, ResourceManager* resources) {
if (!active) return;
SDL_Texture* texture = resources->loadTexture(texturePath);
if (texture) {
if (srcRect.w == 0 || srcRect.h == 0) {
SDL_QueryTexture(texture, nullptr, nullptr, &srcRect.w, &srcRect.h);
destRect.w = srcRect.w;
destRect.h = srcRect.h;
}
SDL_RenderCopyEx(renderer, texture, &srcRect, &destRect, rotation, nullptr, SDL_FLIP_NONE);
}
}
virtual void handleInput(const Uint8* keyState) {}
bool isActive() const { return active; }
void setActive(bool value) { active = value; }
Vector2 getPosition() const { return position; }
void setPosition(const Vector2& pos) { position = pos; }
Vector2 getVelocity() const { return velocity; }
void setVelocity(const Vector2& vel) { velocity = vel; }
float getRotation() const { return rotation; }
void setRotation(float rot) { rotation = rot; }
SDL_Rect getBounds() const { return destRect; }
};
// 玩家类
class Player : public GameObject {
private:
float speed;
int health;
bool shooting;
float shootCooldown;
float shootTimer;
public:
Player(float x = 0, float y = 0)
: GameObject(x, y, "assets/player.png"), speed(250), health(100), shooting(false), shootCooldown(0.3f), shootTimer(0) {}
void update(float deltaTime) override {
GameObject::update(deltaTime);
// 边界检查
if (position.x < 0) position.x = 0;
if (position.x > SCREEN_WIDTH - destRect.w) position.x = SCREEN_WIDTH - destRect.w;
if (position.y < 0) position.y = 0;
if (position.y > SCREEN_HEIGHT - destRect.h) position.y = SCREEN_HEIGHT - destRect.h;
// 更新射击计时器
if (shootTimer > 0) {
shootTimer -= deltaTime;
}
}
void handleInput(const Uint8* keyState) override {
velocity.x = 0;
velocity.y = 0;
if (keyState[SDL_SCANCODE_LEFT] || keyState[SDL_SCANCODE_A]) {
velocity.x = -speed;
}
if (keyState[SDL_SCANCODE_RIGHT] || keyState[SDL_SCANCODE_D]) {
velocity.x = speed;
}
if (keyState[SDL_SCANCODE_UP] || keyState[SDL_SCANCODE_W]) {
velocity.y = -speed;
}
if (keyState[SDL_SCANCODE_DOWN] || keyState[SDL_SCANCODE_S]) {
velocity.y = speed;
}
shooting = keyState[SDL_SCANCODE_SPACE] && shootTimer <= 0;
if (shooting) {
shootTimer = shootCooldown;
}
}
bool isShooting() const { return shooting; }
int getHealth() const { return health; }
void takeDamage(int damage) { health -= damage; }
};
// 子弹类
class Bullet : public GameObject {
private:
float speed;
int damage;
public:
Bullet(float x, float y, float angle = 0)
: GameObject(x, y, "assets/bullet.png"), speed(500), damage(10) {
rotation = angle;
velocity.x = std::cos(angle * M_PI / 180.0f) * speed;
velocity.y = std::sin(angle * M_PI / 180.0f) * speed;
}
void update(float deltaTime) override {
GameObject::update(deltaTime);
// 超出屏幕范围则失效
if (position.x < -destRect.w || position.x > SCREEN_WIDTH ||
position.y < -destRect.h || position.y > SCREEN_HEIGHT) {
active = false;
}
}
int getDamage() const { return damage; }
};
// 敌人类
class Enemy : public GameObject {
private:
float speed;
int health;
int damage;
public:
Enemy(float x, float y)
: GameObject(x, y, "assets/enemy.png"), speed(100), health(50), damage(20) {}
void update(float deltaTime, const Vector2& playerPos) override {
// 向玩家移动
Vector2 direction = (playerPos - position).normalize();
velocity = direction * speed;
GameObject::update(deltaTime);
// 边界检查
if (position.x < 0) {
position.x = 0;
velocity.x = -velocity.x;
}
if (position.x > SCREEN_WIDTH - destRect.w) {
position.x = SCREEN_WIDTH - destRect.w;
velocity.x = -velocity.x;
}
if (position.y < 0) {
position.y = 0;
velocity.y = -velocity.y;
}
if (position.y > SCREEN_HEIGHT - destRect.h) {
position.y = SCREEN_HEIGHT - destRect.h;
velocity.y = -velocity.y;
}
}
void takeDamage(int damage) {
health -= damage;
if (health <= 0) {
active = false;
}
}
int getDamage() const { return damage; }
};
// 游戏状态
enum GameState {
GAME_RUNNING,
GAME_PAUSED,
GAME_OVER,
GAME_WON
};
// 游戏主类
class Game {
private:
SDL_Window* window;
SDL_Renderer* renderer;
ResourceManager* resources;
bool running;
GameState state;
Uint32 frameStart;
int frameTime;
std::unique_ptr<Player> player;
std::vector<std::unique_ptr<Bullet>> bullets;
std::vector<std::unique_ptr<Enemy>> enemies;
int score;
float enemySpawnTimer;
float enemySpawnCooldown;
TTF_Font* font;
public:
Game() : window(nullptr), renderer(nullptr), resources(nullptr), running(false), state(GAME_RUNNING),
score(0), enemySpawnTimer(0), enemySpawnCooldown(2.0f) {}
~Game() { clean(); }
bool init() {
// 初始化SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cerr << "SDL初始化失败: " << SDL_GetError() << std::endl;
return false;
}
// 初始化SDL_image
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags)) {
std::cerr << "SDL_image初始化失败: " << IMG_GetError() << std::endl;
return false;
}
// 初始化SDL_ttf
if (TTF_Init() == -1) {
std::cerr << "SDL_ttf初始化失败: " << TTF_GetError() << std::endl;
return false;
}
// 创建窗口
window = SDL_CreateWindow(WINDOW_TITLE.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!window) {
std::cerr << "窗口创建失败: " << SDL_GetError() << std::endl;
return false;
}
// 创建渲染器
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
std::cerr << "渲染器创建失败: " << SDL_GetError() << std::endl;
return false;
}
// 初始化资源管理器
resources = new ResourceManager(renderer);
// 加载字体
font = resources->loadFont("assets/arial.ttf", 24);
if (!font) {
std::cerr << "无法加载字体!" << std::endl;
return false;
}
// 初始化玩家
player = std::make_unique<Player>(SCREEN_WIDTH / 2 - 32, SCREEN_HEIGHT - 100);
running = true;
return true;
}
void handleEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
running = false;
} else if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
running = false;
break;
case SDLK_p:
if (state == GAME_RUNNING) {
state = GAME_PAUSED;
} else if (state == GAME_PAUSED) {
state = GAME_RUNNING;
}
break;
}
}
}
// 处理玩家输入
const Uint8* keyState = SDL_GetKeyboardState(nullptr);
player->handleInput(keyState);
}
void update(float deltaTime) {
if (state != GAME_RUNNING) return;
// 更新玩家
player->update(deltaTime);
// 生成敌人
enemySpawnTimer += deltaTime;
if (enemySpawnTimer >= enemySpawnCooldown) {
spawnEnemy();
enemySpawnTimer = 0;
// 随着时间增加难度
if (enemySpawnCooldown > 0.5f) {
enemySpawnCooldown -= 0.05f;
}
}
// 更新子弹
for (auto& bullet : bullets) {
bullet->update(deltaTime);
}
// 移除无效子弹
bullets.erase(
std::remove_if(bullets.begin(), bullets.end(),
[](const std::unique_ptr<Bullet>& bullet) {
return !bullet->isActive();
}),
bullets.end()
);
// 处理射击
if (player->isShooting()) {
spawnBullet();
}
// 更新敌人
for (auto& enemy : enemies) {
enemy->update(deltaTime, player->getPosition());
}
// 移除无效敌人
enemies.erase(
std::remove_if(enemies.begin(), enemies.end(),
[](const std::unique_ptr<Enemy>& enemy) {
return !enemy->isActive();
}),
enemies.end()
);
// 碰撞检测
checkCollisions();
// 检查游戏状态
if (player->getHealth() <= 0) {
state = GAME_OVER;
}
if (score >= 1000) {
state = GAME_WON;
}
}
void render() {
// 清空屏幕
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
// 渲染玩家
player->render(renderer, resources);
// 渲染子弹
for (const auto& bullet : bullets) {
bullet->render(renderer, resources);
}
// 渲染敌人
for (const auto& enemy : enemies) {
enemy->render(renderer, resources);
}
// 渲染UI
renderUI();
// 显示
SDL_RenderPresent(renderer);
}
void clean() {
// 清理资源
delete resources;
// 销毁渲染器和窗口
if (renderer) {
SDL_DestroyRenderer(renderer);
}
if (window) {
SDL_DestroyWindow(window);
}
// 退出SDL子系统
TTF_Quit();
IMG_Quit();
SDL_Quit();
}
void run() {
if (!init()) {
return;
}
// 游戏循环
while (running) {
frameStart = SDL_GetTicks();
handleEvents();
update(1.0f / FPS);
render();
// 控制帧率
frameTime = SDL_GetTicks() - frameStart;
if (FRAME_DELAY > frameTime) {
SDL_Delay(FRAME_DELAY - frameTime);
}
}
}
void spawnBullet() {
Vector2 pos = player->getPosition();
pos.x += player->getBounds().w / 2 - 4;
pos.y -= 10;
bullets.push_back(std::make_unique<Bullet>(pos.x, pos.y));
}
void spawnEnemy() {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> xDist(0, SCREEN_WIDTH - 64);
std::uniform_int_distribution<> yDist(-100, -50);
enemies.push_back(std::make_unique<Enemy>(xDist(gen), yDist(gen)));
}
void checkCollisions() {
// 子弹与敌人碰撞
for (auto& bullet : bullets) {
if (!bullet->isActive()) continue;
for (auto& enemy : enemies) {
if (!enemy->isActive()) continue;
if (checkCollision(bullet->getBounds(), enemy->getBounds())) {
bullet->setActive(false);
enemy->takeDamage(bullet->getDamage());
if (!enemy->isActive()) {
score += 100;
}
}
}
}
// 玩家与敌人碰撞
for (auto& enemy : enemies) {
if (!enemy->isActive()) continue;
if (checkCollision(player->getBounds(), enemy->getBounds())) {
enemy->setActive(false);
player->takeDamage(enemy->getDamage());
score += 50;
}
}
}
bool checkCollision(const SDL_Rect& a, const SDL_Rect& b) {
// 简单的矩形碰撞检测
return (a.x < b.x + b.w &&
a.x + a.w > b.x &&
a.y < b.y + b.h &&
a.y + a.h > b.y);
}
void renderUI() {
// 渲染分数
std::string scoreText = "分数: " + std::to_string(score);
renderText(scoreText, 10, 10);
// 渲染生命值
std::string healthText = "生命值: " + std::to_string(player->getHealth());
renderText(healthText, 10, 40);
// 渲染游戏状态
if (state == GAME_PAUSED) {
renderText("游戏暂停 - 按P继续", SCREEN_WIDTH / 2 - 100, SCREEN_HEIGHT / 2);
} else if (state == GAME_OVER) {
renderText("游戏结束 - 按ESC退出", SCREEN_WIDTH / 2 - 100, SCREEN_HEIGHT / 2);
} else if (state == GAME_WON) {
renderText("恭喜你赢了! - 按ESC退出", SCREEN_WIDTH / 2 - 100, SCREEN_HEIGHT / 2);
}
}
void renderText(const std::string& text, int x, int y) {
SDL_Color color = {255, 255, 255, 255};
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str(), color);
if (surface) {
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
if (texture) {
SDL_Rect rect = {x, y, surface->w, surface->h};
SDL_RenderCopy(renderer, texture, nullptr, &rect);
SDL_DestroyTexture(texture);
}
SDL_FreeSurface(surface);
}
}
};
int main(int argc, char* args[]) {
Game game;
game.run();
return 0;
}
1183

被折叠的 条评论
为什么被折叠?



