009-游戏引擎开发中的调试及开发工具

游戏引擎开发中的调试及开发工具

游戏引擎开发过程中,高效的调试和开发工具对提升开发效率和保证产品质量至关重要。本章将详细介绍游戏引擎中常用的调试与开发工具的设计理念、实现方法及实际应用案例。

9.1 日志及跟踪

9.1.1 日志系统的设计理念

日志系统是游戏引擎中最基础的调试工具,它能够记录程序运行过程中的关键信息,帮助开发者追踪问题、分析性能并理解程序的执行流程。一个优秀的日志系统应具备以下特性:

分级记录:不同重要程度的信息应有不同的记录级别
可配置性:能够在运行时调整日志级别和输出目标
性能影响小:日志系统自身不应成为性能瓶颈
线程安全:支持多线程环境下的并发日志记录
格式丰富:支持多种格式化选项,便于信息呈现

9.1.2 实现一个高效的日志系统

下面是一个基于C++11的高性能日志系统实现:

cpp

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <mutex>
#include <vector>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <memory>
#include <unordered_map>

// 日志级别枚举
enum class LogLevel {
    Debug,
    Info,
    Warning,
    Error,
    Fatal
};

// 日志级别转字符串
const char* LogLevelToString(LogLevel level) {
    static const char* levelNames[] = {
        "DEBUG", "INFO", "WARNING", "ERROR", "FATAL"
    };
    return levelNames[static_cast<int>(level)];
}

// 日志输出接口
class LogSink {
public:
    virtual ~LogSink() = default;
    virtual void Write(LogLevel level, const std::string& message) = 0;
};

// 控制台日志输出
class ConsoleSink : public LogSink {
public:
    void Write(LogLevel level, const std::string& message) override {
        // 根据日志级别设置不同颜色
        switch (level) {
            case LogLevel::Debug:
                std::cout << "33[37m"; // 白色
                break;
            case LogLevel::Info:
                std::cout << "33[32m"; // 绿色
                break;
            case LogLevel::Warning:
                std::cout << "33[33m"; // 黄色
                break;
            case LogLevel::Error:
                std::cout << "33[31m"; // 红色
                break;
            case LogLevel::Fatal:
                std::cout << "33[35m"; // 紫色
                break;
        }
        
        std::cout << message << "33[0m" << std::endl;
    }
};

// 文件日志输出
class FileSink : public LogSink {
public:
    FileSink(const std::string& filename) {
        file_.open(filename, std::ios::out | std::ios::app);
        if (!file_.is_open()) {
            throw std::runtime_error("Failed to open log file: " + filename);
        }
    }
    
    ~FileSink() {
        if (file_.is_open()) {
            file_.close();
        }
    }
    
    void Write(LogLevel level, const std::string& message) override {
        if (file_.is_open()) {
            file_ << message << std::endl;
            file_.flush();
        }
    }
    
private:
    std::ofstream file_;
};

// 日志记录器
class Logger {
public:
    static Logger& GetInstance() {
        static Logger instance;
        return instance;
    }
    
    // 添加日志输出目标
    void AddSink(std::shared_ptr<LogSink> sink) {
        std::lock_guard<std::mutex> lock(mutex_);
        sinks_.push_back(sink);
    }
    
    // 设置全局日志级别
    void SetLevel(LogLevel level) {
        std::lock_guard<std::mutex> lock(mutex_);
        level_ = level;
    }
    
    // 记录日志
    void Log(LogLevel level, const char* file, int line, const char* function, const std::string& message) {
        std::lock_guard<std::mutex> lock(mutex_);
        
        // 检查日志级别
        if (level < level_) {
            return;
        }
        
        // 获取当前时间
        auto now = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);
        auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
            now.time_since_epoch()) % 1000;
        
        std::stringstream ss;
        ss << "[" << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S") 
           << "." << std::setfill('0') << std::setw(3) << ms.count() << "] "
           << "[" << LogLevelToString(level) << "] "
           << "[" << file << ":" << line << " " << function << "] "
           << message;
        
        std::string formatted_message = ss.str();
        
        // 输出到所有目标
        for (auto& sink : sinks_) {
            sink->Write(level, formatted_message);
        }
    }
    
private:
    Logger() : level_(LogLevel::Info) {
        // 默认添加控制台输出
        AddSink(std::make_shared<ConsoleSink>());
    }
    
    ~Logger() = default;
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;
    
    std::mutex mutex_;
    LogLevel level_;
    std::vector<std::shared_ptr<LogSink>> sinks_;
};

// 便捷宏定义
#define LOG_DEBUG(message) 
    Logger::GetInstance().Log(LogLevel::Debug, __FILE__, __LINE__, __FUNCTION__, message)
#define LOG_INFO(message) 
    Logger::GetInstance().Log(LogLevel::Info, __FILE__, __LINE__, __FUNCTION__, message)
#define LOG_WARNING(message) 
    Logger::GetInstance().Log(LogLevel::Warning, __FILE__, __LINE__, __FUNCTION__, message)
#define LOG_ERROR(message) 
    Logger::GetInstance().Log(LogLevel::Error, __FILE__, __LINE__, __FUNCTION__, message)
#define LOG_FATAL(message) 
    Logger::GetInstance().Log(LogLevel::Fatal, __FILE__, __LINE__, __FUNCTION__, message)

// 格式化日志宏
#define LOG_DEBUG_FMT(...) 
    Logger::GetInstance().Log(LogLevel::Debug, __FILE__, __LINE__, __FUNCTION__, FormatString(__VA_ARGS__))
#define LOG_INFO_FMT(...) 
    Logger::GetInstance().Log(LogLevel::Info, __FILE__, __LINE__, __FUNCTION__, FormatString(__VA_ARGS__))
#define LOG_WARNING_FMT(...) 
    Logger::GetInstance().Log(LogLevel::Warning, __FILE__, __LINE__, __FUNCTION__, FormatString(__VA_ARGS__))
#define LOG_ERROR_FMT(...) 
    Logger::GetInstance().Log(LogLevel::Error, __FILE__, __LINE__, __FUNCTION__, FormatString(__VA_ARGS__))
#define LOG_FATAL_FMT(...) 
    Logger::GetInstance().Log(LogLevel::Fatal, __FILE__, __LINE__, __FUNCTION__, FormatString(__VA_ARGS__))

// 字符串格式化助手函数
template<typename... Args>
std::string FormatString(const char* format, Args... args) {
    // 计算需要的缓冲区大小
    int size = snprintf(nullptr, 0, format, args...);
    if (size <= 0) {
        return "Format error";
    }
    
    std::vector<char> buffer(size + 1);
    snprintf(buffer.data(), buffer.size(), format, args...);
    return std::string(buffer.data(), buffer.data() + size);
}

// 使用示例
void logSystemExample() {
    // 初始化日志系统
    auto& logger = Logger::GetInstance();
    
    // 添加文件输出
    try {
        logger.AddSink(std::make_shared<FileSink>("game_engine.log"));
    } catch (const std::exception& e) {
        std::cerr << "Failed to initialize file logger: " << e.what() << std::endl;
    }
    
    // 设置日志级别
    logger.SetLevel(LogLevel::Debug);
    
    // 记录不同级别的日志
    LOG_DEBUG("This is a debug message");
    LOG_INFO("Game engine initialized");
    LOG_WARNING("Resource loading is slow");
    LOG_ERROR("Failed to load texture");
    LOG_FATAL("Critical error: engine shutdown");
    
    // 使用格式化日志
    LOG_INFO_FMT("Player position: (%0.2f, %0.2f, %0.2f)", 10.5f, 20.3f, 30.7f);
    LOG_DEBUG_FMT("Memory usage: %d MB", 1024);
}

9.1.3 跟踪系统

跟踪系统是日志系统的扩展,专注于记录程序执行流程和性能数据,对于性能分析和调试复杂问题尤为重要。

cpp

// 函数执行时间跟踪器
class ScopedTimer {
public:
    ScopedTimer(const std::string& name) 
        : name_(name), start_(std::chrono::high_resolution_clock::now()) {
        LOG_DEBUG_FMT("Entering %s", name_.c_str());
    }
    
    ~ScopedTimer() {
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start_);
        LOG_DEBUG_FMT("Exiting %s - execution time: %.3f ms", 
                      name_.c_str(), duration.count() / 1000.0);
    }
    
private:
    std::string name_;
    std::chrono::high_resolution_clock::time_point start_;
};

// 使用宏简化函数跟踪
#define TRACE_FUNCTION() ScopedTimer timer_##__LINE__(__FUNCTION__)
#define TRACE_SCOPE(name) ScopedTimer timer_##__LINE__(name)

// 函数执行计数器
class FunctionCounter {
public:
    static void IncrementCounter(const std::string& name) {
        std::lock_guard<std::mutex> lock(mutex_);
        ++counters_[name];
    }
    
    static void PrintCounters() {
        std::lock_guard<std::mutex> lock(mutex_);
        LOG_INFO("Function execution counts:");
        for (const auto& pair : counters_) {
            LOG_INFO_FMT("  %s: %d", pair.first.c_str(), pair.second);
        }
    }
    
private:
    static std::mutex mutex_;
    static std::unordered_map<std::string, int> counters_;
};

std::mutex FunctionCounter::mutex_;
std::unordered_map<std::string, int> FunctionCounter::counters_;

// 函数计数宏
#define COUNT_FUNCTION() FunctionCounter::IncrementCounter(__FUNCTION__)

// 使用示例
void expensiveOperation() {
    TRACE_FUNCTION(); // 自动跟踪函数执行时间
    COUNT_FUNCTION(); // 计数函数调用次数
    
    // 模拟耗时操作
    for (int i = 0; i < 1000000; ++i) {
        volatile int x = i * i;
    }
}

void tracingExample() {
    for (int i = 0; i < 5; ++i) {
        expensiveOperation();
    }
    
    FunctionCounter::PrintCounters();
}

9.2 调试用的绘图功能

9.2.1 调试绘图的重要性

调试绘图功能允许开发者在游戏世界中可视化各种调试信息,如碰撞边界、寻路路径、物理力量等,对于理解复杂系统的行为至关重要。

9.2.2 调试绘图系统设计

一个高效的调试绘图系统应具备以下特点:

低开销:调试绘图不应显著影响游戏性能
易于使用:简单的API接口,使开发者能快速添加调试可视化
分类控制:能根据不同类别开启或关闭特定类型的调试绘制
持久与即时:支持单帧绘制和持久绘制两种模式
3D和2D:同时支持三维空间和屏幕空间的调试绘制

9.2.3 调试绘图系统实现

cpp

#include <vector>
#include <string>
#include <unordered_map>
#include <functional>
#include <array>
#include <memory>

// 基础向量和颜色类型
struct Vector3 {
    float x, y, z;
    
    Vector3(float x_ = 0.0f, float y_ = 0.0f, float z_ = 0.0f) : x(x_), y(y_), z(z_) {}
};

struct Vector2 {
    float x, y;
    
    Vector2(float x_ = 0.0f, float y_ = 0.0f) : x(x_), y(y_) {}
};

struct Color {
    float r, g, b, a;
    
    Color(float r_ = 1.0f, float g_ = 1.0f, float b_ = 1.0f, float a_ = 1.0f) 
        : r(r_), g(g_), b(b_), a(a_) {}
    
    // 预定义的颜色
    static Color Red()    { return Color(1.0f, 0.0f, 0.0f); }
    static Color Green()  { return Color(0.0f, 1.0f, 0.0f); }
    static Color Blue()   { return Color(0.0f, 0.0f, 1.0f); }
    static Color Yellow() { return Color(1.0f, 1.0f, 0.0f); }
    static Color Cyan()   { return Color(0.0f, 1.0f, 1.0f); }
    static Color Magenta(){ return Color(1.0f, 0.0f, 1.0f); }
    static Color White()  { return Color(1.0f, 1.0f, 1.0f); }
    static Color Black()  { return Color(0.0f, 0.0f, 0.0f); }
};

// 调试图元类型
enum class DebugPrimitiveType {
    Point,
    Line,
    Triangle,
    Box,
    Sphere,
    Cylinder,
    Cone,
    Text,
    Arrow
};

// 调试绘制持续时间模式
enum class DebugDrawDuration {
    OneFrame,   // 仅当前帧绘制
    Persistent, // 持续绘制直到手动移除
    Timed       // 绘制指定时间
};

// 调试绘制类别
enum class DebugDrawCategory {
    Physics,
    AI,
    Gameplay,
    Navigation,
    Rendering,
    Network,
    Sound,
    Custom
};

// 调试图元基类
struct DebugPrimitive {
    DebugPrimitiveType type;
    DebugDrawCategory category;
    DebugDrawDuration duration;
    float timeRemaining;  // 仅用于Timed模式
    Color color;
    bool depthTest;       // 是否进行深度测试
    
    DebugPrimitive(DebugPrimitiveType t, DebugDrawCategory c, DebugDrawDuration d, 
                   const Color& col, bool depth = true, float time = 0.0f)
        : type(t), category(c), duration(d), timeRemaining(time), color(col), depthTest(depth) {}
    
    virtual ~DebugPrimitive() = default;
    
    // 子类需要实现的绘制函数
    virtual void Draw() = 0;
};

// 点图元
struct DebugPoint : public DebugPrimitive {
    Vector3 position;
    float size;
    
    DebugPoint(const Vector3& pos, float sz, DebugDrawCategory category, 
               DebugDrawDuration duration, const Color& color, float time = 0.0f)
        : DebugPrimitive(DebugPrimitiveType::Point, category, duration, color, true, time),
          position(pos), size(sz) {}
          
    void Draw() override {
        // 这里是实际的绘制代码,依赖于具体的渲染API
        // 在实际实现中,这将调用渲染系统的绘制点方法
        // 例如:renderer->DrawPoint(position, size, color);
    }
};

// 线图元
struct DebugLine : public DebugPrimitive {
    Vector3 start;
    Vector3 end;
    float thickness;
    
    DebugLine(const Vector3& from, const Vector3& to, float thick, DebugDrawCategory category,
              DebugDrawDuration duration, const Color& color, float time = 0.0f)
        : DebugPrimitive(DebugPrimitiveType::Line, category, duration, color, true, time),
          start(from), end(to), thickness(thick) {}
          
    void Draw() override {
        // 实际的线绘制代码
        // 例如:renderer->DrawLine(start, end, thickness, color);
    }
};

// 盒子图元
struct DebugBox : public DebugPrimitive {
    Vector3 center;
    Vector3 extents;
    Vector3 rotation; // 欧拉角
    bool wireframe;
    
    DebugBox(const Vector3& c, const Vector3& e, const Vector3& r, bool wire,
             DebugDrawCategory category, DebugDrawDuration duration, 
             const Color& color, float time = 0.0f)
        : DebugPrimitive(DebugPrimitiveType::Box, category, duration, color, true, time),
          center(c), extents(e), rotation(r), wireframe(wire) {}
          
    void Draw() override {
        // 实际的盒子绘制代码
        // 例如:renderer->DrawBox(center, extents, rotation, wireframe, color);
    }
};

// 球体图元
struct DebugSphere : public DebugPrimitive {
    Vector3 center;
    float radius;
    bool wireframe;
    
    DebugSphere(const Vector3& c, float r, bool wire, DebugDrawCategory category,
                DebugDrawDuration duration, const Color& color, float time = 0.0f)
        : DebugPrimitive(DebugPrimitiveType::Sphere, category, duration, color, true, time),
          center(c), radius(r), wireframe(wire) {}
          
    void Draw() override {
        // 实际的球体绘制代码
        // 例如:renderer->DrawSphere(center, radius, wireframe, color);
    }
};

// 文本图元
struct DebugText : public DebugPrimitive {
    Vector3 position;
    std::string text;
    float size;
    bool billboarded; // 是否始终面向摄像机
    
    DebugText(const Vector3& pos, const std::string& txt, float sz, bool billboard,
              DebugDrawCategory category, DebugDrawDuration duration, 
              const Color& color, float time = 0.0f)
        : DebugPrimitive(DebugPrimitiveType::Text, category, duration, color, false, time),
          position(pos), text(txt), size(sz), billboarded(billboard) {}
          
    void Draw() override {
        // 实际的文本绘制代码
        // 例如:renderer->DrawText(position, text, size, billboarded, color);
    }
};

// 调试绘制管理器
class DebugDrawManager {
public:
    static DebugDrawManager& GetInstance() {
        static DebugDrawManager instance;
        return instance;
    }
    
    // 添加调试图元
    template<typename T, typename... Args>
    void AddPrimitive(Args&&... args) {
        primitives_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
    }
    
    // 清除所有调试图元
    void ClearAll() {
        primitives_.clear();
    }
    
    // 清除特定类别的调试图元
    void ClearCategory(DebugDrawCategory category) {
        auto it = primitives_.begin();
        while (it != primitives_.end()) {
            if ((*it)->category == category) {
                it = primitives_.erase(it);
            } else {
                ++it;
            }
        }
    }
    
    // 启用/禁用特定类别的绘制
    void SetCategoryEnabled(DebugDrawCategory category, bool enabled) {
        categoryEnabled_[static_cast<int>(category)] = enabled;
    }
    
    // 更新调试图元(处理计时和移除)
    void Update(float deltaTime) {
        auto it = primitives_.begin();
        while (it != primitives_.end()) {
            auto& primitive = *it;
            
            if (primitive->duration == DebugDrawDuration::OneFrame) {
                it = primitives_.erase(it);
            } else if (primitive->duration == DebugDrawDuration::Timed) {
                primitive->timeRemaining -= deltaTime;
                if (primitive->timeRemaining <= 0.0f) {
                    it = primitives_.erase(it);
                } else {
                    ++it;
                }
            } else {
                ++it;
            }
        }
    }
    
    // 渲染所有启用类别的调试图元
    void Render() {
        for (const auto& primitive : primitives_) {
            if (categoryEnabled_[static_cast<int>(primitive->category)]) {
                primitive->Draw();
            }
        }
    }
    
    // 便捷绘制方法
    void DrawPoint(const Vector3& position, float size, DebugDrawCategory category,
                  DebugDrawDuration duration = DebugDrawDuration::OneFrame,
                  const Color& color = Color::White(), float time = 0.0f) {
        AddPrimitive<DebugPoint>(position, size, category, duration, color, time);
    }
    
    void DrawLine(const Vector3& start, const Vector3& end, float thickness,
                 DebugDrawCategory category, DebugDrawDuration duration = DebugDrawDuration::OneFrame,
                 const Color& color = Color::White(), float time = 0.0f) {
        AddPrimitive<DebugLine>(start, end, thickness, category, duration, color, time);
    }
    
    void DrawBox(const Vector3& center, const Vector3& extents, const Vector3& rotation,
                bool wireframe, DebugDrawCategory category,
                DebugDrawDuration duration = DebugDrawDuration::OneFrame,
                const Color& color = Color::White(), float time = 0.0f) {
        AddPrimitive<DebugBox>(center, extents, rotation, wireframe, category, duration, color, time);
    }
    
    void DrawSphere(const Vector3& center, float radius, bool wireframe,
                   DebugDrawCategory category, DebugDrawDuration duration = DebugDrawDuration::OneFrame,
                   const Color& color = Color::White(), float time = 0.0f) {
        AddPrimitive<DebugSphere>(center, radius, wireframe, category, duration, color, time);
    }
    
    void DrawText(const Vector3& position, const std::string& text, float size,
                 bool billboarded, DebugDrawCategory category,
                 DebugDrawDuration duration = DebugDrawDuration::OneFrame,
                 const Color& color = Color::White(), float time = 0.0f) {
        AddPrimitive<DebugText>(position, text, size, billboarded, category, duration, color, time);
    }
    
private:
    DebugDrawManager() {
        // 默认启用所有类别
        for (auto& enabled : categoryEnabled_) {
            enabled = true;
        }
    }
    
    ~DebugDrawManager() = default;
    DebugDrawManager(const DebugDrawManager&) = delete;
    DebugDrawManager& operator=(const DebugDrawManager&) = delete;
    
    std::vector<std::unique_ptr<DebugPrimitive>> primitives_;
    std::array<bool, 8> categoryEnabled_; // 对应DebugDrawCategory的枚举值
};

// 使用示例
void debugDrawExample() {
    auto& debugDraw = DebugDrawManager::GetInstance();
    
    // 绘制物理碰撞盒
    debugDraw.DrawBox(Vector3(0, 0, 0), Vector3(1, 1, 1), Vector3(0, 0, 0), 
                     true, DebugDrawCategory::Physics);
    
    // 绘制AI寻路路径
    std::vector<Vector3> path = {
        Vector3(0, 0, 0),
        Vector3(5, 0, 0),
        Vector3(5, 0, 5),
        Vector3(10, 0, 5)
    };
    
    for (size_t i = 0; i < path.size() - 1; ++i) {
        debugDraw.DrawLine(path[i], path[i+1], 2.0f, 
                          DebugDrawCategory::AI, DebugDrawDuration::Persistent,
                          Color::Green());
    }
    
    // 绘制持续3秒的玩家检测范围
    debugDraw.DrawSphere(Vector3(5, 1, 5), 10.0f, true, 
                        DebugDrawCategory::Gameplay, DebugDrawDuration::Timed,
                        Color::Yellow(), 3.0f);
    
    // 绘制调试文本
    debugDraw.DrawText(Vector3(5, 2, 5), "Player Detection Range", 1.0f, true,
                      DebugDrawCategory::Gameplay, DebugDrawDuration::Timed,
                      Color::White(), 3.0f);
    
    // 禁用物理绘制
    debugDraw.SetCategoryEnabled(DebugDrawCategory::Physics, false);
    
    // 游戏循环中调用
    float deltaTime = 0.016f; // 假设60fps
    debugDraw.Update(deltaTime);
    debugDraw.Render();
}

9.3 游戏内置菜单

9.3.1 游戏内调试菜单的重要性

游戏内置菜单提供了一种在不离开游戏的情况下调整游戏参数、查看状态信息和触发调试功能的方式,极大提高了开发效率。

9.3.2 调试菜单系统设计

一个灵活的调试菜单系统应具备以下特性:

模块化:易于添加新菜单项和功能
层次结构:支持子菜单和菜单导航
交互友好:支持键盘、鼠标、手柄等多种输入方式
可视化:提供参数调整的即时视觉反馈
持久化:能够保存和加载调试配置

9.3.3 调试菜单系统实现

cpp

#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <any>
#include <variant>
#include <iostream>

// 菜单项类型
enum class MenuItemType {
    Action,     // 触发动作
    Toggle,     // 开关选项
    Value,      // 数值调整
    SubMenu,    // 子菜单
    Separator,  // 分隔符
    ColorPicker, // 颜色选择器
    Vector3Editor // 三维向量编辑器
};

// 菜单项基类
class MenuItem {
public:
    MenuItem(const std::string& name, const std::string& description = "")
        : name_(name), description_(description), enabled_(true), visible_(true) {}
    
    virtual ~MenuItem() = default;
    
    virtual MenuItemType GetType() const = 0;
    
    const std::string& GetName() const { return name_; }
    const std::string& GetDescription() const { return description_; }
    
    bool IsEnabled() const { return enabled_; }
    void SetEnabled(bool enabled) { enabled_ = enabled; }
    
    bool IsVisible() const { return visible_; }
    void SetVisible(bool visible) { visible_ = visible; }
    
    // 处理输入和绘制由子类实现
    virtual bool HandleInput() = 0;
    virtual void Render(int x, int y, bool selected) = 0;
    
protected:
    std::string name_;
    std::string description_;
    bool enabled_;
    bool visible_;
};

// 动作菜单项
class ActionMenuItem : public MenuItem {
public:
    using ActionCallback = std::function<void()>;
    
    ActionMenuItem(const std::string& name, ActionCallback callback,
                  const std::string& description = "")
        : MenuItem(name, description), callback_(callback) {}
    
    MenuItemType GetType() const override { return MenuItemType::Action; }
    
    bool HandleInput() override {
        if (enabled_ && callback_) {
            callback_();
            return true;
        }
        return false;
    }
    
    void Render(int x, int y, bool selected) override {
        // 实现菜单项的渲染逻辑
        std::string text = name_;
        if (selected) text = "> " + text;
        // 实际实现会调用渲染系统绘制文本
        std::cout << "Render at (" << x << "," << y << "): " << text << std::endl;
    }
    
private:
    ActionCallback callback_;
};

// 开关菜单项
class ToggleMenuItem : public MenuItem {
public:
    using ToggleCallback = std::function<void(bool)>;
    
    ToggleMenuItem(const std::string& name, bool initialValue,
                  ToggleCallback callback, const std::string& description = "")
        : MenuItem(name, description), value_(initialValue), callback_(callback) {}
    
    MenuItemType GetType() const override { return MenuItemType::Toggle; }
    
    bool GetValue() const { return value_; }
    
    void SetValue(bool value) {
        if (value_ != value) {
            value_ = value;
            if (callback_) callback_(value_);
        }
    }
    
    bool HandleInput() override {
        if (enabled_) {
            SetValue(!value_);
            return true;
        }
        return false;
    }
    
    void Render(int x, int y, bool selected) override {
        std::string text = name_ + ": " + (value_ ? "ON" : "OFF");
        if (selected) text = "> " + text;
        // 实际实现会调用渲染系统绘制文本
        std::cout << "Render at (" << x << "," << y << "): " << text << std::endl;
    }
    
private:
    bool value_;
    ToggleCallback callback_;
};

// 数值菜单项
template<typename T>
class ValueMenuItem : public MenuItem {
public:
    using ValueCallback = std::function<void(T)>;
    
    ValueMenuItem(const std::string& name, T initialValue, T min, T max, T step,
                 ValueCallback callback, const std::string& description = "")
        : MenuItem(name, description), value_(initialValue), min_(min), max_(max), 
          step_(step), callback_(callback) {}
    
    MenuItemType GetType() const override { return MenuItemType::Value; }
    
    T GetValue() const { return value_; }
    
    void SetValue(T value) {
        T newValue = std::max(min_, std::min(max_, value));
        if (value_ != newValue) {
            value_ = newValue;
            if (callback_) callback_(value_);
        }
    }
    
    void Increment() {
        SetValue(value_ + step_);
    }
    
    void Decrement() {
        SetValue(value_ - step_);
    }
    
    bool HandleInput() override {
        // 在实际实现中,这里会处理左右键调整值
        return enabled_;
    }
    
    void Render(int x, int y, bool selected) override {
        std::string text = name_ + ": " + std::to_string(value_);
        if (selected) text = "> " + text + " < Press Left/Right to adjust";
        // 实际实现会调用渲染系统绘制文本
        std::cout << "Render at (" << x << "," << y << "): " << text << std::endl;
    }
    
private:
    T value_;
    T min_;
    T max_;
    T step_;
    ValueCallback callback_;
};

// 子菜单项
class SubMenuItem : public MenuItem {
public:
    SubMenuItem(const std::string& name, const std::string& description = "")
        : MenuItem(name, description) {}
    
    MenuItemType GetType() const override { return MenuItemType::SubMenu; }
    
    void AddItem(std::shared_ptr<MenuItem> item) {
        items_.push_back(item);
    }
    
    const std::vector<std::shared_ptr<MenuItem>>& GetItems() const {
        return items_;
    }
    
    bool HandleInput() override {
        // 在实际实现中,这里会进入子菜单
        return enabled_;
    }
    
    void Render(int x, int y, bool selected) override {
        std::string text = name_ + " >";
        if (selected) text = "> " + text;
        // 实际实现会调用渲染系统绘制文本
        std::cout << "Render at (" << x << "," << y << "): " << text << std::endl;
    }
    
private:
    std::vector<std::shared_ptr<MenuItem>> items_;
};

// 分隔符菜单项
class SeparatorMenuItem : public MenuItem {
public:
    SeparatorMenuItem() : MenuItem("", "") {}
    
    MenuItemType GetType() const override { return MenuItemType::Separator; }
    
    bool HandleInput() override {
        // 分隔符不处理输入
        return false;
    }
    
    void Render(int x, int y, bool selected) override {
        // 实际实现会绘制一条分隔线
        std::cout << "Render at (" << x << "," << y << "): ----------" << std::endl;
    }
};

// 调试菜单管理器
class DebugMenuManager {
public:
    static DebugMenuManager& GetInstance() {
        static DebugMenuManager instance;
        return instance;
    }
    
    // 添加根菜单项
    void AddMenuItem(std::shared_ptr<MenuItem> item) {
        if (item) {
            rootMenu_.AddItem(item);
        }
    }
    
    // 创建并添加动作菜单项
    std::shared_ptr<ActionMenuItem> AddAction(const std::string& name, 
                                            std::function<void()> callback,
                                            const std::string& description = "") {
        auto item = std::make_shared<ActionMenuItem>(name, callback, description);
        AddMenuItem(item);
        return item;
    }
    
    // 创建并添加开关菜单项
    std::shared_ptr<ToggleMenuItem> AddToggle(const std::string& name, bool initialValue,
                                           std::function<void(bool)> callback,
                                           const std::string& description = "") {
        auto item = std::make_shared<ToggleMenuItem>(name, initialValue, callback, description);
        AddMenuItem(item);
        return item;
    }
    
    // 创建并添加数值菜单项
    template<typename T>
    std::shared_ptr<ValueMenuItem<T>> AddValue(const std::string& name, T initialValue,
                                             T min, T max, T step,
                                             std::function<void(T)> callback,
                                             const std::string& description = "") {
        auto item = std::make_shared<ValueMenuItem<T>>(
            name, initialValue, min, max, step, callback, description);
        AddMenuItem(item);
        return item;
    }
    
    // 创建并添加子菜单
    std::shared_ptr<SubMenuItem> AddSubMenu(const std::string& name,
                                          const std::string& description = "") {
        auto item = std::make_shared<SubMenuItem>(name, description);
        AddMenuItem(item);
        return item;
    }
    
    // 添加分隔符
    void AddSeparator() {
        AddMenuItem(std::make_shared<SeparatorMenuItem>());
    }
    
    // 显示/隐藏菜单
    void SetVisible(bool visible) {
        visible_ = visible;
    }
    
    bool IsVisible() const {
        return visible_;
    }
    
    // 处理输入
    void HandleInput() {
        if (!visible_) return;
        
        // 实现菜单导航和项目选择逻辑
        // ...
        
        // 处理当前选中项的输入
        if (currentMenu_ && currentIndex_ >= 0 && 
            currentIndex_ < currentMenu_->GetItems().size()) {
            auto item = currentMenu_->GetItems()[currentIndex_];
            if (item && item->IsEnabled()) {
                item->HandleInput();
            }
        }
    }
    
    // 渲染菜单
    void Render() {
        if (!visible_) return;
        
        // 实现菜单绘制逻辑
        int x = 50;
        int y = 50;
        int lineHeight = 20;
        
        // 绘制菜单标题
        std::cout << "Debug Menu" << std::endl;
        y += lineHeight;
        
        // 绘制菜单项
        if (currentMenu_) {
            const auto& items = currentMenu_->GetItems();
            for (size_t i = 0; i < items.size(); ++i) {
                auto item = items[i];
                if (item && item->IsVisible()) {
                    item->Render(x, y, i == currentIndex_);
                    y += lineHeight;
                }
            }
        }
    }
    
private:
    DebugMenuManager() 
        : rootMenu_("Root"), currentMenu_(&rootMenu_), currentIndex_(0), visible_(false) {}
    
    ~DebugMenuManager() = default;
    DebugMenuManager(const DebugMenuManager&) = delete;
    DebugMenuManager& operator=(const DebugMenuManager&) = delete;
    
    SubMenuItem rootMenu_;
    SubMenuItem* currentMenu_;
    size_t currentIndex_;
    bool visible_;
};

// 使用示例
void debugMenuExample() {
    auto& menu = DebugMenuManager::GetInstance();
    
    // 添加一些调试菜单项
    menu.AddAction("Reset Game", []() {
        std::cout << "Game reset!" << std::endl;
    }, "Restart the game from beginning");
    
    menu.AddToggle("Show FPS", true, [](bool value) {
        std::cout << "FPS display " << (value ? "enabled" : "disabled") << std::endl;
    }, "Toggle frame rate display");
    
    menu.AddValue<float>("Player Speed", 5.0f, 1.0f, 20.0f, 0.5f, [](float value) {
        std::cout << "Player speed set to " << value << std::endl;
    }, "Adjust player movement speed");
    
    menu.AddSeparator();
    
    // 添加渲染子菜单
    auto renderMenu = menu.AddSubMenu("Rendering", "Graphics settings");
    renderMenu->AddToggle("Wireframe Mode", false, [](bool value) {
        std::cout << "Wireframe mode " << (value ? "enabled" : "disabled") << std::endl;
    });
    
    renderMenu->AddValue<int>("Shadow Quality", 2, 0, 3, 1, [](int value) {
        const char* qualities[] = { "Off", "Low", "Medium", "High" };
        std::cout << "Shadow quality set to " << qualities[value] << std::endl;
    });
    
    // 添加AI子菜单
    auto aiMenu = menu.AddSubMenu("AI Debug", "AI visualization options");
    aiMenu->AddToggle("Show Pathfinding", false, [](bool value) {
        std::cout << "Pathfinding visualization " << (value ? "enabled" : "disabled") << std::endl;
    });
    
    aiMenu->AddToggle("Show Detection Ranges", true, [](bool value) {
        std::cout << "Detection ranges " << (value ? "visible" : "hidden") << std::endl;
    });
    
    // 显示菜单并处理输入
    menu.SetVisible(true);
    menu.HandleInput();
    menu.Render();
}

9.4 游戏内置主控台

9.4.1 游戏控制台的重要性

游戏内置主控台允许开发者和高级用户通过命令行方式与游戏交互,执行命令、查询状态和修改参数,是一个强大的调试和测试工具。

9.4.2 控制台系统设计

一个功能完备的游戏控制台应具备以下特性:

命令注册:支持动态注册和移除命令
参数解析:能解析各种类型的命令参数
自动完成:提供命令和参数的自动完成功能
历史记录:保存和浏览命令历史
输出控制:支持彩色输出和输出过滤
脚本支持:能够执行控制台脚本文件

9.4.3 控制台系统实现

cpp

#include <string>
#include <vector>
#include <functional>
#include <unordered_map>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <memory>

// 控制台命令参数
class CommandArgs {
public:
    CommandArgs(const std::vector<std::string>& args) : args_(args) {}
    
    size_t Count() const {
        return args_.size();
    }
    
    const std::string& GetString(size_t index) const {
        if (index < args_.size()) {
            return args_[index];
        }
        static const std::string empty;
        return empty;
    }
    
    int GetInt(size_t index, int defaultValue = 0) const {
        if (index < args_.size()) {
            try {
                return std::stoi(args_[index]);
            } catch (...) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    float GetFloat(size_t index, float defaultValue = 0.0f) const {
        if (index < args_.size()) {
            try {
                return std::stof(args_[index]);
            } catch (...) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    bool GetBool(size_t index, bool defaultValue = false) const {
        if (index < args_.size()) {
            const std::string& arg = args_[index];
            if (arg == "true" || arg == "1" || arg == "yes" || arg == "y") {
                return true;
            }
            if (arg == "false" || arg == "0" || arg == "no" || arg == "n") {
                return false;
            }
        }
        return defaultValue;
    }
    
private:
    std::vector<std::string> args_;
};

// 控制台命令执行结果
struct CommandResult {
    bool success;
    std::string message;
    
    CommandResult(bool s = true, const std::string& msg = "")
        : success(s), message(msg) {}
};

// 控制台命令定义
class ConsoleCommand {
public:
    using CommandCallback = std::function<CommandResult(const CommandArgs&)>;
    
    ConsoleCommand(const std::string& name, CommandCallback callback,
                  const std::string& description = "", const std::string& usage = "")
        : name_(name), callback_(callback), description_(description), usage_(usage) {}
    
    const std::string& GetName() const { return name_; }
    const std::string& GetDescription() const { return description_; }
    const std::string& GetUsage() const { return usage_; }
    
    CommandResult Execute(const CommandArgs& args) const {
        if (callback_) {
            return callback_(args);
        }
        return CommandResult(false, "Command not implemented");
    }
    
private:
    std::string name_;
    CommandCallback callback_;
    std::string description_;
    std::string usage_;
};

// 控制台系统
class Console {
public:
    static Console& GetInstance() {
        static Console instance;
        return instance;
    }
    
    // 注册命令
    void RegisterCommand(const std::string& name, ConsoleCommand::CommandCallback callback,
                        const std::string& description = "", const std::string& usage = "") {
        commands_[name] = std::make_unique<ConsoleCommand>(name, callback, description, usage);
    }
    
    // 注销命令
    void UnregisterCommand(const std::string& name) {
        commands_.erase(name);
    }
    
    // 执行命令
    CommandResult ExecuteCommand(const std::string& commandLine) {
        // 将命令行拆分为命令名和参数
        std::vector<std::string> tokens = TokenizeCommandLine(commandLine);
        if (tokens.empty()) {
            return CommandResult(false, "Empty command");
        }
        
        // 查找命令
        const std::string& commandName = tokens[0];
        auto it = commands_.find(commandName);
        if (it == commands_.end()) {
            return CommandResult(false, "Unknown command: " + commandName);
        }
        
        // 提取参数并执行命令
        std::vector<std::string> args(tokens.begin() + 1, tokens.end());
        CommandArgs commandArgs(args);
        
        // 记录命令历史
        commandHistory_.push_back(commandLine);
        if (commandHistory_.size() > maxHistorySize_) {
            commandHistory_.erase(commandHistory_.begin());
        }
        
        // 执行命令
        CommandResult result = it->second->Execute(commandArgs);
        
        // 记录输出
        if (!result.message.empty()) {
            AddOutput(result.success ? OutputType::Normal : OutputType::Error, result.message);
        }
        
        return result;
    }
    
    // 获取命令自动完成建议
    std::vector<std::string> GetAutoCompleteSuggestions(const std::string& partial) {
        std::vector<std::string> suggestions;
        
        for (const auto& pair : commands_) {
            const std::string& name = pair.first;
            if (name.find(partial) == 0) {
                suggestions.push_back(name);
            }
        }
        
        std::sort(suggestions.begin(), suggestions.end());
        return suggestions;
    }
    
    // 获取命令历史
    const std::vector<std::string>& GetCommandHistory() const {
        return commandHistory_;
    }
    
    // 控制台输出类型
    enum class OutputType {
        Normal,
        Warning,
        Error,
        Success
    };
    
    // 添加控制台输出
    void AddOutput(OutputType type, const std::string& message) {
        outputHistory_.push_back({ type, message });
        if (outputHistory_.size() > maxOutputSize_) {
            outputHistory_.erase(outputHistory_.begin());
        }
    }
    
    // 获取控制台输出历史
    const std::vector<std::pair<OutputType, std::string>>& GetOutputHistory() const {
        return outputHistory_;
    }
    
    // 清空输出历史
    void ClearOutput() {
        outputHistory_.clear();
    }
    
    // 显示控制台
    void Show() {
        visible_ = true;
    }
    
    // 隐藏控制台
    void Hide() {
        visible_ = false;
    }
    
    // 切换控制台显示状态
    void Toggle() {
        visible_ = !visible_;
    }
    
    bool IsVisible() const {
        return visible_;
    }
    
    // 设置最大历史记录大小
    void SetMaxHistorySize(size_t size) {
        maxHistorySize_ = size;
    }
    
    // 设置最大输出历史大小
    void SetMaxOutputSize(size_t size) {
        maxOutputSize_ = size;
    }
    
private:
    Console() : visible_(false), maxHistorySize_(100), maxOutputSize_(1000) {
        RegisterDefaultCommands();
    }
    
    ~Console() = default;
    Console(const Console&) = delete;
    Console& operator=(const Console&) = delete;
    
    // 分词处理命令行
    std::vector<std::string> TokenizeCommandLine(const std::string& commandLine) {
        std::vector<std::string> tokens;
        std::string token;
        bool inQuotes = false;
        
        for (char c : commandLine) {
            if (c == '"') {
                inQuotes = !inQuotes;
            } else if (c == ' ' && !inQuotes) {
                if (!token.empty()) {
                    tokens.push_back(token);
                    token.clear();
                }
            } else {
                token += c;
            }
        }
        
        if (!token.empty()) {
            tokens.push_back(token);
        }
        
        return tokens;
    }
    
    // 注册默认命令
    void RegisterDefaultCommands() {
        // help命令
        RegisterCommand("help", [this](const CommandArgs& args) {
            if (args.Count() > 0) {
                // 显示特定命令的帮助
                const std::string& commandName = args.GetString(0);
                auto it = commands_.find(commandName);
                if (it != commands_.end()) {
                    const auto& command = it->second;
                    return CommandResult(true, 
                        command->GetName() + ": " + command->GetDescription() + "
" +
                        "Usage: " + command->GetUsage());
                } else {
                    return CommandResult(false, "Unknown command: " + commandName);
                }
            } else {
                // 显示所有命令列表
                std::stringstream ss;
                ss << "Available commands:
";
                
                std::vector<std::string> commandNames;
                for (const auto& pair : commands_) {
                    commandNames.push_back(pair.first);
                }
                
                std::sort(commandNames.begin(), commandNames.end());
                
                for (const auto& name : commandNames) {
                    const auto& command = commands_[name];
                    ss << "  " << name << " - " << command->GetDescription() << "
";
                }
                
                ss << "Type 'help <command>' for more information on a specific command.";
                return CommandResult(true, ss.str());
            }
        }, "Display help information", "help [command]");
        
        // clear命令
        RegisterCommand("clear", [this](const CommandArgs& args) {
            ClearOutput();
            return CommandResult(true, "Console output cleared");
        }, "Clear console output", "clear");
        
        // echo命令
        RegisterCommand("echo", [](const CommandArgs& args) {
            std::stringstream ss;
            for (size_t i = 0; i < args.Count(); ++i) {
                if (i > 0) ss << " ";
                ss << args.GetString(i);
            }
            return CommandResult(true, ss.str());
        }, "Display a message", "echo <message>");
    }
    
    std::unordered_map<std::string, std::unique_ptr<ConsoleCommand>> commands_;
    std::vector<std::string> commandHistory_;
    std::vector<std::pair<OutputType, std::string>> outputHistory_;
    bool visible_;
    size_t maxHistorySize_;
    size_t maxOutputSize_;
};

// 使用示例
void consoleExample() {
    auto& console = Console::GetInstance();
    
    // 注册一些自定义命令
    console.RegisterCommand("setSpeed", [](const CommandArgs& args) {
        if (args.Count() < 1) {
            return CommandResult(false, "Usage: setSpeed <value>");
        }
        
        float speed = args.GetFloat(0);
        std::string message = "Player speed set to " + std::to_string(speed);
        return CommandResult(true, message);
    }, "Set player movement speed", "setSpeed <value>");
    
    console.RegisterCommand("spawn", [](const CommandArgs& args) {
        if (args.Count() < 1) {
            return CommandResult(false, "Usage: spawn <entityType> [count]");
        }
        
        std::string entityType = args.GetString(0);
        int count = args.GetInt(1, 1);
        
        std::string message = "Spawned " + std::to_string(count) + " " + entityType + "(s)";
        return CommandResult(true, message);
    }, "Spawn entities in the world", "spawn <entityType> [count]");
    
    console.RegisterCommand("godmode", [](const CommandArgs& args) {
        bool enable = true;
        if (args.Count() > 0) {
            enable = args.GetBool(0);
        }
        
        std::string message = "God mode " + std::string(enable ? "enabled" : "disabled");
        return CommandResult(true, message);
    }, "Toggle player invincibility", "godmode [true/false]");
    
    // 显示控制台
    console.Show();
    
    // 执行一些命令
    console.ExecuteCommand("help");
    console.ExecuteCommand("setSpeed 10.5");
    console.ExecuteCommand("spawn enemy 5");
    console.ExecuteCommand("godmode true");
    console.ExecuteCommand("invalid command");
    
    // 获取命令历史
    std::cout << "Command history:" << std::endl;
    for (const auto& cmd : console.GetCommandHistory()) {
        std::cout << "  " << cmd << std::endl;
    }
    
    // 获取输出历史
    std::cout << "Console output:" << std::endl;
    for (const auto& output : console.GetOutputHistory()) {
        const char* typeStr = "";
        switch (output.first) {
            case Console::OutputType::Normal:  typeStr = ""; break;
            case Console::OutputType::Warning: typeStr = "[WARNING] "; break;
            case Console::OutputType::Error:   typeStr = "[ERROR] "; break;
            case Console::OutputType::Success: typeStr = "[SUCCESS] "; break;
        }
        
        std::cout << "  " << typeStr << output.second << std::endl;
    }
}

9.5 调试用摄像机和游戏暂停

9.5.1 调试摄像机系统

调试摄像机允许开发者自由移动和观察游戏世界的任何角落,对于排查视觉问题和调试场景特别有用。

cpp

// 调试摄像机类
class DebugCamera {
public:
    DebugCamera()
        : position_(0, 2, -5), target_(0, 0, 0), up_(0, 1, 0),
          speed_(5.0f), rotationSpeed_(0.005f), enabled_(false) {}
    
    void Enable() {
        // 保存当前游戏摄像机状态
        SaveGameCameraState();
        enabled_ = true;
    }
    
    void Disable() {
        // 恢复游戏摄像机状态
        RestoreGameCameraState();
        enabled_ = false;
    }
    
    void Toggle() {
        if (enabled_) {
            Disable();
        } else {
            Enable();
        }
    }
    
    bool IsEnabled() const {
        return enabled_;
    }
    
    // 处理输入并更新摄像机位置
    void Update(float deltaTime, const InputState& input) {
        if (!enabled_) return;
        
        // 处理移动输入
        Vector3 moveDir(0, 0, 0);
        
        if (input.IsKeyPressed('W')) moveDir.z += 1.0f;
        if (input.IsKeyPressed('S')) moveDir.z -= 1.0f;
        if (input.IsKeyPressed('A')) moveDir.x -= 1.0f;
        if (input.IsKeyPressed('D')) moveDir.x += 1.0f;
        if (input.IsKeyPressed('Q')) moveDir.y -= 1.0f;
        if (input.IsKeyPressed('E')) moveDir.y += 1.0f;
        
        // 规范化移动向量
        if (moveDir.x != 0 || moveDir.y != 0 || moveDir.z != 0) {
            float length = std::sqrt(moveDir.x * moveDir.x + 
                                    moveDir.y * moveDir.y + 
                                    moveDir.z * moveDir.z);
            moveDir.x /= length;
            moveDir.y /= length;
            moveDir.z /= length;
        }
        
        // 计算移动速度(按住Shift键加速)
        float currentSpeed = speed_;
        if (input.IsKeyPressed(VK_SHIFT)) {
            currentSpeed *= 3.0f;
        }
        
        // 计算相机前向、右向和上向量
        Vector3 forward = NormalizeVector(SubtractVectors(target_, position_));
        Vector3 right = NormalizeVector(CrossProduct(forward, up_));
        Vector3 upDir = NormalizeVector(CrossProduct(right, forward));
        
        // 应用移动
        position_.x += (right.x * moveDir.x + upDir.x * moveDir.y + forward.x * moveDir.z) * currentSpeed * deltaTime;
        position_.y += (right.y * moveDir.x + upDir.y * moveDir.y + forward.y * moveDir.z) * currentSpeed * deltaTime;
        position_.z += (right.z * moveDir.x + upDir.z * moveDir.y + forward.z * moveDir.z) * currentSpeed * deltaTime;
        
        // 更新目标点
        target_.x = position_.x + forward.x;
        target_.y = position_.y + forward.y;
        target_.z = position_.z + forward.z;
        
        // 处理旋转输入(鼠标移动)
        if (input.IsMouseButtonPressed(1)) { // 右键
            float mouseX = input.GetMouseDeltaX() * rotationSpeed_;
            float mouseY = input.GetMouseDeltaY() * rotationSpeed_;
            
            // 围绕上向量旋转(左右)
            RotateAroundAxis(up_, mouseX);
            
            // 围绕右向量旋转(上下)
            RotateAroundAxis(right, mouseY);
        }
        
        // 应用摄像机变换到渲染系统
        ApplyCameraToRenderSystem();
    }
    
private:
    Vector3 position_;
    Vector3 target_;
    Vector3 up_;
    float speed_;
    float rotationSpeed_;
    bool enabled_;
    
    // 保存的游戏摄像机状态
    struct {
        Vector3 position;
        Vector3 target;
        Vector3 up;
    } savedGameCamera_;
    
    void SaveGameCameraState() {
        // 在实际实现中,从游戏摄像机系统获取当前状态
        // 这里仅为示例
        savedGameCamera_.position = Vector3(0, 0, 0);
        savedGameCamera_.target = Vector3(0, 0, 1);
        savedGameCamera_.up = Vector3(0, 1, 0);
    }
    
    void RestoreGameCameraState() {
        // 在实际实现中,恢复游戏摄像机状态
    }
    
    void ApplyCameraToRenderSystem() {
        // 在实际实现中,将摄像机变换应用到渲染系统
        // 例如:renderer->SetViewMatrix(CreateLookAtMatrix(position_, target_, up_));
    }
    
    // 向量数学辅助函数
    Vector3 SubtractVectors(const Vector3& a, const Vector3& b) {
        return Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
    }
    
    Vector3 NormalizeVector(const Vector3& v) {
        float length = std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
        if (length < 0.0001f) return Vector3(0, 0, 0);
        return Vector3(v.x / length, v.y / length, v.z / length);
    }
    
    Vector3 CrossProduct(const Vector3& a, const Vector3& b) {
        return Vector3(
            a.y * b.z - a.z * b.y,
            a.z * b.x - a.x * b.z,
            a.x * b.y - a.y * b.x
        );
    }
    
    void RotateAroundAxis(const Vector3& axis, float angle) {
        // 在实际实现中,这将使用四元数或旋转矩阵
        // 简化版仅作示例,实际需要完整的旋转计算
    }
};

9.5.2 游戏暂停系统

游戏暂停功能允许开发者在任何时刻冻结游戏状态,便于观察和调试复杂的游戏场景。

cpp

// 游戏暂停管理器
class GamePauseManager {
public:
    static GamePauseManager& GetInstance() {
        static GamePauseManager instance;
        return instance;
    }
    
    // 暂停游戏
    void Pause() {
        if (!isPaused_) {
            isPaused_ = true;
            pauseTimeScale_ = currentTimeScale_;
            SetTimeScale(0.0f);
            
            // 通知系统游戏已暂停
            NotifyGamePaused();
        }
    }
    
    // 恢复游戏
    void Resume() {
        if (isPaused_) {
            isPaused_ = false;
            SetTimeScale(pauseTimeScale_);
            
            // 通知系统游戏已恢复
            NotifyGameResumed();
        }
    }
    
    // 切换暂停状态
    void TogglePause() {
        if (isPaused_) {
            Resume();
        } else {
            Pause();
        }
    }
    
    // 设置时间缩放
    void SetTimeScale(float scale) {
        currentTimeScale_ = std::max(0.0f, scale);
        
        // 在实际实现中,这会影响游戏的时间系统
        // 例如:gameTime->SetTimeScale(currentTimeScale_);
    }
    
    // 获取时间缩放
    float GetTimeScale() const {
        return currentTimeScale_;
    }
    
    // 检查是否暂停
    bool IsPaused() const {
        return isPaused_;
    }
    
    // 单步执行(在暂停状态下前进一帧)
    void StepForward() {
        if (isPaused_) {
            // 临时恢复时间流动
            SetTimeScale(1.0f);
            
            // 执行一帧更新
            // 在实际实现中,这将触发游戏更新循环执行一次
            // 例如:gameLoop->StepFrame();
            
            // 恢复暂停状态
            SetTimeScale(0.0f);
            
            std::cout << "Stepped forward one frame" << std::endl;
        }
    }
    
    // 注册暂停状态改变回调
    using PauseStateChangeCallback = std::function<void(bool)>;
    
    void RegisterPauseStateChangeCallback(PauseStateChangeCallback callback) {
        if (callback) {
            stateChangeCallbacks_.push_back(callback);
        }
    }
    
private:
    GamePauseManager() 
        : isPaused_(false), currentTimeScale_(1.0f), pauseTimeScale_(1.0f) {}
    
    ~GamePauseManager() = default;
    GamePauseManager(const GamePauseManager&) = delete;
    GamePauseManager& operator=(const GamePauseManager&) = delete;
    
    bool isPaused_;
    float currentTimeScale_;
    float pauseTimeScale_;
    std::vector<PauseStateChangeCallback> stateChangeCallbacks_;
    
    void NotifyGamePaused() {
        for (const auto& callback : stateChangeCallbacks_) {
            callback(true);
        }
    }
    
    void NotifyGameResumed() {
        for (const auto& callback : stateChangeCallbacks_) {
            callback(false);
        }
    }
};

9.6 作弊

作弊功能在游戏开发中非常有用,它允许开发者跳过正常游戏流程,快速测试特定场景或功能。

cpp

// 作弊系统
class CheatSystem {
public:
    static CheatSystem& GetInstance() {
        static CheatSystem instance;
        return instance;
    }
    
    // 注册作弊命令
    using CheatCallback = std::function<bool(const std::vector<std::string>&)>;
    
    void RegisterCheat(const std::string& command, CheatCallback callback,
                      const std::string& description = "") {
        cheats_[command] = { callback, description };
    }
    
    // 执行作弊命令
    bool ExecuteCheat(const std::string& commandLine) {
        // 分割命令行
        std::vector<std::string> tokens;
        std::stringstream ss(commandLine);
        std::string token;
        
        while (std::getline(ss, token, ' ')) {
            if (!token.empty()) {
                tokens.push_back(token);
            }
        }
        
        if (tokens.empty()) {
            return false;
        }
        
        // 查找命令
        const std::string& command = tokens[0];
        auto it = cheats_.find(command);
        if (it == cheats_.end()) {
            std::cout << "Unknown cheat: " << command << std::endl;
            return false;
        }
        
        // 提取参数并执行
        std::vector<std::string> args(tokens.begin() + 1, tokens.end());
        bool result = it->second.callback(args);
        
        // 记录使用情况
        cheatsUsed_[command]++;
        
        return result;
    }
    
    // 启用/禁用作弊
    void SetEnabled(bool enabled) {
        enabled_ = enabled;
    }
    
    bool IsEnabled() const {
        return enabled_;
    }
    
    // 列出所有可用作弊
    void ListCheats() {
        std::cout << "Available cheats:" << std::endl;
        for (const auto& pair : cheats_) {
            std::cout << "  " << pair.first << " - " << pair.second.description << std::endl;
        }
    }
    
    // 获取作弊使用统计
    void PrintCheatUsageStats() {
        std::cout << "Cheat usage statistics:" << std::endl;
        for (const auto& pair : cheatsUsed_) {
            std::cout << "  " << pair.first << ": used " << pair.second << " times" << std::endl;
        }
    }
    
private:
    struct CheatInfo {
        CheatCallback callback;
        std::string description;
    };
    
    CheatSystem() : enabled_(false) {
        RegisterDefaultCheats();
    }
    
    ~CheatSystem() = default;
    CheatSystem(const CheatSystem&) = delete;
    CheatSystem& operator=(const CheatSystem&) = delete;
    
    std::unordered_map<std::string, CheatInfo> cheats_;
    std::unordered_map<std::string, int> cheatsUsed_;
    bool enabled_;
    
    void RegisterDefaultCheats() {
        // 无敌模式
        RegisterCheat("god", [](const std::vector<std::string>& args) {
            bool enable = true;
            if (!args.empty()) {
                enable = (args[0] == "1" || args[0] == "true" || args[0] == "on");
            }
            
            std::cout << "God mode " << (enable ? "enabled" : "disabled") << std::endl;
            // 实际实现中会修改玩家状态
            return true;
        }, "Toggle invincibility (god [on/off])");
        
        // 无限弹药
        RegisterCheat("infiniteammo", [](const std::vector<std::string>& args) {
            bool enable = true;
            if (!args.empty()) {
                enable = (args[0] == "1" || args[0] == "true" || args[0] == "on");
            }
            
            std::cout << "Infinite ammo " << (enable ? "enabled" : "disabled") << std::endl;
            // 实际实现中会修改武器系统
            return true;
        }, "Toggle infinite ammunition (infiniteammo [on/off])");
        
        // 传送
        RegisterCheat("teleport", [](const std::vector<std::string>& args) {
            if (args.size() < 3) {
                std::cout << "Usage: teleport <x> <y> <z>" << std::endl;
                return false;
            }
            
            try {
                float x = std::stof(args[0]);
                float y = std::stof(args[1]);
                float z = std::stof(args[2]);
                
                std::cout << "Teleporting to (" << x << ", " << y << ", " << z << ")" << std::endl;
                // 实际实现中会移动玩家
                return true;
            } catch (const std::exception&) {
                std::cout << "Invalid coordinates" << std::endl;
                return false;
            }
        }, "Teleport to coordinates (teleport <x> <y> <z>)");
        
        // 生成物品
        RegisterCheat("give", [](const std::vector<std::string>& args) {
            if (args.empty()) {
                std::cout << "Usage: give <item> [count]" << std::endl;
                return false;
            }
            
            std::string item = args[0];
            int count = 1;
            
            if (args.size() > 1) {
                try {
                    count = std::stoi(args[1]);
                } catch (const std::exception&) {
                    count = 1;
                }
            }
            
            std::cout << "Giving " << count << " " << item << "(s)" << std::endl;
            // 实际实现中会添加物品到玩家库存
            return true;
        }, "Give items to player (give <item> [count])");
    }
};

9.7 屏幕截图及录像

9.7.1 屏幕截图功能

屏幕截图功能允许开发者在任何时刻捕获游戏画面,对于记录BUG、制作文档和分享成果非常有用。

cpp

#include <string>
#include <vector>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <filesystem>

// 简化的屏幕截图管理器
class ScreenshotManager {
public:
    static ScreenshotManager& GetInstance() {
        static ScreenshotManager instance;
        return instance;
    }
    
    // 捕获屏幕截图
    bool CaptureScreenshot(const std::string& filename = "") {
        // 生成文件名(如果未提供)
        std::string outputFilename = filename;
        if (outputFilename.empty()) {
            outputFilename = GenerateScreenshotFilename();
        }
        
        // 确保目录存在
        std::filesystem::path filePath(outputFilename);
        if (!filePath.parent_path().empty()) {
            std::filesystem::create_directories(filePath.parent_path());
        }
        
        // 在实际实现中,这里会从渲染系统获取当前帧缓冲并保存为图像
        // 这里仅模拟过程
        std::cout << "Capturing screenshot to: " << outputFilename << std::endl;
        
        // 记录截图历史
        screenshotHistory_.push_back(outputFilename);
        
        return true;
    }
    
    // 设置截图保存路径
    void SetScreenshotPath(const std::string& path) {
        screenshotPath_ = path;
        
        // 确保路径存在
        if (!screenshotPath_.empty()) {
            std::filesystem::create_directories(screenshotPath_);
        }
    }
    
    // 获取截图历史
    const std::vector<std::string>& GetScreenshotHistory() const {
        return screenshotHistory_;
    }
    
private:
    ScreenshotManager() : screenshotPath_("screenshots/") {}
    
    ~ScreenshotManager() = default;
    ScreenshotManager(const ScreenshotManager&) = delete;
    ScreenshotManager& operator=(const ScreenshotManager&) = delete;
    
    std::string screenshotPath_;
    std::vector<std::string> screenshotHistory_;
    
    // 生成唯一的截图文件名
    std::string GenerateScreenshotFilename() {
        auto now = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);
        
        std::stringstream ss;
        ss << screenshotPath_;
        ss << "screenshot_";
        ss << std::put_time(std::localtime(&time), "%Y%m%d_%H%M%S");
        ss << ".png";
        
        return ss.str();
    }
};

9.7.2 游戏录像功能

游戏录像功能能够记录一段时间内的游戏画面,对于捕获难以复现的BUG和制作演示视频尤为重要。

cpp

// 简化的游戏录像管理器
class GameRecorder {
public:
    static GameRecorder& GetInstance() {
        static GameRecorder instance;
        return instance;
    }
    
    // 开始录制
    bool StartRecording(const std::string& filename = "", int frameRate = 30, int quality = 75) {
        if (isRecording_) {
            std::cout << "Already recording" << std::endl;
            return false;
        }
        
        // 生成文件名(如果未提供)
        std::string outputFilename = filename;
        if (outputFilename.empty()) {
            outputFilename = GenerateRecordingFilename();
        }
        
        // 确保目录存在
        std::filesystem::path filePath(outputFilename);
        if (!filePath.parent_path().empty()) {
            std::filesystem::create_directories(filePath.parent_path());
        }
        
        // 在实际实现中,这里会初始化视频编码器和输出文件
        std::cout << "Starting recording to: " << outputFilename 
                  << " (FPS: " << frameRate << ", Quality: " << quality << ")" << std::endl;
        
        // 设置录制参数
        currentRecording_.filename = outputFilename;
        currentRecording_.frameRate = frameRate;
        currentRecording_.quality = quality;
        currentRecording_.startTime = std::chrono::system_clock::now();
        currentRecording_.frameCount = 0;
        
        isRecording_ = true;
        return true;
    }
    
    // 停止录制
    bool StopRecording() {
        if (!isRecording_) {
            std::cout << "Not recording" << std::endl;
            return false;
        }
        
        // 在实际实现中,这里会完成视频编码并关闭输出文件
        auto endTime = std::chrono::system_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::seconds>(
            endTime - currentRecording_.startTime).count();
        
        std::cout << "Stopping recording. " 
                  << "Duration: " << duration << "s, "
                  << "Frames: " << currentRecording_.frameCount << std::endl;
        
        // 记录录像历史
        recordingHistory_.push_back(currentRecording_);
        
        isRecording_ = false;
        return true;
    }
    
    // 捕获当前帧
    void CaptureFrame() {
        if (!isRecording_) return;
        
        // 在实际实现中,这里会从渲染系统获取当前帧并添加到视频中
        currentRecording_.frameCount++;
        
        // 显示录制状态(每30帧更新一次)
        if (currentRecording_.frameCount % 30 == 0) {
            auto now = std::chrono::system_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::seconds>(
                now - currentRecording_.startTime).count();
            
            std::cout << "Recording: " << duration << "s, "
                      << "Frames: " << currentRecording_.frameCount << std::endl;
        }
    }
    
    // 检查是否正在录制
    bool IsRecording() const {
        return isRecording_;
    }
    
    // 设置录制保存路径
    void SetRecordingPath(const std::string& path) {
        recordingPath_ = path;
        
        // 确保路径存在
        if (!recordingPath_.empty()) {
            std::filesystem::create_directories(recordingPath_);
        }
    }
    
    // 获取录制历史
    const std::vector<RecordingInfo>& GetRecordingHistory() const {
        return recordingHistory_;
    }
    
private:
    struct RecordingInfo {
        std::string filename;
        int frameRate;
        int quality;
        std::chrono::system_clock::time_point startTime;
        int frameCount;
    };
    
    GameRecorder() : recordingPath_("recordings/"), isRecording_(false) {}
    
    ~GameRecorder() {
        // 确保停止录制
        if (isRecording_) {
            StopRecording();
        }
    }
    
    GameRecorder(const GameRecorder&) = delete;
    GameRecorder& operator=(const GameRecorder&) = delete;
    
    std::string recordingPath_;
    bool isRecording_;
    RecordingInfo currentRecording_;
    std::vector<RecordingInfo> recordingHistory_;
    
    // 生成唯一的录像文件名
    std::string GenerateRecordingFilename() {
        auto now = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);
        
        std::stringstream ss;
        ss << recordingPath_;
        ss << "recording_";
        ss << std::put_time(std::localtime(&time), "%Y%m%d_%H%M%S");
        ss << ".mp4";
        
        return ss.str();
    }
};

9.8 游戏内置性能剖析

9.8.1 性能剖析的重要性

性能剖析是游戏优化的基础,通过精确测量各模块的执行时间和资源使用情况,可以找出性能瓶颈并进行针对性优化。

9.8.2 剖析系统设计

一个完善的性能剖析系统应具备以下特性:

低开销:剖析系统本身不应成为性能瓶颈
多层次:支持函数级、区块级和系统级剖析
可视化:提供直观的性能数据展示
记录功能:能够保存剖析数据供后续分析
多维度:不仅测量时间,还能监控内存和GPU等资源使用

9.8.3 剖析系统实现

cpp

#include <string>
#include <chrono>
#include <vector>
#include <unordered_map>
#include <stack>
#include <mutex>
#include <fstream>
#include <algorithm>
#include <iostream>
#include <thread>
#include <iomanip>

// 性能计数器类型
enum class ProfilerCounterType {
    CPU,     // CPU时间
    GPU,     // GPU时间
    Memory,  // 内存使用
    Draw,    // 绘制调用次数
    Custom   // 自定义计数
};

// 性能采样点
struct ProfilerSample {
    std::string name;
    ProfilerCounterType type;
    uint64_t startTime;
    uint64_t endTime;
    uint64_t value;        // 对于Memory和Custom等非时间计数器
    std::thread::id threadId;
    
    ProfilerSample(const std::string& n, ProfilerCounterType t)
        : name(n), type(t), startTime(0), endTime(0), value(0),
          threadId(std::this_thread::get_id()) {}
};

// 性能计数器
struct ProfilerCounter {
    std::string name;
    ProfilerCounterType type;
    uint64_t totalTime;     // 累计时间(对于CPU和GPU类型)
    uint64_t callCount;     // 调用次数
    uint64_t minTime;       // 最小执行时间
    uint64_t maxTime;       // 最大执行时间
    uint64_t lastValue;     // 最后一次值(对于Memory和Custom类型)
    
    ProfilerCounter(const std::string& n, ProfilerCounterType t)
        : name(n), type(t), totalTime(0), callCount(0),
          minTime(UINT64_MAX), maxTime(0), lastValue(0) {}
    
    // 添加样本
    void AddSample(const ProfilerSample& sample) {
        callCount++;
        
        if (type == ProfilerCounterType::CPU || type == ProfilerCounterType::GPU) {
            uint64_t time = sample.endTime - sample.startTime;
            totalTime += time;
            minTime = std::min(minTime, time);
            maxTime = std::max(maxTime, time);
        } else {
            lastValue = sample.value;
        }
    }
    
    // 获取平均时间
    double GetAverageTime() const {
        if (callCount == 0) return 0.0;
        return static_cast<double>(totalTime) / callCount;
    }
    
    // 重置计数器
    void Reset() {
        totalTime = 0;
        callCount = 0;
        minTime = UINT64_MAX;
        maxTime = 0;
    }
};

// 性能剖析器
class Profiler {
public:
    static Profiler& GetInstance() {
        static Profiler instance;
        return instance;
    }
    
    // 开始一个性能采样
    void BeginSample(const std::string& name, ProfilerCounterType type = ProfilerCounterType::CPU) {
        if (!enabled_) return;
        
        std::lock_guard<std::mutex> lock(mutex_);
        
        // 创建新样本
        ProfilerSample sample(name, type);
        sample.startTime = GetTimestamp();
        
        // 添加到活动样本栈
        auto& threadSamples = activeSamples_[std::this_thread::get_id()];
        threadSamples.push(sample);
    }
    
    // 结束一个性能采样
    void EndSample(const std::string& name, uint64_t customValue = 0) {
        if (!enabled_) return;
        
        std::lock_guard<std::mutex> lock(mutex_);
        
        auto& threadSamples = activeSamples_[std::this_thread::get_id()];
        
        if (threadSamples.empty()) {
            std::cerr << "Profiler::EndSample called without matching BeginSample: " << name << std::endl;
            return;
        }
        
        // 获取栈顶样本
        ProfilerSample sample = threadSamples.top();
        threadSamples.pop();
        
        // 检查名称匹配
        if (sample.name != name) {
            std::cerr << "Profiler::EndSample name mismatch: expected " 
                      << sample.name << ", got " << name << std::endl;
            return;
        }
        
        // 更新样本结束时间
        sample.endTime = GetTimestamp();
        if (sample.type == ProfilerCounterType::Memory || 
            sample.type == ProfilerCounterType::Custom) {
            sample.value = customValue;
        }
        
        // 添加到完成样本列表
        completedSamples_.push_back(sample);
        
        // 更新计数器
        auto& counter = counters_[name];
        if (counter == nullptr) {
            counter = std::make_unique<ProfilerCounter>(name, sample.type);
        }
        counter->AddSample(sample);
    }
    
    // 记录单个性能事件(无需成对调用)
    void RecordEvent(const std::string& name, uint64_t value, 
                    ProfilerCounterType type = ProfilerCounterType::Custom) {
        if (!enabled_) return;
        
        std::lock_guard<std::mutex> lock(mutex_);
        
        // 创建样本
        ProfilerSample sample(name, type);
        sample.startTime = GetTimestamp();
        sample.endTime = sample.startTime;
        sample.value = value;
        
        // 添加到完成样本列表
        completedSamples_.push_back(sample);
        
        // 更新计数器
        auto& counter = counters_[name];
        if (counter == nullptr) {
            counter = std::make_unique<ProfilerCounter>(name, type);
        }
        counter->AddSample(sample);
    }
    
    // 启用/禁用剖析
    void SetEnabled(bool enabled) {
        enabled_ = enabled;
        
        if (!enabled_) {
            // 清理活动样本
            std::lock_guard<std::mutex> lock(mutex_);
            activeSamples_.clear();
        }
    }
    
    bool IsEnabled() const {
        return enabled_;
    }
    
    // 重置所有计数器
    void Reset() {
        std::lock_guard<std::mutex> lock(mutex_);
        
        for (auto& pair : counters_) {
            pair.second->Reset();
        }
        
        completedSamples_.clear();
        activeSamples_.clear();
    }
    
    // 保存性能剖析报告
    bool SaveReport(const std::string& filename) {
        std::lock_guard<std::mutex> lock(mutex_);
        
        std::ofstream file(filename);
        if (!file.is_open()) {
            std::cerr << "Failed to open profiler report file: " << filename << std::endl;
            return false;
        }
        
        // 写入HTML报告头
        file << "<!DOCTYPE html>
"
             << "<html>
"
             << "<head>
"
             << "  <title>Performance Profiler Report</title>
"
             << "  <style>
"
             << "    body { font-family: Arial, sans-serif; margin: 20px; }
"
             << "    table { border-collapse: collapse; width: 100%; }
"
             << "    th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
"
             << "    th { background-color: #f2f2f2; }
"
             << "    tr:nth-child(even) { background-color: #f9f9f9; }
"
             << "    .cpu { color: #1a73e8; }
"
             << "    .gpu { color: #d93025; }
"
             << "    .memory { color: #188038; }
"
             << "    .draw { color: #c26401; }
"
             << "    .custom { color: #9334e6; }
"
             << "  </style>
"
             << "</head>
"
             << "<body>
"
             << "  <h1>Performance Profiler Report</h1>
";
        
        // 写入计数器表格
        file << "  <h2>Performance Counters</h2>
"
             << "  <table>
"
             << "    <tr>
"
             << "      <th>Name</th>
"
             << "      <th>Type</th>
"
             << "      <th>Calls</th>
"
             << "      <th>Total Time (ms)</th>
"
             << "      <th>Avg Time (ms)</th>
"
             << "      <th>Min Time (ms)</th>
"
             << "      <th>Max Time (ms)</th>
"
             << "      <th>Last Value</th>
"
             << "    </tr>
";
        
        // 收集计数器数据
        std::vector<std::pair<std::string, std::unique_ptr<ProfilerCounter>*>> sortedCounters;
        for (auto& pair : counters_) {
            sortedCounters.push_back({pair.first, &pair.second});
        }
        
        // 按总时间降序排序
        std::sort(sortedCounters.begin(), sortedCounters.end(),
            [](const auto& a, const auto& b) {
                return (*a.second)->totalTime > (*b.second)->totalTime;
            });
        
        // 写入计数器数据
        for (const auto& pair : sortedCounters) {
            const auto& counter = *pair.second;
            
            std::string typeClass;
            std::string typeName;
            
            switch (counter->type) {
                case ProfilerCounterType::CPU:
                    typeClass = "cpu";
                    typeName = "CPU";
                    break;
                case ProfilerCounterType::GPU:
                    typeClass = "gpu";
                    typeName = "GPU";
                    break;
                case ProfilerCounterType::Memory:
                    typeClass = "memory";
                    typeName = "Memory";
                    break;
                case ProfilerCounterType::Draw:
                    typeClass = "draw";
                    typeName = "Draw";
                    break;
                case ProfilerCounterType::Custom:
                    typeClass = "custom";
                    typeName = "Custom";
                    break;
            }
            
            file << "    <tr>
"
                 << "      <td>" << counter->name << "</td>
"
                 << "      <td class="" << typeClass << "">" << typeName << "</td>
"
                 << "      <td>" << counter->callCount << "</td>
";
            
            if (counter->type == ProfilerCounterType::CPU || counter->type == ProfilerCounterType::GPU) {
                file << "      <td>" << (counter->totalTime / 1000.0) << "</td>
"
                     << "      <td>" << (counter->GetAverageTime() / 1000.0) << "</td>
"
                     << "      <td>" << (counter->minTime == UINT64_MAX ? 0 : counter->minTime / 1000.0) << "</td>
"
                     << "      <td>" << (counter->maxTime / 1000.0) << "</td>
"
                     << "      <td>-</td>
";
            } else {
                file << "      <td>-</td>
"
                     << "      <td>-</td>
"
                     << "      <td>-</td>
"
                     << "      <td>-</td>
"
                     << "      <td>" << counter->lastValue << "</td>
";
            }
            
            file << "    </tr>
";
        }
        
        file << "  </table>
";
        
        // 生成时间线图表
        file << "  <h2>Execution Timeline</h2>
"
             << "  <div id="timeline" style="height: 500px;"></div>
"
             << "  <script src="https://www.gstatic.com/charts/loader.js"></script>
"
             << "  <script>
"
             << "    google.charts.load('current', {'packages':['timeline']});
"
             << "    google.charts.setOnLoadCallback(drawChart);
"
             << "    function drawChart() {
"
             << "      var container = document.getElementById('timeline');
"
             << "      var chart = new google.visualization.Timeline(container);
"
             << "      var dataTable = new google.visualization.DataTable();
"
             << "      dataTable.addColumn({ type: 'string', id: 'Thread' });
"
             << "      dataTable.addColumn({ type: 'string', id: 'Name' });
"
             << "      dataTable.addColumn({ type: 'number', id: 'Start' });
"
             << "      dataTable.addColumn({ type: 'number', id: 'End' });
"
             << "      dataTable.addRows([
";
        
        // 准备时间线数据
        if (!completedSamples_.empty()) {
            // 找到最早的样本开始时间作为基准
            uint64_t baseTime = completedSamples_[0].startTime;
            for (const auto& sample : completedSamples_) {
                if (sample.startTime < baseTime) {
                    baseTime = sample.startTime;
                }
            }
            
            // 添加样本数据
            for (const auto& sample : completedSamples_) {
                if (sample.type == ProfilerCounterType::CPU || sample.type == ProfilerCounterType::GPU) {
                    file << "        ['Thread " << sample.threadId << "', '" 
                         << sample.name << "', " 
                         << ((sample.startTime - baseTime) / 1000.0) << ", "
                         << ((sample.endTime - baseTime) / 1000.0) << "],
";
                }
            }
        }
        
        file << "      ]);
"
             << "      var options = {
"
             << "        timeline: { colorByRowLabel: true }
"
             << "      };
"
             << "      chart.draw(dataTable, options);
"
             << "    }
"
             << "  </script>
";
        
        // 写入HTML尾
        file << "</body>
"
             << "</html>
";
        
        file.close();
        
        std::cout << "Profiler report saved to: " << filename << std::endl;
        return true;
    }
    
    // 打印性能报告到控制台
    void PrintReport() {
        std::lock_guard<std::mutex> lock(mutex_);
        
        std::cout << "==== Performance Profiler Report ====" << std::endl;
        std::cout << std::left << std::setw(30) << "Name" 
                  << std::setw(10) << "Type"
                  << std::setw(10) << "Calls"
                  << std::setw(15) << "Total Time (ms)"
                  << std::setw(15) << "Avg Time (ms)"
                  << std::setw(15) << "Min Time (ms)"
                  << std::setw(15) << "Max Time (ms)"
                  << std::setw(15) << "Last Value"
                  << std::endl;
        
        std::cout << std::string(125, '-') << std::endl;
        
        // 收集计数器数据
        std::vector<std::pair<std::string, std::unique_ptr<ProfilerCounter>*>> sortedCounters;
        for (auto& pair : counters_) {
            sortedCounters.push_back({pair.first, &pair.second});
        }
        
        // 按总时间降序排序
        std::sort(sortedCounters.begin(), sortedCounters.end(),
            [](const auto& a, const auto& b) {
                return (*a.second)->totalTime > (*b.second)->totalTime;
            });
        
        // 打印计数器数据
        for (const auto& pair : sortedCounters) {
            const auto& counter = *pair.second;
            
            std::string typeName;
            switch (counter->type) {
                case ProfilerCounterType::CPU:    typeName = "CPU"; break;
                case ProfilerCounterType::GPU:    typeName = "GPU"; break;
                case ProfilerCounterType::Memory: typeName = "Memory"; break;
                case ProfilerCounterType::Draw:   typeName = "Draw"; break;
                case ProfilerCounterType::Custom: typeName = "Custom"; break;
            }
            
            std::cout << std::left << std::setw(30) << counter->name 
                      << std::setw(10) << typeName
                      << std::setw(10) << counter->callCount;
            
            if (counter->type == ProfilerCounterType::CPU || counter->type == ProfilerCounterType::GPU) {
                std::cout << std::setw(15) << std::fixed << std::setprecision(3) << (counter->totalTime / 1000.0)
                          << std::setw(15) << std::fixed << std::setprecision(3) << (counter->GetAverageTime() / 1000.0)
                          << std::setw(15) << std::fixed << std::setprecision(3) << (counter->minTime == UINT64_MAX ? 0 : counter->minTime / 1000.0)
                          << std::setw(15) << std::fixed << std::setprecision(3) << (counter->maxTime / 1000.0)
                          << std::setw(15) << "-";
            } else {
                std::cout << std::setw(15) << "-"
                          << std::setw(15) << "-"
                          << std::setw(15) << "-"
                          << std::setw(15) << "-"
                          << std::setw(15) << counter->lastValue;
            }
            
            std::cout << std::endl;
        }
        
        std::cout << std::string(125, '-') << std::endl;
        std::cout << "Total samples: " << completedSamples_.size() << std::endl;
        std::cout << "=================================" << std::endl;
    }
    
private:
    Profiler() : enabled_(false) {}
    
    ~Profiler() = default;
    Profiler(const Profiler&) = delete;
    Profiler& operator=(const Profiler&) = delete;
    
    // 获取当前高精度时间戳(微秒)
    uint64_t GetTimestamp() {
        auto now = std::chrono::high_resolution_clock::now();
        return std::chrono::duration_cast<std::chrono::microseconds>(
            now.time_since_epoch()).count();
    }
    
    bool enabled_;
    std::mutex mutex_;
    std::unordered_map<std::thread::id, std::stack<ProfilerSample>> activeSamples_;
    std::vector<ProfilerSample> completedSamples_;
    std::unordered_map<std::string, std::unique_ptr<ProfilerCounter>> counters_;
};

// 辅助宏,用于简化代码中的性能剖析
#define PROFILE_SCOPE(name) 
    ProfileScope profileScope##__LINE__(name)

#define PROFILE_FUNCTION() 
    ProfileScope profileFunction(__FUNCTION__)

// 作用域性能剖析辅助类
class ProfileScope {
public:
    ProfileScope(const std::string& name)
        : name_(name) {
        Profiler::GetInstance().BeginSample(name_);
    }
    
    ~ProfileScope() {
        Profiler::GetInstance().EndSample(name_);
    }
    
private:
    std::string name_;
};

// 使用示例
void profileExample() {
    auto& profiler = Profiler::GetInstance();
    profiler.SetEnabled(true);
    
    {
        PROFILE_FUNCTION();
        
        // 模拟一些工作
        {
            PROFILE_SCOPE("Initialization");
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        
        // 模拟渲染
        {
            PROFILE_SCOPE("Rendering");
            
            {
                PROFILE_SCOPE("UpdateScene");
                std::this_thread::sleep_for(std::chrono::milliseconds(5));
            }
            
            {
                PROFILE_SCOPE("DrawGeometry");
                profiler.RecordEvent("DrawCalls", 1250, ProfilerCounterType::Draw);
                std::this_thread::sleep_for(std::chrono::milliseconds(15));
            }
            
            {
                PROFILE_SCOPE("PostProcessing");
                std::this_thread::sleep_for(std::chrono::milliseconds(8));
            }
        }
        
        // 模拟物理更新
        {
            PROFILE_SCOPE("Physics");
            std::this_thread::sleep_for(std::chrono::milliseconds(12));
        }
        
        // 记录内存使用
        profiler.RecordEvent("TotalMemory", 1024 * 1024 * 125, ProfilerCounterType::Memory);
    }
    
    // 打印报告
    profiler.PrintReport();
    
    // 保存HTML报告
    profiler.SaveReport("profiler_report.html");
}

9.9 游戏内置的内存统计和泄漏检测

9.9.1 内存管理的重要性

在游戏引擎中,有效的内存管理至关重要,尤其是在资源受限的平台上。内存泄漏和碎片化会导致性能下降、崩溃和不稳定性。

9.9.2 内存跟踪系统设计

一个完善的内存跟踪系统应具备以下特性:

低开销:内存跟踪不应显著影响性能
分类统计:按系统、资源类型等分类统计内存使用
泄漏检测:能够检测和定位内存泄漏
分配追踪:记录内存分配的源代码位置
可视化:提供内存使用的可视化展示

9.9.3 内存跟踪系统实现

cpp

#include <cstddef>
#include <string>
#include <unordered_map>
#include <vector>
#include <mutex>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <atomic>
#include <cstring>
#include <cassert>

// 内存分配追踪信息
struct AllocationInfo {
    void* address;
    size_t size;
    const char* file;
    int line;
    const char* function;
    const char* tag;
    uint64_t timestamp;
    uint32_t threadId;
    
    AllocationInfo(void* addr, size_t sz, const char* f, int ln, 
                  const char* func, const char* t, uint64_t ts, uint32_t tid)
        : address(addr), size(sz), file(f), line(ln), 
          function(func), tag(t), timestamp(ts), threadId(tid) {}
};

// 内存分配标签
enum class MemoryTag {
    General,
    Rendering,
    Physics,
    Audio,
    AI,
    Scripting,
    UI,
    Networking,
    Assets,
    Debug,
    Custom
};

// 将内存标签转换为字符串
const char* MemoryTagToString(MemoryTag tag) {
    static const char* tagNames[] = {
        "General", "Rendering", "Physics", "Audio", "AI", 
        "Scripting", "UI", "Networking", "Assets", "Debug", "Custom"
    };
    return tagNames[static_cast<int>(tag)];
}

// 内存追踪器
class MemoryTracker {
public:
    static MemoryTracker& GetInstance() {
        static MemoryTracker instance;
        return instance;
    }
    
    // 注册内存分配
    void* TrackAllocation(size_t size, const char* file, int line, 
                         const char* function, MemoryTag tag = MemoryTag::General) {
        // 实际分配内存
        void* ptr = malloc(size);
        if (!ptr) return nullptr;
        
        if (!enabled_) return ptr;
        
        std::lock_guard<std::mutex> lock(mutex_);
        
        // 获取当前时间戳
        uint64_t timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::steady_clock::now().time_since_epoch()).count();
        
        // 获取线程ID
        uint32_t threadId = static_cast<uint32_t>(
            std::hash<std::thread::id>{}(std::this_thread::get_id()));
        
        // 记录分配信息
        allocations_[ptr] = AllocationInfo(
            ptr, size, file, line, function, 
            MemoryTagToString(tag), timestamp, threadId);
        
        // 更新统计数据
        totalAllocated_ += size;
        allocatedByTag_[static_cast<int>(tag)] += size;
        allocationCount_++;
        
        return ptr;
    }
    
    // 注销内存分配
    void TrackDeallocation(void* ptr) {
        if (!ptr) return;
        
        if (enabled_) {
            std::lock_guard<std::mutex> lock(mutex_);
            
            auto it = allocations_.find(ptr);
            if (it != allocations_.end()) {
                // 更新统计数据
                size_t size = it->second.size;
                totalAllocated_ -= size;
                
                const char* tag = it->second.tag;
                int tagIndex = -1;
                
                for (int i = 0; i < static_cast<int>(MemoryTag::Custom) + 1; ++i) {
                    if (strcmp(tag, MemoryTagToString(static_cast<MemoryTag>(i))) == 0) {
                        tagIndex = i;
                        break;
                    }
                }
                
                if (tagIndex >= 0) {
                    allocatedByTag_[tagIndex] -= size;
                }
                
                // 从映射表中移除
                allocations_.erase(it);
                
                // 记录历史
                if (recordHistory_) {
                    deallocations_.push_back(ptr);
                    if (deallocations_.size() > maxHistorySize_) {
                        deallocations_.erase(deallocations_.begin());
                    }
                }
            } else {
                // 试图释放未跟踪的内存,可能是双重释放或其他错误
                std::cerr << "WARNING: Attempting to free untracked memory at " 
                          << ptr << std::endl;
            }
        }
        
        // 实际释放内存
        free(ptr);
    }
    
    // 检查内存泄漏
    void CheckLeaks() {
        std::lock_guard<std::mutex> lock(mutex_);
        
        if (allocations_.empty()) {
            std::cout << "No memory leaks detected." << std::endl;
            return;
        }
        
        std::cout << "Memory Leak Report:" << std::endl;
        std::cout << "===================" << std::endl;
        std::cout << "Found " << allocations_.size() << " memory leaks totaling " 
                  << FormatSize(totalAllocated_) << std::endl;
        std::cout << std::endl;
        
        std::cout << std::left << std::setw(16) << "Address" 
                  << std::setw(12) << "Size" 
                  << std::setw(20) << "Allocation Time" 
                  << std::setw(20) << "Tag" 
                  << std::setw(30) << "Function" 
                  << std::setw(30) << "File:Line" 
                  << std::endl;
        
        std::cout << std::string(128, '-') << std::endl;
        
        // 按大小排序泄漏
        std::vector<std::pair<void*, AllocationInfo*>> leaks;
        for (auto& pair : allocations_) {
            leaks.push_back({pair.first, &pair.second});
        }
        
        std::sort(leaks.begin(), leaks.end(), [](const auto& a, const auto& b) {
            return a.second->size > b.second->size;
        });
        
        // 打印泄漏详情
        for (const auto& leak : leaks) {
            const AllocationInfo& info = *leak.second;
            
            std::cout << std::left << std::setw(16) << info.address 
                      << std::setw(12) << FormatSize(info.size) 
                      << std::setw(20) << FormatTimestamp(info.timestamp) 
                      << std::setw(20) << info.tag 
                      << std::setw(30) << info.function 
                      << std::setw(30) << (std::string(info.file) + ":" + std::to_string(info.line)) 
                      << std::endl;
        }
        
        std::cout << std::string(128, '-') << std::endl;
        
        // 按标签统计泄漏
        std::unordered_map<std::string, std::pair<size_t, size_t>> leaksByTag;
        
        for (const auto& leak : leaks) {
            const AllocationInfo& info = *leak.second;
            auto& stats = leaksByTag[info.tag];
            stats.first += info.size;
            stats.second++;
        }
        
        std::cout << "Leaks by tag:" << std::endl;
        
        for (const auto& pair : leaksByTag) {
            std::cout << "  " << std::left << std::setw(15) << pair.first 
                      << ": " << std::setw(12) << FormatSize(pair.first.size()) 
                      << " in " << pair.second.second << " allocations" << std::endl;
        }
    }
    
    // 生成内存报告
    void GenerateReport(const std::string& filename = "") {
        std::lock_guard<std::mutex> lock(mutex_);
        
        if (filename.empty()) {
            // 打印到控制台
            PrintMemoryStats();
        } else {
            // 保存到文件
            SaveMemoryStatsToFile(filename);
        }
    }
    
    // 启用/禁用内存追踪
    void SetEnabled(bool enabled) {
        enabled_ = enabled;
    }
    
    bool IsEnabled() const {
        return enabled_;
    }
    
    // 启用/禁用历史记录
    void SetRecordHistory(bool record) {
        recordHistory_ = record;
    }
    
    // 设置历史记录大小限制
    void SetMaxHistorySize(size_t size) {
        maxHistorySize_ = size;
    }
    
    // 获取总分配内存
    size_t GetTotalAllocated() const {
        return totalAllocated_;
    }
    
    // 获取分配计数
    size_t GetAllocationCount() const {
        return allocationCount_;
    }
    
    // 获取指定标签的已分配内存
    size_t GetAllocatedByTag(MemoryTag tag) const {
        return allocatedByTag_[static_cast<int>(tag)];
    }
    
private:
    MemoryTracker() 
        : enabled_(false), recordHistory_(false), maxHistorySize_(1000),
          totalAllocated_(0), allocationCount_(0) {
        // 初始化标签统计
        for (int i = 0; i < static_cast<int>(MemoryTag::Custom) + 1; ++i) {
            allocatedByTag_[i] = 0;
        }
    }
    
    ~MemoryTracker() {
        // 检查泄漏
        if (!allocations_.empty()) {
            std::cerr << "WARNING: " << allocations_.size() 
                      << " memory leaks detected at program exit." << std::endl;
        }
    }
    
    MemoryTracker(const MemoryTracker&) = delete;
    MemoryTracker& operator=(const MemoryTracker&) = delete;
    
    // 格式化大小为可读字符串
    std::string FormatSize(size_t size) const {
        const char* units[] = {"B", "KB", "MB", "GB", "TB"};
        int unitIndex = 0;
        double adjustedSize = static_cast<double>(size);
        
        while (adjustedSize >= 1024.0 && unitIndex < 4) {
            adjustedSize /= 1024.0;
            unitIndex++;
        }
        
        std::stringstream ss;
        ss << std::fixed << std::setprecision(2) << adjustedSize << " " << units[unitIndex];
        return ss.str();
    }
    
    // 格式化时间戳
    std::string FormatTimestamp(uint64_t timestamp) const {
        std::time_t time = timestamp / 1000;
        std::stringstream ss;
        ss << std::put_time(std::localtime(&time), "%H:%M:%S");
        ss << "." << std::setfill('0') << std::setw(3) << (timestamp % 1000);
        return ss.str();
    }
    
    // 打印内存统计
    void PrintMemoryStats() {
        std::cout << "Memory Usage Report:" << std::endl;
        std::cout << "====================" << std::endl;
        std::cout << "Total allocated: " << FormatSize(totalAllocated_) 
                  << " in " << allocationCount_ << " allocations" << std::endl;
        std::cout << std::endl;
        
        std::cout << "Memory by tag:" << std::endl;
        for (int i = 0; i < static_cast<int>(MemoryTag::Custom) + 1; ++i) {
            if (allocatedByTag_[i] > 0) {
                std::cout << "  " << std::left << std::setw(15) << MemoryTagToString(static_cast<MemoryTag>(i)) 
                          << ": " << FormatSize(allocatedByTag_[i]) << std::endl;
            }
        }
        
        std::cout << std::endl;
        
        std::cout << "Top 10 largest allocations:" << std::endl;
        std::cout << std::left << std::setw(16) << "Address" 
                  << std::setw(12) << "Size" 
                  << std::setw(20) << "Tag" 
                  << std::setw(30) << "Function" 
                  << std::setw(30) << "File:Line" 
                  << std::endl;
        
        std::cout << std::string(108, '-') << std::endl;
        
        // 找出最大的10个分配
        std::vector<std::pair<void*, AllocationInfo*>> topAllocations;
        for (auto& pair : allocations_) {
            topAllocations.push_back({pair.first, &pair.second});
        }
        
        std::sort(topAllocations.begin(), topAllocations.end(), [](const auto& a, const auto& b) {
            return a.second->size > b.second->size;
        });
        
        int count = std::min(10, static_cast<int>(topAllocations.size()));
        
        for (int i = 0; i < count; ++i) {
            const AllocationInfo& info = *topAllocations[i].second;
            
            std::cout << std::left << std::setw(16) << info.address 
                      << std::setw(12) << FormatSize(info.size) 
                      << std::setw(20) << info.tag 
                      << std::setw(30) << info.function 
                      << std::setw(30) << (std::string(info.file) + ":" + std::to_string(info.line)) 
                      << std::endl;
        }
    }
    
    // 保存内存统计到文件
    void SaveMemoryStatsToFile(const std::string& filename) {
        std::ofstream file(filename);
        if (!file.is_open()) {
            std::cerr << "Failed to open memory report file: " << filename << std::endl;
            return;
        }
        
        file << "<!DOCTYPE html>
"
             << "<html>
"
             << "<head>
"
             << "  <title>Memory Usage Report</title>
"
             << "  <style>
"
             << "    body { font-family: Arial, sans-serif; margin: 20px; }
"
             << "    table { border-collapse: collapse; width: 100%; }
"
             << "    th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
"
             << "    th { background-color: #f2f2f2; }
"
             << "    tr:nth-child(even) { background-color: #f9f9f9; }
"
             << "  </style>
"
             << "</head>
"
             << "<body>
"
             << "  <h1>Memory Usage Report</h1>
"
             << "  <p><strong>Total allocated:</strong> " << FormatSize(totalAllocated_) 
             << " in " << allocationCount_ << " allocations</p>
";
        
        // 内存标签饼图
        file << "  <h2>Memory by Tag</h2>
"
             << "  <div id="tagChart" style="height: 400px;"></div>
";
        
        // 标签表格
        file << "  <table>
"
             << "    <tr>
"
             << "      <th>Tag</th>
"
             << "      <th>Size</th>
"
             << "      <th>Percentage</th>
"
             << "    </tr>
";
        
        for (int i = 0; i < static_cast<int>(MemoryTag::Custom) + 1; ++i) {
            if (allocatedByTag_[i] > 0) {
                double percentage = (totalAllocated_ > 0) 
                    ? (static_cast<double>(allocatedByTag_[i]) / totalAllocated_ * 100.0) 
                    : 0.0;
                
                file << "    <tr>
"
                     << "      <td>" << MemoryTagToString(static_cast<MemoryTag>(i)) << "</td>
"
                     << "      <td>" << FormatSize(allocatedByTag_[i]) << "</td>
"
                     << "      <td>" << std::fixed << std::setprecision(2) << percentage << "%</td>
"
                     << "    </tr>
";
            }
        }
        
        file << "  </table>
";
        
        // 最大分配
        file << "  <h2>Top 10 Largest Allocations</h2>
"
             << "  <table>


cpp

             << "  <table>
"
             << "    <tr>
"
             << "      <th>Address</th>
"
             << "      <th>Size</th>
"
             << "      <th>Tag</th>
"
             << "      <th>Function</th>
"
             << "      <th>File:Line</th>
"
             << "    </tr>
";
        
        // 找出最大的10个分配
        std::vector<std::pair<void*, AllocationInfo*>> topAllocations;
        for (auto& pair : allocations_) {
            topAllocations.push_back({pair.first, &pair.second});
        }
        
        std::sort(topAllocations.begin(), topAllocations.end(), [](const auto& a, const auto& b) {
            return a.second->size > b.second->size;
        });
        
        int count = std::min(10, static_cast<int>(topAllocations.size()));
        
        for (int i = 0; i < count; ++i) {
            const AllocationInfo& info = *topAllocations[i].second;
            
            file << "    <tr>
"
                 << "      <td>" << info.address << "</td>
"
                 << "      <td>" << FormatSize(info.size) << "</td>
"
                 << "      <td>" << info.tag << "</td>
"
                 << "      <td>" << info.function << "</td>
"
                 << "      <td>" << info.file << ":" << info.line << "</td>
"
                 << "    </tr>
";
        }
        
        file << "  </table>
";
        
        // 添加图表脚本
        file << "  <script src="https://www.gstatic.com/charts/loader.js"></script>
"
             << "  <script>
"
             << "    google.charts.load('current', {'packages':['corechart']});
"
             << "    google.charts.setOnLoadCallback(drawCharts);
"
             << "    function drawCharts() {
"
             << "      // 标签分布图表
"
             << "      var tagData = google.visualization.arrayToDataTable([
"
             << "        ['Tag', 'Size'],
";
        
        for (int i = 0; i < static_cast<int>(MemoryTag::Custom) + 1; ++i) {
            if (allocatedByTag_[i] > 0) {
                file << "        ['" << MemoryTagToString(static_cast<MemoryTag>(i)) << "', " 
                     << allocatedByTag_[i] << "],
";
            }
        }
        
        file << "      ]);
"
             << "      var tagOptions = {
"
             << "        title: 'Memory by Tag',
"
             << "        pieHole: 0.4,
"
             << "      };
"
             << "      var tagChart = new google.visualization.PieChart(document.getElementById('tagChart'));
"
             << "      tagChart.draw(tagData, tagOptions);
"
             << "    }
"
             << "  </script>
"
             << "</body>
"
             << "</html>
";
        
        file.close();
        
        std::cout << "Memory report saved to: " << filename << std::endl;
    }
    
    bool enabled_;
    bool recordHistory_;
    size_t maxHistorySize_;
    std::mutex mutex_;
    std::unordered_map<void*, AllocationInfo> allocations_;
    std::vector<void*> deallocations_;
    size_t totalAllocated_;
    size_t allocationCount_;
    size_t allocatedByTag_[static_cast<int>(MemoryTag::Custom) + 1];
};

// 重载全局new和delete操作符以跟踪所有分配
#ifdef MEMORY_TRACKING_ENABLED

void* operator new(size_t size) {
    return MemoryTracker::GetInstance().TrackAllocation(
        size, "Unknown", 0, "global new", MemoryTag::General);
}

void* operator new[](size_t size) {
    return MemoryTracker::GetInstance().TrackAllocation(
        size, "Unknown", 0, "global new[]", MemoryTag::General);
}

void* operator new(size_t size, const char* file, int line, 
                  const char* function, MemoryTag tag = MemoryTag::General) {
    return MemoryTracker::GetInstance().TrackAllocation(
        size, file, line, function, tag);
}

void* operator new[](size_t size, const char* file, int line, 
                    const char* function, MemoryTag tag = MemoryTag::General) {
    return MemoryTracker::GetInstance().TrackAllocation(
        size, file, line, function, tag);
}

void operator delete(void* ptr) noexcept {
    MemoryTracker::GetInstance().TrackDeallocation(ptr);
}

void operator delete[](void* ptr) noexcept {
    MemoryTracker::GetInstance().TrackDeallocation(ptr);
}

void operator delete(void* ptr, size_t) noexcept {
    MemoryTracker::GetInstance().TrackDeallocation(ptr);
}

void operator delete[](void* ptr, size_t) noexcept {
    MemoryTracker::GetInstance().TrackDeallocation(ptr);
}

// 定义跟踪宏
#define new new(__FILE__, __LINE__, __FUNCTION__)

#endif // MEMORY_TRACKING_ENABLED

// 内存分配器接口
class Allocator {
public:
    virtual ~Allocator() = default;
    
    virtual void* Allocate(size_t size, size_t alignment = alignof(std::max_align_t)) = 0;
    virtual void Deallocate(void* ptr) = 0;
    
    virtual size_t GetAllocatedSize() const = 0;
    virtual size_t GetTotalSize() const = 0;
};

// 线性分配器(栈式分配)
class LinearAllocator : public Allocator {
public:
    LinearAllocator(size_t size)
        : totalSize_(size), allocatedSize_(0) {
        buffer_ = static_cast<uint8_t*>(malloc(size));
        if (!buffer_) {
            throw std::bad_alloc();
        }
        current_ = buffer_;
    }
    
    ~LinearAllocator() override {
        free(buffer_);
    }
    
    void* Allocate(size_t size, size_t alignment = alignof(std::max_align_t)) override {
        // 计算对齐地址
        uintptr_t currentPtr = reinterpret_cast<uintptr_t>(current_);
        uintptr_t alignedPtr = (currentPtr + alignment - 1) & ~(alignment - 1);
        uintptr_t adjustment = alignedPtr - currentPtr;
        
        // 检查是否有足够空间
        if (allocatedSize_ + adjustment + size > totalSize_) {
            return nullptr; // 内存不足
        }
        
        // 更新当前指针
        current_ = reinterpret_cast<uint8_t*>(alignedPtr);
        void* result = current_;
        
        // 前进size字节
        current_ += size;
        allocatedSize_ = current_ - buffer_;
        
        return result;
    }
    
    void Deallocate(void* ptr) override {
        // 线性分配器不支持单独释放
        // 只能通过Reset一次性释放所有内存
    }
    
    void Reset() {
        current_ = buffer_;
        allocatedSize_ = 0;
    }
    
    size_t GetAllocatedSize() const override {
        return allocatedSize_;
    }
    
    size_t GetTotalSize() const override {
        return totalSize_;
    }
    
private:
    uint8_t* buffer_;
    uint8_t* current_;
    size_t totalSize_;
    size_t allocatedSize_;
};

// 池分配器(固定大小对象)
class PoolAllocator : public Allocator {
public:
    PoolAllocator(size_t objectSize, size_t objectCount, size_t alignment = alignof(std::max_align_t))
        : objectSize_(objectSize), objectCount_(objectCount), freeBlocks_(0),
          alignment_(alignment), allocatedBlocks_(0) {
        // 确保对象大小至少能容纳一个指针
        objectSize_ = std::max(objectSize_, sizeof(void*));
        
        // 计算块大小(考虑对齐)
        blockSize_ = (objectSize_ + alignment_ - 1) & ~(alignment_ - 1);
        
        // 分配内存池
        poolSize_ = blockSize_ * objectCount_;
        pool_ = static_cast<uint8_t*>(malloc(poolSize_));
        if (!pool_) {
            throw std::bad_alloc();
        }
        
        // 初始化自由块链表
        Reset();
    }
    
    ~PoolAllocator() override {
        free(pool_);
    }
    
    void* Allocate(size_t size, size_t alignment = alignof(std::max_align_t)) override {
        // 检查请求大小是否适合对象大小
        if (size > objectSize_ || alignment > alignment_) {
            return nullptr;
        }
        
        // 检查是否有空闲块
        if (!freeBlocks_) {
            return nullptr;
        }
        
        // 从自由链表中获取一个块
        void* result = freeBlocks_;
        freeBlocks_ = *reinterpret_cast<void**>(freeBlocks_);
        allocatedBlocks_++;
        
        return result;
    }
    
    void Deallocate(void* ptr) override {
        // 检查指针是否在池中
        if (ptr >= pool_ && ptr < pool_ + poolSize_) {
            // 将块放回自由链表
            *reinterpret_cast<void**>(ptr) = freeBlocks_;
            freeBlocks_ = ptr;
            allocatedBlocks_--;
        }
    }
    
    void Reset() {
        // 构建自由块链表
        freeBlocks_ = pool_;
        uint8_t* block = pool_;
        
        for (size_t i = 0; i < objectCount_ - 1; ++i) {
            *reinterpret_cast<void**>(block) = block + blockSize_;
            block += blockSize_;
        }
        
        // 最后一个块的下一个指针为nullptr
        *reinterpret_cast<void**>(block) = nullptr;
        
        allocatedBlocks_ = 0;
    }
    
    size_t GetAllocatedSize() const override {
        return allocatedBlocks_ * blockSize_;
    }
    
    size_t GetTotalSize() const override {
        return poolSize_;
    }
    
private:
    uint8_t* pool_;
    void* freeBlocks_;
    size_t objectSize_;
    size_t blockSize_;
    size_t objectCount_;
    size_t poolSize_;
    size_t alignment_;
    size_t allocatedBlocks_;
};

// 定制内存管理实用函数
namespace MemoryUtils {
    // 分配对齐内存
    void* AlignedAlloc(size_t size, size_t alignment) {
        void* ptr = nullptr;
        
        #ifdef _WIN32
        ptr = _aligned_malloc(size, alignment);
        #else
        if (posix_memalign(&ptr, alignment, size) != 0) {
            ptr = nullptr;
        }
        #endif
        
        return ptr;
    }
    
    // 释放对齐内存
    void AlignedFree(void* ptr) {
        #ifdef _WIN32
        _aligned_free(ptr);
        #else
        free(ptr);
        #endif
    }
    
    // 内存拷贝
    void* MemCopy(void* dest, const void* src, size_t size) {
        return memcpy(dest, src, size);
    }
    
    // 内存移动
    void* MemMove(void* dest, const void* src, size_t size) {
        return memmove(dest, src, size);
    }
    
    // 内存设置
    void* MemSet(void* dest, int value, size_t size) {
        return memset(dest, value, size);
    }
    
    // 内存比较
    int MemCompare(const void* ptr1, const void* ptr2, size_t size) {
        return memcmp(ptr1, ptr2, size);
    }
}

// 使用示例
void memoryTrackingExample() {
    // 启用内存追踪
    MemoryTracker::GetInstance().SetEnabled(true);
    
    // 分配一些内存
    {
        PROFILE_FUNCTION();
        
        // 模拟一些内存分配
        void* p1 = MemoryTracker::GetInstance().TrackAllocation(
            1024, __FILE__, __LINE__, __FUNCTION__, MemoryTag::Rendering);
        
        void* p2 = MemoryTracker::GetInstance().TrackAllocation(
            2048, __FILE__, __LINE__, __FUNCTION__, MemoryTag::Physics);
        
        void* p3 = MemoryTracker::GetInstance().TrackAllocation(
            4096, __FILE__, __LINE__, __FUNCTION__, MemoryTag::Audio);
        
        // 测试线性分配器
        LinearAllocator linearAllocator(1024 * 1024); // 1MB
        
        for (int i = 0; i < 10; ++i) {
            void* p = linearAllocator.Allocate(1024); // 分配1KB
            // 使用内存...
        }
        
        // 重置线性分配器
        linearAllocator.Reset();
        
        // 测试池分配器
        PoolAllocator poolAllocator(64, 100); // 100个64字节对象
        
        std::vector<void*> objects;
        for (int i = 0; i < 50; ++i) {
            void* p = poolAllocator.Allocate(64);
            objects.push_back(p);
        }
        
        // 释放一些对象
        for (int i = 0; i < 20; ++i) {
            poolAllocator.Deallocate(objects[i]);
        }
        
        // 释放一些分配
        MemoryTracker::GetInstance().TrackDeallocation(p1);
        MemoryTracker::GetInstance().TrackDeallocation(p3);
        
        // p2故意不释放,模拟内存泄漏
    }
    
    // 生成内存报告
    MemoryTracker::GetInstance().GenerateReport();
    
    // 检查内存泄漏
    MemoryTracker::GetInstance().CheckLeaks();
    
    // 保存HTML报告
    MemoryTracker::GetInstance().GenerateReport("memory_report.html");
}

这个内存跟踪系统允许游戏引擎在开发过程中监控内存使用情况,并及时发现内存泄漏。通过将内存分配按系统模块分类,开发者可以更好地理解各个模块的内存占用,并进行有针对性的优化。

结论

调试和开发工具是构建高质量游戏引擎的关键组件。通过本章介绍的各种工具和技术,开发者可以更高效地识别和解决问题,优化性能,并确保游戏引擎的稳定性和可靠性。

从基础的日志系统到复杂的性能剖析和内存跟踪工具,每一个工具都在游戏开发流程中扮演着重要角色。这些工具不仅可以加速开发过程,还能提高最终产品的质量。

关键的调试工具总结:

日志和跟踪系统:记录程序执行流程和错误信息,便于回溯问题
调试绘图功能:可视化游戏世界中的技术细节,如碰撞、寻路等
内置菜单和控制台:提供游戏内调试界面,便于调整参数和执行命令
调试摄像机和暂停系统:便于观察游戏世界并控制游戏流程
作弊功能:加速测试流程,便于验证特定功能
截图和录像:捕获游戏状态,便于回顾和分享
性能剖析:精确测量各模块执行时间,找出性能瓶颈
内存跟踪:监控内存使用,防止泄漏和碎片化

在实际游戏开发中,根据项目规模和团队需求,可以选择性地实现上述工具,或者进一步扩展这些工具的功能。随着游戏规模的增长,强大的调试和开发工具将成为保证开发效率和产品质量的关键因素。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容