feat(asset): 增强资产管理系统和编辑器 UI (#291)
* fix(editor): 修复粒子实体创建和优化检视器 - 添加 effects 分类到右键菜单,修复粒子实体无法创建的问题 - 添加粒子效果的本地化标签 - 简化粒子组件检视器,优先显示资产文件选择 - 高级属性只在未选择资产时显示,且默认折叠 - 添加可折叠的属性分组提升用户体验 * fix(particle): 修复粒子系统在浏览器预览中的资产加载和渲染 - 添加粒子 Gizmo 支持,显示发射形状并响应 Transform 缩放/旋转 - 修复资产热重载:添加 reloadAsset() 方法和 assets:refresh 事件监听 - 修复 VectorFieldEditors 数值输入精度(step 改为 0.01) - 修复浏览器预览中粒子资产加载失败的问题: - 将相对路径转换为绝对路径以正确复制资产文件 - 使用原始 GUID 而非生成的 GUID 构建 asset catalog - 初始化全局 assetManager 单例的 catalog 和 loader - 在 GameRuntime 的 systemContext 中添加 engineIntegration - 公开 AssetManager.initializeFromCatalog 方法供运行时使用 * feat(asset): 增强资产管理系统和编辑器 UI 主要改动: - 添加 loaderType 字段支持显式指定加载器类型覆盖 - 添加 .particle 扩展名和类型映射 - 新增 MANAGED_ASSET_DIRECTORIES 常量和相关工具方法 - EngineService 使用全局 assetManager 并同步 AssetRegistry 数据 - 修复插件启用逻辑,defaultEnabled=true 的新插件不被旧配置禁用 - ContentBrowser 添加 GUID 管理目录指示和非托管目录警告 - AssetPickerDialog 和 AssetFileInspector UI 增强
This commit is contained in:
@@ -19,7 +19,9 @@ import {
|
||||
AssetPathResolver,
|
||||
AssetPlatform,
|
||||
globalPathResolver,
|
||||
SceneResourceManager
|
||||
SceneResourceManager,
|
||||
assetManager as globalAssetManager,
|
||||
AssetType
|
||||
} from '@esengine/asset-system';
|
||||
import {
|
||||
GameRuntime,
|
||||
@@ -202,12 +204,18 @@ export class EngineService {
|
||||
engineBridge: this._runtime.bridge,
|
||||
renderSystem: this._runtime.renderSystem,
|
||||
assetManager: this._assetManager,
|
||||
isEditor: true
|
||||
engineIntegration: this._engineIntegration,
|
||||
isEditor: true,
|
||||
transformType: TransformComponent
|
||||
};
|
||||
|
||||
// 让插件为场景创建系统
|
||||
pluginManager.createSystemsForScene(this._runtime.scene!, context);
|
||||
|
||||
// Re-sync assets after plugins registered their loaders
|
||||
// 插件注册完加载器后,重新同步资产(确保类型正确)
|
||||
await this._syncAssetRegistryToManager();
|
||||
|
||||
// 同步系统引用到 GameRuntime 的 systemContext(用于 start/stop 时启用/禁用系统)
|
||||
this._runtime.updateSystemContext({
|
||||
animatorSystem: context.animatorSystem,
|
||||
@@ -357,7 +365,9 @@ export class EngineService {
|
||||
*/
|
||||
private async _initializeAssetSystem(): Promise<void> {
|
||||
try {
|
||||
this._assetManager = new AssetManager();
|
||||
// Use global assetManager instance so all systems share the same manager
|
||||
// 使用全局 assetManager 实例,以便所有系统共享同一个管理器
|
||||
this._assetManager = globalAssetManager;
|
||||
|
||||
// Set up asset reader for Tauri environment.
|
||||
// 为 Tauri 环境设置资产读取器。
|
||||
@@ -374,6 +384,10 @@ export class EngineService {
|
||||
}
|
||||
}
|
||||
|
||||
// Sync AssetRegistryService data to global assetManager's database
|
||||
// 将 AssetRegistryService 的数据同步到全局 assetManager 的数据库
|
||||
await this._syncAssetRegistryToManager();
|
||||
|
||||
const pathTransformerFn = (path: string) => {
|
||||
if (!path.startsWith('http://') && !path.startsWith('https://') &&
|
||||
!path.startsWith('data:') && !path.startsWith('asset://')) {
|
||||
@@ -431,6 +445,97 @@ export class EngineService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync AssetRegistryService data to AssetManager's database.
|
||||
* 将 AssetRegistryService 的数据同步到 AssetManager 的数据库。
|
||||
*
|
||||
* This enables GUID-based asset loading through the global assetManager.
|
||||
* Components like ParticleSystemComponent use the global assetManager to load assets by GUID.
|
||||
*
|
||||
* Asset type resolution order:
|
||||
* 1. loaderType from .meta file (explicit user override)
|
||||
* 2. loaderFactory.getAssetTypeByPath (plugin-registered loaders)
|
||||
* 3. Extension-based fallback (built-in types)
|
||||
*
|
||||
* 资产类型解析顺序:
|
||||
* 1. .meta 文件中的 loaderType(用户显式覆盖)
|
||||
* 2. loaderFactory.getAssetTypeByPath(插件注册的加载器)
|
||||
* 3. 基于扩展名的回退(内置类型)
|
||||
*/
|
||||
private async _syncAssetRegistryToManager(): Promise<void> {
|
||||
if (!this._assetManager) return;
|
||||
|
||||
const assetRegistry = Core.services.tryResolve(AssetRegistryService) as AssetRegistryService | null;
|
||||
if (!assetRegistry || !assetRegistry.isReady) {
|
||||
console.warn('[EngineService] AssetRegistryService not ready, skipping sync');
|
||||
return;
|
||||
}
|
||||
|
||||
const database = this._assetManager.getDatabase();
|
||||
const allAssets = assetRegistry.getAllAssets();
|
||||
const metaManager = assetRegistry.metaManager;
|
||||
|
||||
console.log(`[EngineService] Syncing ${allAssets.length} assets from AssetRegistry to AssetManager`);
|
||||
|
||||
// Use loaderFactory to determine asset type from path
|
||||
// This allows plugins to register their own loaders and types
|
||||
// 使用 loaderFactory 根据路径确定资产类型
|
||||
// 这允许插件注册自己的加载器和类型
|
||||
const loaderFactory = this._assetManager.getLoaderFactory();
|
||||
|
||||
for (const asset of allAssets) {
|
||||
let assetType: string | null = null;
|
||||
|
||||
// 1. Check for explicit loaderType in .meta file (user override)
|
||||
// 1. 检查 .meta 文件中的显式 loaderType(用户覆盖)
|
||||
const meta = metaManager.getMetaByGUID(asset.guid);
|
||||
if (meta?.loaderType) {
|
||||
assetType = meta.loaderType;
|
||||
}
|
||||
|
||||
// 2. Try to get type from registered loaders
|
||||
// 2. 尝试从已注册的加载器获取类型
|
||||
if (!assetType) {
|
||||
assetType = loaderFactory?.getAssetTypeByPath?.(asset.path) ?? null;
|
||||
}
|
||||
|
||||
// 3. Fallback: determine type from extension for basic types
|
||||
// 3. 回退:根据扩展名确定基本类型
|
||||
if (!assetType) {
|
||||
const ext = asset.path.substring(asset.path.lastIndexOf('.')).toLowerCase();
|
||||
if (['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'].includes(ext)) {
|
||||
assetType = AssetType.Texture;
|
||||
} else if (['.mp3', '.wav', '.ogg', '.m4a'].includes(ext)) {
|
||||
assetType = AssetType.Audio;
|
||||
} else if (['.json'].includes(ext)) {
|
||||
assetType = AssetType.Json;
|
||||
} else if (['.txt', '.md', '.xml', '.yaml'].includes(ext)) {
|
||||
assetType = AssetType.Text;
|
||||
} else {
|
||||
// Use Custom type - the plugin's loader should handle it
|
||||
// 使用 Custom 类型 - 插件的加载器应该处理它
|
||||
assetType = AssetType.Custom;
|
||||
}
|
||||
}
|
||||
|
||||
database.addAsset({
|
||||
guid: asset.guid,
|
||||
path: asset.path,
|
||||
type: assetType,
|
||||
name: asset.name,
|
||||
size: asset.size,
|
||||
hash: asset.hash || '',
|
||||
dependencies: [],
|
||||
labels: [],
|
||||
tags: new Map(),
|
||||
lastModified: asset.lastModified,
|
||||
version: 1
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[EngineService] Asset sync complete`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup asset path resolver for EngineRenderSystem.
|
||||
* 为 EngineRenderSystem 设置资产路径解析器。
|
||||
@@ -873,8 +978,12 @@ export class EngineService {
|
||||
dispose(): void {
|
||||
this.stop();
|
||||
|
||||
// Don't dispose the global assetManager, just clear the reference
|
||||
// 不要 dispose 全局 assetManager,只是清除引用
|
||||
if (this._assetManager) {
|
||||
this._assetManager.dispose();
|
||||
// Clear the database to free memory when switching projects
|
||||
// 切换项目时清空数据库以释放内存
|
||||
this._assetManager.getDatabase().clear();
|
||||
this._assetManager = null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user