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:
@@ -1080,11 +1080,16 @@ export class PluginManager implements IService {
|
||||
const wasEnabled = plugin.enabled;
|
||||
const isDefaultEnabled = plugin.plugin.manifest.defaultEnabled;
|
||||
|
||||
// 如果插件在配置中明确列出,按配置来
|
||||
// 如果插件不在配置中但 defaultEnabled=true,保持启用(新插件不应被旧配置禁用)
|
||||
// If plugin is explicitly in config, follow config
|
||||
// If plugin is not in config but defaultEnabled=true, keep enabled (new plugins should not be disabled by old config)
|
||||
const shouldBeEnabled = inConfig || (isDefaultEnabled && !enabledPlugins.some(p => p === id));
|
||||
// 逻辑:
|
||||
// 1. 如果插件在配置中明确列出,启用它
|
||||
// 2. 如果插件不在配置中但 defaultEnabled=true,也启用它(新插件不应被旧配置禁用)
|
||||
// 3. 只有在配置中明确不包含且 defaultEnabled=false 的插件才禁用
|
||||
//
|
||||
// Logic:
|
||||
// 1. If plugin is explicitly in config, enable it
|
||||
// 2. If plugin is not in config but defaultEnabled=true, also enable it (new plugins should not be disabled by old config)
|
||||
// 3. Only disable plugins that are not in config AND have defaultEnabled=false
|
||||
const shouldBeEnabled = inConfig || isDefaultEnabled;
|
||||
|
||||
if (shouldBeEnabled && !wasEnabled) {
|
||||
toEnable.push(id);
|
||||
|
||||
@@ -125,8 +125,17 @@ const EXTENSION_TYPE_MAP: Record<string, AssetRegistryType> = {
|
||||
'.prefab': 'prefab',
|
||||
'.tmx': 'tilemap',
|
||||
'.tsx': 'tileset',
|
||||
// Particle system
|
||||
'.particle': 'particle',
|
||||
};
|
||||
|
||||
/**
|
||||
* Directories managed by asset registry (GUID system)
|
||||
* 被资产注册表(GUID 系统)管理的目录
|
||||
*/
|
||||
export const MANAGED_ASSET_DIRECTORIES = ['assets', 'scripts', 'scenes'] as const;
|
||||
export type ManagedAssetDirectory = typeof MANAGED_ASSET_DIRECTORIES[number];
|
||||
|
||||
// 使用从 IFileSystem.ts 导入的标准接口
|
||||
// Using standard interface imported from IFileSystem.ts
|
||||
|
||||
@@ -482,13 +491,12 @@ export class AssetRegistryService {
|
||||
|
||||
const sep = this._projectPath.includes('\\') ? '\\' : '/';
|
||||
|
||||
// 扫描多个目录:assets, scripts, scenes
|
||||
// Scan multiple directories: assets, scripts, scenes
|
||||
const directoriesToScan = [
|
||||
{ path: `${this._projectPath}${sep}assets`, name: 'assets' },
|
||||
{ path: `${this._projectPath}${sep}scripts`, name: 'scripts' },
|
||||
{ path: `${this._projectPath}${sep}scenes`, name: 'scenes' }
|
||||
];
|
||||
// 扫描多个目录:assets, scripts, scenes, ecs-scenes
|
||||
// Scan multiple directories: assets, scripts, scenes, ecs-scenes
|
||||
const directoriesToScan = MANAGED_ASSET_DIRECTORIES.map(name => ({
|
||||
path: `${this._projectPath}${sep}${name}`,
|
||||
name
|
||||
}));
|
||||
|
||||
for (const dir of directoriesToScan) {
|
||||
try {
|
||||
@@ -789,6 +797,74 @@ export class AssetRegistryService {
|
||||
return this._projectPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get managed asset directories
|
||||
* 获取被管理的资产目录
|
||||
*/
|
||||
getManagedDirectories(): readonly string[] {
|
||||
return MANAGED_ASSET_DIRECTORIES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a path is within a managed directory
|
||||
* 检查路径是否在被管理的目录中
|
||||
*
|
||||
* @param pathToCheck - Absolute or relative path | 绝对或相对路径
|
||||
* @returns Whether the path is in a managed directory | 路径是否在被管理的目录中
|
||||
*/
|
||||
isPathManaged(pathToCheck: string): boolean {
|
||||
if (!pathToCheck) return false;
|
||||
|
||||
// Normalize path
|
||||
const normalizedPath = pathToCheck.replace(/\\/g, '/');
|
||||
|
||||
// Check if path starts with any managed directory
|
||||
for (const dir of MANAGED_ASSET_DIRECTORIES) {
|
||||
// Check relative path (e.g., "assets/textures/...")
|
||||
if (normalizedPath.startsWith(`${dir}/`) || normalizedPath === dir) {
|
||||
return true;
|
||||
}
|
||||
// Check absolute path (e.g., "C:/project/assets/...")
|
||||
if (this._projectPath) {
|
||||
const normalizedProject = this._projectPath.replace(/\\/g, '/');
|
||||
const managedAbsPath = `${normalizedProject}/${dir}`;
|
||||
if (normalizedPath.startsWith(`${managedAbsPath}/`) || normalizedPath === managedAbsPath) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the managed directory name for a path (if any)
|
||||
* 获取路径所属的被管理目录名称(如果有)
|
||||
*
|
||||
* @param pathToCheck - Absolute or relative path | 绝对或相对路径
|
||||
* @returns The managed directory name or null | 被管理的目录名称或 null
|
||||
*/
|
||||
getManagedDirectoryForPath(pathToCheck: string): ManagedAssetDirectory | null {
|
||||
if (!pathToCheck) return null;
|
||||
|
||||
const normalizedPath = pathToCheck.replace(/\\/g, '/');
|
||||
|
||||
for (const dir of MANAGED_ASSET_DIRECTORIES) {
|
||||
if (normalizedPath.startsWith(`${dir}/`) || normalizedPath === dir) {
|
||||
return dir;
|
||||
}
|
||||
if (this._projectPath) {
|
||||
const normalizedProject = this._projectPath.replace(/\\/g, '/');
|
||||
const managedAbsPath = `${normalizedProject}/${dir}`;
|
||||
if (normalizedPath.startsWith(`${managedAbsPath}/`) || normalizedPath === managedAbsPath) {
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the service
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user