Feature/physics and tilemap enhancement (#247)
* feat(behavior-tree,tilemap): 修复编辑器连线缩放问题并增强插件系统 * feat(node-editor,blueprint): 新增通用节点编辑器和蓝图可视化脚本系统 * feat(editor,tilemap): 优化编辑器UI样式和Tilemap编辑器功能 * fix: 修复CodeQL安全警告和CI类型检查错误 * fix: 修复CodeQL安全警告和CI类型检查错误 * fix: 修复CodeQL安全警告和CI类型检查错误
This commit is contained in:
@@ -135,7 +135,19 @@ export class AssetManager implements IAssetManager {
|
||||
}
|
||||
|
||||
// 创建加载器 / Create loader
|
||||
const loader = this._loaderFactory.createLoader(metadata.type);
|
||||
let loader = this._loaderFactory.createLoader(metadata.type);
|
||||
|
||||
// 如果没有找到 loader 且类型是 Custom,尝试重新解析类型
|
||||
// If no loader found and type is Custom, try to re-resolve the type
|
||||
if (!loader && metadata.type === AssetType.Custom) {
|
||||
const newType = this.resolveAssetType(metadata.path);
|
||||
if (newType !== AssetType.Custom) {
|
||||
// 更新 metadata 类型 / Update metadata type
|
||||
this._database.updateAsset(guid, { type: newType });
|
||||
loader = this._loaderFactory.createLoader(newType);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loader) {
|
||||
throw AssetLoadError.unsupportedType(guid, metadata.type);
|
||||
}
|
||||
@@ -238,17 +250,7 @@ export class AssetManager implements IAssetManager {
|
||||
let metadata = this._database.getMetadataByPath(path);
|
||||
if (!metadata) {
|
||||
// 动态创建元数据 / Create metadata dynamically
|
||||
const fileExt = path.substring(path.lastIndexOf('.')).toLowerCase();
|
||||
let assetType = AssetType.Custom;
|
||||
|
||||
// 根据文件扩展名确定资产类型 / Determine asset type by file extension
|
||||
if (['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'].includes(fileExt)) {
|
||||
assetType = AssetType.Texture;
|
||||
} else if (['.json'].includes(fileExt)) {
|
||||
assetType = AssetType.Json;
|
||||
} else if (['.txt', '.md', '.xml', '.yaml'].includes(fileExt)) {
|
||||
assetType = AssetType.Text;
|
||||
}
|
||||
const assetType = this.resolveAssetType(path);
|
||||
|
||||
// 生成唯一GUID / Generate unique GUID
|
||||
const dynamicGuid = `dynamic_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
@@ -271,15 +273,59 @@ export class AssetManager implements IAssetManager {
|
||||
this._database.addAsset(metadata);
|
||||
this._pathToGuid.set(path, metadata.guid);
|
||||
} else {
|
||||
// 如果之前缓存的类型是 Custom,检查是否现在有注册的 loader 可以处理
|
||||
// If previously cached as Custom, check if a registered loader can now handle it
|
||||
if (metadata.type === AssetType.Custom) {
|
||||
const newType = this.resolveAssetType(path);
|
||||
if (newType !== AssetType.Custom) {
|
||||
metadata.type = newType;
|
||||
}
|
||||
}
|
||||
this._pathToGuid.set(path, metadata.guid);
|
||||
}
|
||||
|
||||
return this.loadAsset<T>(metadata.guid, options);
|
||||
}
|
||||
|
||||
// 同样检查已缓存的资产,如果类型是 Custom 但现在有 loader 可以处理
|
||||
// Also check cached assets, if type is Custom but now a loader can handle it
|
||||
const entry = this._assets.get(guid);
|
||||
if (entry && entry.metadata.type === AssetType.Custom) {
|
||||
const newType = this.resolveAssetType(path);
|
||||
if (newType !== AssetType.Custom) {
|
||||
entry.metadata.type = newType;
|
||||
}
|
||||
}
|
||||
|
||||
return this.loadAsset<T>(guid, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve asset type from path
|
||||
* 从路径解析资产类型
|
||||
*/
|
||||
private resolveAssetType(path: string): AssetType {
|
||||
// 首先尝试从已注册的加载器获取资产类型 / First try to get asset type from registered loaders
|
||||
const loaderType = (this._loaderFactory as AssetLoaderFactory).getAssetTypeByPath(path);
|
||||
if (loaderType !== null) {
|
||||
return loaderType;
|
||||
}
|
||||
|
||||
// 如果没有找到匹配的加载器,使用默认的扩展名映射 / Fallback to default extension mapping
|
||||
const fileExt = path.substring(path.lastIndexOf('.')).toLowerCase();
|
||||
|
||||
// 默认支持的基础类型 / Default supported basic types
|
||||
if (['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'].includes(fileExt)) {
|
||||
return AssetType.Texture;
|
||||
} else if (['.json'].includes(fileExt)) {
|
||||
return AssetType.Json;
|
||||
} else if (['.txt', '.md', '.xml', '.yaml'].includes(fileExt)) {
|
||||
return AssetType.Text;
|
||||
}
|
||||
|
||||
return AssetType.Custom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load multiple assets
|
||||
* 批量加载资产
|
||||
|
||||
@@ -73,6 +73,18 @@ export interface IAssetLoaderFactory {
|
||||
* 检查类型是否有加载器
|
||||
*/
|
||||
hasLoader(type: AssetType): boolean;
|
||||
|
||||
/**
|
||||
* Get asset type by file extension
|
||||
* 根据文件扩展名获取资产类型
|
||||
*/
|
||||
getAssetTypeByExtension(extension: string): AssetType | null;
|
||||
|
||||
/**
|
||||
* Get asset type by file path
|
||||
* 根据文件路径获取资产类型
|
||||
*/
|
||||
getAssetTypeByPath(path: string): AssetType | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,6 +72,57 @@ export class AssetLoaderFactory implements IAssetLoaderFactory {
|
||||
return this._loaders.has(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get asset type by file extension
|
||||
* 根据文件扩展名获取资产类型
|
||||
*
|
||||
* @param extension - File extension including dot (e.g., '.btree', '.png')
|
||||
* @returns Asset type if a loader supports this extension, null otherwise
|
||||
*/
|
||||
getAssetTypeByExtension(extension: string): AssetType | null {
|
||||
const ext = extension.toLowerCase();
|
||||
for (const [type, loader] of this._loaders) {
|
||||
if (loader.supportedExtensions.some(e => e.toLowerCase() === ext)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get asset type by file path
|
||||
* 根据文件路径获取资产类型
|
||||
*
|
||||
* Checks for compound extensions (like .tilemap.json) first, then simple extensions
|
||||
*
|
||||
* @param path - File path
|
||||
* @returns Asset type if a loader supports this file, null otherwise
|
||||
*/
|
||||
getAssetTypeByPath(path: string): AssetType | null {
|
||||
const lowerPath = path.toLowerCase();
|
||||
|
||||
// First check compound extensions (e.g., .tilemap.json)
|
||||
for (const [type, loader] of this._loaders) {
|
||||
for (const ext of loader.supportedExtensions) {
|
||||
if (ext.includes('.') && ext.split('.').length > 2) {
|
||||
// This is a compound extension like .tilemap.json
|
||||
if (lowerPath.endsWith(ext.toLowerCase())) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then check simple extensions
|
||||
const lastDot = path.lastIndexOf('.');
|
||||
if (lastDot !== -1) {
|
||||
const ext = path.substring(lastDot).toLowerCase();
|
||||
return this.getAssetTypeByExtension(ext);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered loaders
|
||||
* 获取所有注册的加载器
|
||||
|
||||
@@ -33,45 +33,50 @@ export enum AssetState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Asset types supported by the system
|
||||
* 系统支持的资产类型
|
||||
* Asset type - string based for extensibility
|
||||
* 资产类型 - 使用字符串以支持插件扩展
|
||||
*
|
||||
* Plugins can define their own asset types by using custom strings.
|
||||
* Built-in types are provided as constants below.
|
||||
* 插件可以通过使用自定义字符串定义自己的资产类型。
|
||||
* 内置类型作为常量提供如下。
|
||||
*/
|
||||
export enum AssetType {
|
||||
export type AssetType = string;
|
||||
|
||||
/**
|
||||
* Built-in asset types provided by asset-system
|
||||
* asset-system 提供的内置资产类型
|
||||
*/
|
||||
export const AssetType = {
|
||||
/** 纹理 */
|
||||
Texture = 'texture',
|
||||
Texture: 'texture',
|
||||
/** 网格 */
|
||||
Mesh = 'mesh',
|
||||
Mesh: 'mesh',
|
||||
/** 材质 */
|
||||
Material = 'material',
|
||||
Material: 'material',
|
||||
/** 着色器 */
|
||||
Shader = 'shader',
|
||||
Shader: 'shader',
|
||||
/** 音频 */
|
||||
Audio = 'audio',
|
||||
Audio: 'audio',
|
||||
/** 字体 */
|
||||
Font = 'font',
|
||||
Font: 'font',
|
||||
/** 预制体 */
|
||||
Prefab = 'prefab',
|
||||
Prefab: 'prefab',
|
||||
/** 场景 */
|
||||
Scene = 'scene',
|
||||
Scene: 'scene',
|
||||
/** 脚本 */
|
||||
Script = 'script',
|
||||
Script: 'script',
|
||||
/** 动画片段 */
|
||||
AnimationClip = 'animation',
|
||||
/** 行为树 */
|
||||
BehaviorTree = 'behaviortree',
|
||||
/** 瓦片地图 */
|
||||
Tilemap = 'tilemap',
|
||||
/** 瓦片集 */
|
||||
Tileset = 'tileset',
|
||||
AnimationClip: 'animation',
|
||||
/** JSON数据 */
|
||||
Json = 'json',
|
||||
Json: 'json',
|
||||
/** 文本 */
|
||||
Text = 'text',
|
||||
Text: 'text',
|
||||
/** 二进制 */
|
||||
Binary = 'binary',
|
||||
Binary: 'binary',
|
||||
/** 自定义 */
|
||||
Custom = 'custom'
|
||||
}
|
||||
Custom: 'custom'
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Platform variants for assets
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist",
|
||||
|
||||
Reference in New Issue
Block a user