529 lines
11 KiB
Markdown
529 lines
11 KiB
Markdown
|
|
# Extra2D 资源服务系统
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
Extra2D 资源服务系统提供了一套完整的资源管理解决方案,包括资源加载、缓存、打包和加密压缩功能。
|
|||
|
|
|
|||
|
|
### 主要特性
|
|||
|
|
|
|||
|
|
- **类型安全**:使用模板和强类型 ID 避免类型错误
|
|||
|
|
- **自动缓存**:LRU 缓存策略,自动管理内存
|
|||
|
|
- **异步加载**:后台线程加载,不阻塞主线程
|
|||
|
|
- **资源打包**:支持压缩和加密的资源包格式
|
|||
|
|
- **多格式支持**:纹理、字体、着色器、音频等
|
|||
|
|
|
|||
|
|
## 模块结构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
extra2d/asset/
|
|||
|
|
├── asset_types.h # 资源类型定义
|
|||
|
|
├── asset.h # 资源基类
|
|||
|
|
├── asset_handle.h # 资源句柄
|
|||
|
|
├── asset_cache.h # 资源缓存
|
|||
|
|
├── asset_loader.h # 资源加载器
|
|||
|
|
├── asset_pack.h # 资源包
|
|||
|
|
├── data_processor.h # 数据处理器
|
|||
|
|
└── asset_service.h # 资源服务
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 快速开始
|
|||
|
|
|
|||
|
|
### 1. 初始化资源服务
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <extra2d/extra2d.h>
|
|||
|
|
|
|||
|
|
using namespace extra2d;
|
|||
|
|
|
|||
|
|
// 创建资源服务
|
|||
|
|
AssetService service;
|
|||
|
|
service.init();
|
|||
|
|
service.setRoot("assets");
|
|||
|
|
|
|||
|
|
// 注册加载器
|
|||
|
|
service.registerLoader<TextureAsset>(AssetLoaderFactory::createTextureLoader());
|
|||
|
|
service.registerLoader<FontAsset>(AssetLoaderFactory::createFontLoader());
|
|||
|
|
service.registerLoader<ShaderAsset>(AssetLoaderFactory::createShaderLoader());
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 同步加载资源
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 加载纹理
|
|||
|
|
AssetHandle<TextureAsset> texture = service.load<TextureAsset>("sprites/player.png");
|
|||
|
|
if (texture.valid()) {
|
|||
|
|
int width = texture->width();
|
|||
|
|
int height = texture->height();
|
|||
|
|
const u8* data = texture->data();
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 异步加载资源
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 异步加载
|
|||
|
|
service.loadAsync<TextureAsset>("sprites/background.png",
|
|||
|
|
[](AssetHandle<TextureAsset> handle) {
|
|||
|
|
if (handle.valid()) {
|
|||
|
|
// 资源加载完成,在主线程处理
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 在主线程处理回调
|
|||
|
|
service.process();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 使用资源包
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 挂载资源包
|
|||
|
|
service.mount("data.pak");
|
|||
|
|
|
|||
|
|
// 从资源包加载
|
|||
|
|
auto texture = service.load<TextureAsset>("textures/hero.png");
|
|||
|
|
|
|||
|
|
// 卸载资源包
|
|||
|
|
service.unmount("data.pak");
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. 缓存管理
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 设置缓存上限 (100MB)
|
|||
|
|
service.setLimit(100 * 1024 * 1024);
|
|||
|
|
|
|||
|
|
// 获取缓存统计
|
|||
|
|
CacheStats stats = service.stats();
|
|||
|
|
std::cout << "缓存使用: " << stats.bytes << " 字节\n";
|
|||
|
|
std::cout << "命中率: " << (stats.hitRate() * 100) << "%\n";
|
|||
|
|
|
|||
|
|
// 清理无引用资源
|
|||
|
|
service.purge();
|
|||
|
|
|
|||
|
|
// 清空缓存
|
|||
|
|
service.clear();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## API 参考
|
|||
|
|
|
|||
|
|
### AssetID
|
|||
|
|
|
|||
|
|
资源标识符,使用哈希值进行快速比较。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
struct AssetID {
|
|||
|
|
u64 hash; // 哈希值
|
|||
|
|
std::string path; // 原始路径
|
|||
|
|
|
|||
|
|
explicit AssetID(const std::string& path);
|
|||
|
|
bool valid() const;
|
|||
|
|
bool operator==(const AssetID& other) const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### AssetHandle<T>
|
|||
|
|
|
|||
|
|
类型安全的资源句柄,使用弱引用避免阻止资源回收。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
template<typename T>
|
|||
|
|
class AssetHandle {
|
|||
|
|
public:
|
|||
|
|
bool valid() const; // 检查是否有效
|
|||
|
|
Ref<T> get() const; // 获取强引用
|
|||
|
|
T* operator->() const; // 解引用
|
|||
|
|
bool loaded() const; // 检查是否已加载
|
|||
|
|
AssetState state() const; // 获取状态
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### AssetCache
|
|||
|
|
|
|||
|
|
LRU 缓存管理器。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class AssetCache {
|
|||
|
|
public:
|
|||
|
|
explicit AssetCache(size_t limit = 0);
|
|||
|
|
|
|||
|
|
template<typename T>
|
|||
|
|
AssetHandle<T> add(Ref<T> asset);
|
|||
|
|
|
|||
|
|
template<typename T>
|
|||
|
|
AssetHandle<T> get(const AssetID& id);
|
|||
|
|
|
|||
|
|
bool has(const AssetID& id) const;
|
|||
|
|
bool remove(const AssetID& id);
|
|||
|
|
|
|||
|
|
void setLimit(size_t limit);
|
|||
|
|
size_t bytes() const;
|
|||
|
|
size_t count() const;
|
|||
|
|
|
|||
|
|
size_t purge(); // 清理无引用资源
|
|||
|
|
void clear(); // 清空缓存
|
|||
|
|
CacheStats stats() const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### IAssetService
|
|||
|
|
|
|||
|
|
资源服务接口。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class IAssetService : public IService {
|
|||
|
|
public:
|
|||
|
|
// 同步加载
|
|||
|
|
template<typename T>
|
|||
|
|
AssetHandle<T> load(const std::string& path);
|
|||
|
|
|
|||
|
|
// 异步加载
|
|||
|
|
template<typename T>
|
|||
|
|
void loadAsync(const std::string& path, AssetLoadCallback<T> callback);
|
|||
|
|
|
|||
|
|
// 获取已缓存资源
|
|||
|
|
template<typename T>
|
|||
|
|
AssetHandle<T> get(const std::string& path);
|
|||
|
|
|
|||
|
|
// 预加载
|
|||
|
|
template<typename T>
|
|||
|
|
void preload(const std::string& path);
|
|||
|
|
|
|||
|
|
// 状态查询
|
|||
|
|
bool isLoaded(const std::string& path) const;
|
|||
|
|
bool isLoading(const std::string& path) const;
|
|||
|
|
|
|||
|
|
// 资源管理
|
|||
|
|
void unload(const std::string& path);
|
|||
|
|
void setLimit(size_t maxBytes);
|
|||
|
|
size_t size() const;
|
|||
|
|
void purge();
|
|||
|
|
void clear();
|
|||
|
|
CacheStats stats() const;
|
|||
|
|
|
|||
|
|
// 加载器注册
|
|||
|
|
template<typename T>
|
|||
|
|
void registerLoader(Unique<AssetLoader<T>> loader);
|
|||
|
|
|
|||
|
|
// 资源包管理
|
|||
|
|
bool mount(const std::string& path);
|
|||
|
|
void unmount(const std::string& path);
|
|||
|
|
|
|||
|
|
// 数据处理管道
|
|||
|
|
void setPipe(DataPipe pipe);
|
|||
|
|
|
|||
|
|
// 根目录
|
|||
|
|
void setRoot(const std::string& path);
|
|||
|
|
std::string root() const;
|
|||
|
|
|
|||
|
|
// 处理异步回调
|
|||
|
|
void process();
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### DataPipe
|
|||
|
|
|
|||
|
|
数据处理管道,支持链式调用。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class DataPipe {
|
|||
|
|
public:
|
|||
|
|
DataPipe& decrypt(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
|||
|
|
DataPipe& decompress(Compression algo);
|
|||
|
|
DataPipe& encrypt(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
|||
|
|
DataPipe& compress(Compression algo, int level = 3);
|
|||
|
|
DataPipe& add(Unique<DataProcessor> processor);
|
|||
|
|
|
|||
|
|
std::vector<u8> process(const std::vector<u8>& input);
|
|||
|
|
void clear();
|
|||
|
|
bool empty() const;
|
|||
|
|
size_t size() const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### AssetPackBuilder
|
|||
|
|
|
|||
|
|
资源包构建器。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class AssetPackBuilder {
|
|||
|
|
public:
|
|||
|
|
explicit AssetPackBuilder(Compression compression = Compression::None, int level = 3);
|
|||
|
|
|
|||
|
|
void add(const std::string& path, const std::vector<u8>& data);
|
|||
|
|
void add(const std::string& path, std::vector<u8>&& data);
|
|||
|
|
bool addFile(const std::string& filePath, const std::string& packPath = "");
|
|||
|
|
size_t addDirectory(const std::string& dirPath, const std::string& prefix = "");
|
|||
|
|
|
|||
|
|
void setEncryption(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
|||
|
|
bool build(const std::string& outputPath);
|
|||
|
|
|
|||
|
|
void clear();
|
|||
|
|
size_t count() const;
|
|||
|
|
size_t totalOriginalSize() const;
|
|||
|
|
size_t totalCompressedSize() const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 资源类型
|
|||
|
|
|
|||
|
|
### TextureAsset
|
|||
|
|
|
|||
|
|
纹理资源,使用 stb_image 加载。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class TextureAsset : public Asset {
|
|||
|
|
public:
|
|||
|
|
AssetType type() const override { return AssetType::Texture; }
|
|||
|
|
|
|||
|
|
int width() const;
|
|||
|
|
int height() const;
|
|||
|
|
int channels() const;
|
|||
|
|
const u8* data() const;
|
|||
|
|
size_t dataSize() const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### FontAsset
|
|||
|
|
|
|||
|
|
字体资源,支持 TrueType 字体。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class FontAsset : public Asset {
|
|||
|
|
public:
|
|||
|
|
AssetType type() const override { return AssetType::Font; }
|
|||
|
|
|
|||
|
|
float scaleForPixelHeight(float pixels) const;
|
|||
|
|
const u8* data() const;
|
|||
|
|
size_t dataSize() const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ShaderAsset
|
|||
|
|
|
|||
|
|
着色器资源。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class ShaderAsset : public Asset {
|
|||
|
|
public:
|
|||
|
|
AssetType type() const override { return AssetType::Shader; }
|
|||
|
|
|
|||
|
|
const std::string& vertexSource() const;
|
|||
|
|
const std::string& fragmentSource() const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### AudioAsset
|
|||
|
|
|
|||
|
|
音频资源。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class AudioAsset : public Asset {
|
|||
|
|
public:
|
|||
|
|
AssetType type() const override { return AssetType::Audio; }
|
|||
|
|
|
|||
|
|
AudioFormat format() const;
|
|||
|
|
int channels() const;
|
|||
|
|
int sampleRate() const;
|
|||
|
|
int bitsPerSample() const;
|
|||
|
|
float duration() const;
|
|||
|
|
const u8* data() const;
|
|||
|
|
size_t dataSize() const;
|
|||
|
|
bool streaming() const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### DataAsset
|
|||
|
|
|
|||
|
|
通用二进制数据。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class DataAsset : public Asset {
|
|||
|
|
public:
|
|||
|
|
AssetType type() const override { return AssetType::Data; }
|
|||
|
|
|
|||
|
|
const u8* data() const;
|
|||
|
|
size_t size() const;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 资源打包工具
|
|||
|
|
|
|||
|
|
### 安装
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
xmake build asset_packer
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 用法
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Extra2D 资源打包工具 v1.0.0
|
|||
|
|
|
|||
|
|
用法:
|
|||
|
|
asset_packer create <output.pack> [options] <inputs...>
|
|||
|
|
asset_packer list <pack file>
|
|||
|
|
asset_packer extract <pack file> <output dir>
|
|||
|
|
|
|||
|
|
命令:
|
|||
|
|
create 创建资源包
|
|||
|
|
list 列出资源包内容
|
|||
|
|
extract 提取资源包内容
|
|||
|
|
|
|||
|
|
选项:
|
|||
|
|
-c, --compression <algo> 压缩算法 (none, zstd, lz4, zlib),默认 zstd
|
|||
|
|
-l, --level <level> 压缩级别 (1-22),默认 3
|
|||
|
|
-e, --encrypt <key> 加密密钥
|
|||
|
|
-t, --encrypt-type <type> 加密类型 (xor, aes),默认 xor
|
|||
|
|
-v, --verbose 详细输出
|
|||
|
|
-h, --help 显示帮助
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 示例
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 创建资源包(使用 Zstd 压缩)
|
|||
|
|
asset_packer create game.pak -c zstd -v assets/
|
|||
|
|
|
|||
|
|
# 创建加密资源包
|
|||
|
|
asset_packer create game.pak -c zstd -e "secret_key" -t xor -v assets/
|
|||
|
|
|
|||
|
|
# 列出资源包内容
|
|||
|
|
asset_packer list game.pak
|
|||
|
|
|
|||
|
|
# 提取资源包
|
|||
|
|
asset_packer extract game.pak extracted/
|
|||
|
|
|
|||
|
|
# 使用不同压缩算法
|
|||
|
|
asset_packer create game.pak -c lz4 -v assets/
|
|||
|
|
asset_packer create game.pak -c zlib -l 9 -v assets/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 压缩算法对比
|
|||
|
|
|
|||
|
|
| 算法 | 压缩率 | 压缩速度 | 解压速度 | 适用场景 |
|
|||
|
|
|------|--------|----------|----------|----------|
|
|||
|
|
| Zstd | 高 | 快 | 很快 | 通用推荐 |
|
|||
|
|
| LZ4 | 中 | 很快 | 很快 | 实时解压 |
|
|||
|
|
| Zlib | 高 | 中 | 中 | 兼容性好 |
|
|||
|
|
| None | - | - | - | 已压缩资源 |
|
|||
|
|
|
|||
|
|
## 设计模式
|
|||
|
|
|
|||
|
|
### 策略模式 (AssetLoader)
|
|||
|
|
|
|||
|
|
不同资源类型使用不同的加载策略。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class AssetLoader<T> {
|
|||
|
|
public:
|
|||
|
|
virtual Ref<T> load(const std::string& path) = 0;
|
|||
|
|
virtual Ref<T> loadFromMemory(const u8* data, size_t size) = 0;
|
|||
|
|
virtual bool canLoad(const std::string& path) const = 0;
|
|||
|
|
virtual AssetType type() const = 0;
|
|||
|
|
virtual std::vector<std::string> extensions() const = 0;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 享元模式 (AssetCache)
|
|||
|
|
|
|||
|
|
共享资源实例,减少内存占用。
|
|||
|
|
|
|||
|
|
### 装饰器模式 (DataProcessor)
|
|||
|
|
|
|||
|
|
链式处理数据流。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
DataPipe pipe;
|
|||
|
|
pipe.encrypt("key")
|
|||
|
|
.compress(Compression::Zstd);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 服务定位器模式
|
|||
|
|
|
|||
|
|
全局访问资源服务。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
auto* service = ServiceLocator::get<IAssetService>();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 线程安全
|
|||
|
|
|
|||
|
|
- `AssetCache` 使用读写锁 (`std::shared_mutex`)
|
|||
|
|
- `AssetService` 使用读写锁保护资源映射
|
|||
|
|
- 异步加载使用独立的工作线程
|
|||
|
|
- 回调在主线程执行(需要调用 `process()`)
|
|||
|
|
|
|||
|
|
## 最佳实践
|
|||
|
|
|
|||
|
|
### 1. 资源路径约定
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
assets/
|
|||
|
|
├── textures/
|
|||
|
|
│ ├── sprites/
|
|||
|
|
│ └── backgrounds/
|
|||
|
|
├── fonts/
|
|||
|
|
├── shaders/
|
|||
|
|
└── audio/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 预加载关键资源
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 游戏启动时预加载
|
|||
|
|
service.preload<TextureAsset>("textures/loading.png");
|
|||
|
|
service.preload<FontAsset>("fonts/main.ttf");
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 合理设置缓存上限
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 根据目标平台设置
|
|||
|
|
#ifdef MOBILE
|
|||
|
|
service.setLimit(50 * 1024 * 1024); // 50MB
|
|||
|
|
#else
|
|||
|
|
service.setLimit(200 * 1024 * 1024); // 200MB
|
|||
|
|
#endif
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 定期清理缓存
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 场景切换时清理
|
|||
|
|
void onSceneChange() {
|
|||
|
|
service.purge();
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. 使用资源包减少文件数量
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 将小文件打包成资源包
|
|||
|
|
// 减少文件 I/O 操作,提高加载速度
|
|||
|
|
service.mount("textures.pak");
|
|||
|
|
service.mount("audio.pak");
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 错误处理
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
auto handle = service.load<TextureAsset>("missing.png");
|
|||
|
|
if (!handle.valid()) {
|
|||
|
|
// 资源加载失败
|
|||
|
|
std::cerr << "Failed to load texture\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查加载状态
|
|||
|
|
if (handle.state() == AssetState::Failed) {
|
|||
|
|
// 处理失败情况
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 版本历史
|
|||
|
|
|
|||
|
|
- **v1.0.0** - 初始版本
|
|||
|
|
- 资源加载和缓存
|
|||
|
|
- 资源打包和加密
|
|||
|
|
- 异步加载支持
|
|||
|
|
- 多种压缩算法
|