Extra2D/docs/API_Tutorial/04_Resource_Management.md

9.1 KiB
Raw Blame History

04. 资源管理

Extra2D 提供了统一的资源管理系统,用于加载和管理游戏中的各种资源。

资源管理器

通过 Application::instance().resources() 访问资源管理器:

auto& resources = Application::instance().resources();

支持的资源类型

资源类型 加载方法 说明
纹理 loadTexture() 图片文件 (PNG, JPG, etc.)
字体 loadFont() TrueType 字体文件
音频 loadSound() / loadMusic() 音频文件

纹理加载

基本用法

// 加载纹理
auto texture = resources.loadTexture("assets/images/player.png");

if (texture) {
    // 创建精灵
    auto sprite = Sprite::create(texture);
    addChild(sprite);
}

异步加载

Extra2D 支持异步加载纹理,避免阻塞主线程:

// 同步加载(默认)
auto texture = resources.loadTexture("assets/images/player.png");

// 异步加载
auto texture = resources.loadTexture("assets/images/player.png", true);

// 使用回调函数处理异步加载完成
resources.loadTextureAsync("assets/images/player.png", 
    TextureFormat::Auto,
    [](Ptr<Texture> texture, const std::string& path) {
        if (texture) {
            // 加载成功,可以安全使用
            auto sprite = Sprite::create(texture);
            // ...
        }
    });

纹理压缩格式

Extra2D 支持多种纹理压缩格式,可显著减少显存占用:

// 支持的纹理格式
enum class TextureFormat {
    Auto,       // 自动选择最佳格式
    RGBA8,      // 32位 RGBA无压缩
    RGB8,       // 24位 RGB无压缩
    DXT1,       // DXT1 压缩(适用于不透明纹理)
    DXT5,       // DXT5 压缩(适用于透明纹理)
    ETC2,       // ETC2 压缩(移动平台)
    ASTC4x4,    // ASTC 4x4 高质量压缩
    ASTC8x8     // ASTC 8x8 高压缩率
};

// 使用压缩格式加载纹理
auto texture = resources.loadTexture("assets/images/player.png", false, TextureFormat::DXT5);

// 异步加载 + 压缩
auto texture = resources.loadTexture("assets/images/player.png", true, TextureFormat::ASTC4x4);

格式选择建议:

格式 压缩比 质量 适用场景
RGBA8 1:1 最高 小图标、需要最高质量
DXT1 1:8 不透明纹理、大背景图
DXT5 1:4 透明纹理、角色精灵
ETC2 1:4 移动设备、跨平台
ASTC4x4 1:4 很高 高质量透明纹理
ASTC8x8 1:16 中等 大纹理、远景贴图

纹理缓存

资源管理器会自动缓存已加载的纹理,多次加载同一文件会返回缓存的实例:

// 第一次加载 - 从文件读取
auto tex1 = resources.loadTexture("assets/image.png");

// 第二次加载 - 返回缓存
auto tex2 = resources.loadTexture("assets/image.png");

// tex1 和 tex2 指向同一个纹理对象

纹理图集Texture Atlas

Extra2D 自动使用纹理图集优化渲染性能:

// 获取纹理图集管理器
auto& atlasManager = resources.getTextureAtlasManager();

// 将多个纹理打包到图集(自动进行)
// 渲染时,相同图集的精灵会自动批处理

// 手动创建图集(高级用法)
auto atlas = atlasManager.createAtlas("ui_atlas", 2048, 2048);
atlas->addTexture("button", buttonTexture);
atlas->addTexture("icon", iconTexture);
atlas->pack();  // 执行打包

字体加载

基本用法

// 加载字体(指定字号)
auto font24 = resources.loadFont("assets/font.ttf", 24, true);

// 创建文本
auto text = Text::create("Hello World", font24);
addChild(text);

字体后备

支持设置后备字体,当主字体缺少某些字符时自动使用后备字体:

// 加载主字体和后备字体
auto mainFont = resources.loadFont("assets/main.ttf", 24, true);
auto fallbackFont = resources.loadFont("assets/fallback.ttf", 24, true);

// 设置后备字体
mainFont->setFallback(fallbackFont);

资源路径

路径格式

// 相对路径(相对于工作目录)
auto tex = resources.loadTexture("assets/images/player.png");

// Switch 平台使用 romfs
auto tex = resources.loadTexture("romfs:/images/player.png");

// SD 卡路径
auto tex = resources.loadTexture("sdmc:/switch/game/images/player.png");

路径辅助函数

// 获取平台特定的资源路径
std::string path = ResourceManager::getPlatformPath("images/player.png");
// Windows: "assets/images/player.png"
// Switch: "romfs:/images/player.png"

资源释放

自动释放

资源使用智能指针管理,当没有引用时会自动释放:

{
    auto tex = resources.loadTexture("assets/temp.png");
    // 使用纹理...
} // 超出作用域,如果没有其他引用,纹理自动释放

手动清理缓存

// 清理未使用的资源
resources.cleanupUnused();

// 清空所有缓存(谨慎使用)
resources.clearCache();

内存管理

内存池(内部自动管理)

Extra2D 使用内存池优化小对象分配,无需用户干预:

// 内存池自动管理以下对象:
// - 场景节点
// - 渲染命令
// - 碰撞形状
// - 事件对象

// 用户代码无需特殊处理,正常使用即可
auto node = Node::create();  // 自动使用内存池
auto sprite = Sprite::create(texture);  // 自动使用内存池

批量更新(内部自动进行)

Extra2D 自动批量更新节点变换,优化性能:

// 以下操作会自动批处理:
// - 节点变换更新
// - 渲染命令提交
// - 纹理绑定

// 用户代码无需特殊处理
for (int i = 0; i < 1000; ++i) {
    auto sprite = Sprite::create(texture);
    sprite->setPosition(i * 10, 100);
    addChild(sprite);  // 变换更新会自动批处理
}

渲染批处理

自动批处理

Extra2D 自动将渲染命令批处理以优化性能:

// 以下情况会自动批处理:
// 1. 相同纹理的精灵
// 2. 相同图层的节点
// 3. 相同混合模式

// 示例1000 个相同纹理的精灵会自动批处理为少量 draw call
for (int i = 0; i < 1000; ++i) {
    auto sprite = Sprite::create(texture);
    addChild(sprite);
}

手动控制渲染顺序

// 设置节点的渲染层级z-order
sprite->setZOrder(10);  // 值越大,渲染越靠前

// 同层级的节点会自动批处理

完整示例

参考 examples/push_box/StartScene.cpp

void StartScene::onEnter() {
    Scene::onEnter();
    
    auto& app = Application::instance();
    auto& resources = app.resources();
    
    // 加载背景纹理(异步 + 压缩)
    auto bgTex = resources.loadTexture("assets/images/start.jpg", true, TextureFormat::DXT1);
    if (bgTex) {
        auto background = Sprite::create(bgTex);
        background->setAnchor(0.0f, 0.0f);
        addChild(background);
    }
    
    // 加载音效图标纹理(异步 + DXT5 压缩支持透明)
    auto soundOn = resources.loadTexture("assets/images/soundon.png", true, TextureFormat::DXT5);
    auto soundOff = resources.loadTexture("assets/images/soundoff.png", true, TextureFormat::DXT5);
    if (soundOn && soundOff) {
        soundIcon_ = Sprite::create(g_SoundOpen ? soundOn : soundOff);
        addChild(soundIcon_);
    }
    
    // 加载字体
    font_ = resources.loadFont("assets/font.ttf", 28, true);
    
    // 创建按钮...
}

性能优化建议

纹理优化

  1. 使用纹理压缩 - 对大型纹理使用 DXT/ASTC 压缩减少显存占用
  2. 使用纹理图集 - 将多个小纹理打包到图集,减少 draw call
  3. 异步加载大纹理 - 避免在主线程加载大型资源造成卡顿
  4. 合理设置纹理尺寸 - 避免使用过大的纹理(建议最大 2048x2048

资源加载策略

// 场景预加载
void GameScene::onEnter() {
    auto& resources = Application::instance().resources();
    
    // 预加载关键资源
    resources.loadTexture("assets/textures/player.png", true);
    resources.loadTexture("assets/textures/enemy.png", true);
    resources.loadFont("assets/fonts/main.ttf", 24, true);
}

// 异步加载非关键资源
void GameScene::loadOptionalResources() {
    resources.loadTextureAsync("assets/textures/background.jpg", 
        TextureFormat::DXT1,
        [](Ptr<Texture> tex, const std::string& path) {
            if (tex) {
                // 加载完成后创建背景
            }
        });
}

最佳实践

  1. 预加载资源 - 在场景 onEnter() 中加载所需资源
  2. 检查资源有效性 - 始终检查加载结果是否为 nullptr
  3. 复用资源 - 多次使用同一资源时保存指针,避免重复加载
  4. 合理设置字号 - 字体加载时会生成对应字号的图集
  5. 使用异步加载 - 对大型资源使用异步加载避免卡顿
  6. 选择合适的压缩格式 - 根据纹理用途选择最佳压缩格式
  7. 利用自动批处理 - 相同纹理的精灵会自动批处理,无需手动优化

下一步