高质量C++射击游戏示例

#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;
}    

用DDraw实现射击游戏说明文档 要点一:画图自动切割 IDirectDrawSurface7::BltFast()方法中没有自动切割功能,即当画图元素超出窗口以外时不会自动切割,DDraw选择自动忽略不画,造成一旦超出窗口,画图元素会突然消失。 解决这一问题的方法是手动切割,代码如下: //自动切割 RECT scRect; //存放当前窗口大小区域 ZeroMemory( &scRect, sizeof( scRect ) ); GetWindowRect( GetActiveWindow(), &scRect ); //防止图片左上角超过窗口左上角 if ( x < 0 ) { m_rect.left -= x; x = 0; } if ( y scRect.right ? scRect.right : x; y = y > scRect.bottom ? scRect.bottom : y; m_rect.right = x + m_rect.right - m_rect.left > scRect.right ? scRect.right - x + m_rect.left : m_rect.right; m_rect.bottom = y + m_rect.bottom - m_rect.top > scRect.bottom ? scRect.bottom - y + m_rect.top : m_rect.bottom; 只需将上述代码加在CGraphic::BltBBuffer() 中的m_bRect = m_rect; 前即可。 要点二:背景的滚轴实现 画背景可以分为以下三种情况: 情况一:背景图片与窗口等高 情况二:背景图片高度小于窗口高度 情况三:背景图片高度大于窗口高度 上述讲解图与代码相对应地看,有助于容易理解。 另外,要点一实现之后,由于已经可以自动切割,画背景可以用其它方法。 要点三:精灵图的实现 在游戏中,如RPG游戏中的人物图、射击游戏的飞机、爆炸等,叫做精灵图。 精灵图实际上是将所有帧的图片放在一个文件中,游戏时靠一个RECT来控制画图像文件中的哪一部分,进而控制游戏显示哪一帧图,只需控制好RECT的位置即可。如下图: 控制RECT的四个角的坐标的移动,有以下代码: if (m_timeEnd – m_timeStart > 100) //只有到了100ms之后才绘图 { m_ImageID++; if(m_ImageID - m_beginID >= num) { m_ImageID = m_beginID; //最后一帧的下一帧是第一帧 } m_timeStart = timeGetTime(); } int id = m_ImageID++; SetRect(&m_rect, 41 * id, 0, 41 * (id + 1), 41); //飞机精灵图大小是41×41 m_pGraph->BltBBuffer(m_pImageBuffer, true, m_Pos.x, m_Pos.y, m_rect); 这样就实现了精灵动画的效果。 要点四:拿STL进行子弹的实现 子弹的实现可以使用STL中的vector,当按下开火键时发出一颗子弹,就往vector中添加一个结点;当子弹飞出窗口或击中敌机时,再将结点从vector中删除。每帧游戏画面中子弹飞行时只需将vector中的所有子弹进行处理、绘画即可。 参考代码如下: 1.添加子弹 if (g_ctrlDown) //当ctrl键按下时开炮! { m_BulletEnd = m_Gtime->GetTime(); if ((m_BulletEnd - m_BulletStart) * 1000 > 120) //如果连续按着开火键不放,这里控制不会发出太多子弹 { m_BulletStart = m_BulletEnd; MBULLET tmpBullet; tmpBullet.pos.x = m_SPos.x - 1; //记录开火时的子弹位置 tmpBullet.pos.y = m_SPos.y - 26; tmpBullet.speed = 5; //该子弹的飞行速度 m_BulletList.push_back(tmpBullet); //将子弹添加到vector中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值