小游戏中主流的资源加载方案

1. 本地资源加载(随包体)

(1) Resources 系统(少量高频资源)

适用于小量、频繁使用的资源(如UI图标、音效)。

// 单个加载
resources.load("images/button", SpriteFrame, (err, spriteFrame) => {
    this.button.spriteFrame = spriteFrame;
});

// 批量加载目录
resources.loadDir("images/ui", (err, assets) => {
    // assets 是该目录下所有资源数组
});

(2) Asset Bundle(模块化资源包)

这是Cocos Creator的核心资源管理方式,支持分包、热更新

// 配置bundle(在构建时指定)
// 创建 bundles/main, bundles/level1 等bundle

// 使用bundle
// 1. 加载bundle
assetManager.loadBundle('main', (err, bundle) => {
    if (err) { console.error(err); return; }
    
    // 2. 从bundle中加载资源
    bundle.load('ui/panel', Prefab, (err, prefab) => {
        if (err) { console.error(err); return; }
        const node = instantiate(prefab);
        this.node.addChild(node);
    });
});

2. 远程资源加载(动态下载)

(1) 远程Asset Bundle

这是小游戏最佳实践,可以极大减小首包体积。

async loadRemoteBundle() {
    // 远程bundle地址
    const bundleUrl = "https://cdn.yourgame.com/bundles/level1";
    
    try {
        // 加载远程bundle
        const bundle = await new Promise((resolve, reject) => {
            assetManager.loadBundle(bundleUrl, (err, bundle) => {
                err ? reject(err) : resolve(bundle);
            });
        });
        
        // 从bundle加载资源
        const prefab = await new Promise((resolve, reject) => {
            bundle.load('monster/slime', Prefab, (err, prefab) => {
                err ? reject(err) : resolve(prefab);
            });
        });
        
        // 实例化
        const monster = instantiate(prefab);
        this.node.addChild(monster);
        
    } catch (error) {
        console.error("加载失败:", error);
    }
}

(2) 远程单文件资源

适合配置文件、少量图片等。

// 加载远程图片
loadRemoteImage(url) {
    return new Promise((resolve, reject) => {
        assetManager.loadRemote(url, (err, texture) => {
            if (err) {
                reject(err);
                return;
            }
            
            const spriteFrame = new SpriteFrame();
            spriteFrame.texture = texture;
            resolve(spriteFrame);
        });
    });
}

// 加载JSON配置
async loadConfig() {
    const configUrl = "https://cdn.yourgame.com/config/game.json";
    
    try {
        const json = await new Promise((resolve, reject) => {
            assetManager.loadRemote(configUrl, (err, data) => {
                err ? reject(err) : resolve(data);
            });
        });
        
        console.log("配置加载成功:", json);
        return json;
    } catch (error) {
        console.error("配置加载失败:", error);
    }
}

3. 小游戏平台特有方案

微信小游戏分包加载

// 1. 在game.json中配置分包
// 2. 代码中加载分包
wx.loadSubpackage({
    name: 'level1', // 分包名
    success: () => {
        console.log('分包加载成功');
        // 分包资源现在可用
    },
    fail: (err) => {
        console.error('分包加载失败', err);
    }
});

临时文件下载(微信小游戏)

// 下载文件到本地临时路径
wx.downloadFile({
    url: 'https://example.com/image.png',
    success: (res) => {
        if (res.statusCode === 200) {
            // 从临时路径加载
            assetManager.loadRemote(res.tempFilePath, (err, texture) => {
                if (!err) {
                    const spriteFrame = new SpriteFrame();
                    spriteFrame.texture = texture;
                    this.sprite.spriteFrame = spriteFrame;
                }
            });
        }
    }
});

实战资源架构示例

// 资源管理器的简化实现
class ResourceManager {
    constructor() {
        this.bundles = new Map(); // 存储已加载的bundle
    }
    
    // 1. 预加载必要资源(启动时)
    async preloadEssential() {
        // 加载主bundle
        const mainBundle = await this.loadBundle('main');
        
        // 加载必要的UI资源
        const uiResources = await this.loadFromBundle(mainBundle, [
            'ui/loading',
            'ui/buttons',
            'ui/fonts'
        ]);
        
        return uiResources;
    }
    
    // 2. 按场景加载资源
    async loadSceneResources(sceneName) {
        // 动态决定加载本地还是远程bundle
        const bundleName = `scene_${sceneName}`;
        
        // 检查是否已下载
        if (!this.isBundleDownloaded(bundleName)) {
            // 下载远程bundle
            const remoteUrl = this.getBundleUrl(bundleName);
            await this.downloadBundle(remoteUrl, bundleName);
        }
        
        // 加载bundle
        const bundle = await this.loadBundle(bundleName);
        
        // 加载场景资源
        const sceneAssets = await this.loadFromBundle(bundle, [
            'textures',
            'prefabs',
            'animations'
        ]);
        
        return sceneAssets;
    }
    
    // 3. 动态资源加载
    async loadDynamicAsset(assetPath) {
        // 先尝试从已加载bundle中查找
        for (const bundle of this.bundles.values()) {
            if (bundle.getInfoWithPath(assetPath)) {
                return this.loadFromBundle(bundle, assetPath);
            }
        }
        
        // 从网络动态下载单个资源
        return this.loadRemoteAsset(this.getRemoteUrl(assetPath));
    }
    
    // 4. 资源清理
    releaseUnusedResources() {
        // 根据引用计数释放资源
        for (const [bundleName, bundle] of this.bundles) {
            if (!this.isBundleInUse(bundleName)) {
                bundle.releaseAll();
                this.bundles.delete(bundleName);
            }
        }
    }
}

加载策略选择指南

资源类型推荐加载方式原因
核心UI、通用音效Resources 或 主Bundle启动时需要,使用频率高
游戏场景资源Asset Bundle(可远程)模块化,可动态下载,减小首包
大型图集/动画Asset Bundle方便批量加载和释放
配置文件远程单文件可热更新,无需重新发包
用户头像/分享图assetManager.loadRemote来自网络,动态变化
第三方SDK资源小游戏分包符合平台规范,隔离依赖

小游戏最佳实践

  1. 首包优化
    • 首包控制在4MB内(微信小游戏标准)
    • 只放启动必须资源
    • 使用远程Bundle延迟加载
  2. 缓存策略 // 实现简单的资源缓存 class ResourceCache { constructor() { this.cache = new Map(); this.maxSize = 50; // 最大缓存数 } async get(url) { if (this.cache.has(url)) { return this.cache.get(url); } const resource = await this.loadResource(url); // LRU缓存策略 if (this.cache.size >= this.maxSize) { const firstKey = this.cache.keys().next().value; this.cache.delete(firstKey); } this.cache.set(url, resource); return resource; } }
  3. 加载状态管理
    • 显示加载进度条
    • 实现重试机制
    • 提供跳过选项(必要时)
  4. 内存管理 // 及时释放不用的资源 resources.release('scene1/textures'); bundle.releaseAll(); assetManager.releaseAsset(texture);

总结

小游戏资源加载是多层次、多策略的混合方案:

  • 核心:Asset Bundle 系统(支持本地/远程)
  • 补充resources(简单场景)、assetManager.loadRemote(单文件)
  • 平台:小游戏分包、临时文件系统
  • 优化:缓存、预加载、懒加载、资源池

实际项目建议

  1. 核心框架和首屏UI → 主Bundle
  2. 不同游戏场景 → 多个远程Bundle
  3. 用户生成内容 → loadRemote动态下载
  4. 平台SDK → 小游戏分包

这样既控制了包体大小,又保证了游戏体验的流畅性。

Comments

No comments yet. Why don’t you start the discussion?

发表回复