Files
esengine/packages/asset-system/src/services/SceneResourceManager.ts

156 lines
5.7 KiB
TypeScript
Raw Normal View History

/**
* -
* SceneResourceManager - Centralized resource loading for scenes
*
* ID
* Scans all components in a scene, collects resource references, batch-loads them, and assigns runtime IDs back to components
*/
import type { Scene } from '@esengine/ecs-framework';
import { isResourceComponent, type ResourceReference } from '../interfaces/IResourceComponent';
/**
*
* Resource loader interface
*/
export interface IResourceLoader {
/**
* ID
* Load a batch of resources and return path-to-ID mapping
* @param paths / Array of resource paths
* @param type / Resource type
* @returns ID / Map of paths to runtime IDs
*/
loadResourcesBatch(paths: string[], type: ResourceReference['type']): Promise<Map<string, number>>;
}
export class SceneResourceManager {
private resourceLoader: IResourceLoader | null = null;
/**
*
* Set the resource loader implementation
*
*
* This should be called by the engine integration layer
*
* @param loader / Resource loader instance
*/
setResourceLoader(loader: IResourceLoader): void {
this.resourceLoader = loader;
}
/**
*
* Load all resources required by a scene
*
* / Process:
* 1. IResourceComponent
* Scan all entities and collect resource references from IResourceComponent implementations
* 2.
* Group resources by type (texture, audio, etc.)
* 3.
* Batch load each resource type
* 4. ID
* Assign runtime IDs back to components
*
* @param scene / The scene to load resources for
* @returns Promise / Promise that resolves when all resources are loaded
*/
async loadSceneResources(scene: Scene): Promise<void> {
if (!this.resourceLoader) {
console.warn('[SceneResourceManager] No resource loader set, skipping resource loading');
return;
}
// 从组件收集所有资源引用 / Collect all resource references from components
const resourceRefs = this.collectResourceReferences(scene);
if (resourceRefs.length === 0) {
return;
}
// 按资源类型分组 / Group by resource type
const resourcesByType = new Map<ResourceReference['type'], Set<string>>();
for (const ref of resourceRefs) {
if (!resourcesByType.has(ref.type)) {
resourcesByType.set(ref.type, new Set());
}
resourcesByType.get(ref.type)!.add(ref.path);
}
// 批量加载每种资源类型 / Load each resource type in batch
const allResourceIds = new Map<string, number>();
for (const [type, paths] of resourcesByType) {
const pathsArray = Array.from(paths);
try {
const resourceIds = await this.resourceLoader.loadResourcesBatch(pathsArray, type);
// 合并到总映射表 / Merge into combined map
for (const [path, id] of resourceIds) {
allResourceIds.set(path, id);
}
} catch (error) {
console.error(`[SceneResourceManager] Failed to load ${type} resources:`, error);
}
}
// 将资源 ID 分配回组件 / Assign resource IDs back to components
this.assignResourceIds(scene, allResourceIds);
}
/**
*
* Collect all resource references from scene entities
*/
private collectResourceReferences(scene: Scene): ResourceReference[] {
const refs: ResourceReference[] = [];
for (const entity of scene.entities.buffer) {
for (const component of entity.components) {
if (isResourceComponent(component)) {
const componentRefs = component.getResourceReferences();
refs.push(...componentRefs);
}
}
}
return refs;
}
/**
* ID
* Assign loaded resource IDs back to components
*
* @param scene / Scene
* @param pathToId ID / Path to ID mapping
*/
private assignResourceIds(scene: Scene, pathToId: Map<string, number>): void {
for (const entity of scene.entities.buffer) {
for (const component of entity.components) {
if (isResourceComponent(component)) {
component.setResourceIds(pathToId);
}
}
}
}
/**
* 使
* Unload all resources used by a scene
*
*
* Called when a scene is being destroyed
*
* @param scene / The scene to unload resources for
*/
async unloadSceneResources(_scene: Scene): Promise<void> {
// TODO: 实现资源卸载 / Implement resource unloading
// 需要跟踪资源引用计数,仅在不再使用时卸载
// Need to track resource reference counts and only unload when no longer used
console.log('[SceneResourceManager] Scene resource unloading not yet implemented');
}
}