import { Mesh, PBRMaterial, Texture, AbstractMesh, Plane, Vector3, Scene, Color3 } from "@babylonjs/core"; import { Observer } from "@babylonjs/core/Misc/observable"; import { Nullable } from "@babylonjs/core/types"; import { Monobehiver } from '../base/Monobehiver'; import { Dictionary } from '../utils/Dictionary'; type RollerDoorOptions = { upY?: number; downY?: number; speed?: number; meshNames?: string[]; }; /** * 游戏管理器类 - 负责材质管理和场景控制 * * 核心功能: * - 材质管理(applyMaterial) * - 卷帘门动画(待确认是否实际使用) * - Y轴剖切(待确认是否实际使用) */ export class GameManager extends Monobehiver { private materialDic: Dictionary; private meshDic: Dictionary; // 卷帘门相关(如未使用可删除) private rollerDoorMeshes: AbstractMesh[]; private rollerDoorGroup: AbstractMesh | null; private rollerDoorInitialY: Map; private rollerDoorObserver: Nullable>; private rollerDoorIsOpen: boolean; private rollerDoorNames: string[]; // Y轴剖切相关(如未使用可删除) private yClipPlane: Plane | null; private yClipTargets: string[] | null; constructor(mainApp: any) { super(mainApp); this.materialDic = new Dictionary(); this.meshDic = new Dictionary(); this.rollerDoorMeshes = []; this.rollerDoorGroup = null; this.rollerDoorInitialY = new Map(); this.rollerDoorObserver = null; this.rollerDoorIsOpen = false; this.rollerDoorNames = ["Box006.001", "Box005.001"]; this.yClipPlane = null; this.yClipTargets = null; } /** 调试:返回当前场景中所有网格名称 */ listMeshNames(): string[] { return this.meshDic.Keys(); } /** 初始化游戏管理器 */ async Awake() { const scene = this.mainApp.appScene?.object; if (!scene) { console.warn('Scene not found'); return; } this.updateDictionaries(); } /** * 更新材质和网格字典(从场景中同步) */ updateDictionaries(): void { const scene = this.mainApp.appScene?.object; if (!scene) return; this.materialDic.Clear(); this.meshDic.Clear(); // 更新材质字典 for (const mat of scene.materials) { if (!(mat as any)._isDisposed && !this.materialDic.Has(mat.name)) { this.materialDic.Set(mat.name, mat as PBRMaterial); } } // 更新网格字典 for (const mesh of scene.meshes) { if (mesh instanceof Mesh && !mesh.isDisposed() && !this.meshDic.Has(mesh.name)) { this.meshDic.Set(mesh.name, mesh); } const mat = mesh.material; if (mat instanceof PBRMaterial && !(mat as any)._isDisposed) { this.materialDic.Set(mat.name, mat); } } } /** * 应用材质属性 * @param options 材质配置选项 */ applyMaterial(options: { target: string; modelId?: string; albedoColor?: string; albedoTexture?: string; normalMap?: string; metallicTexture?: string; roughness?: number; metallic?: number; }): void { this.updateDictionaries(); const targetMaterials: PBRMaterial[] = []; // 如果提供了 modelId,只查找该模型的材质 if (options.modelId) { const modelMeshes = this.mainApp.appModel.modelDic.Get(options.modelId); if (!modelMeshes || modelMeshes.length === 0) { console.warn(`Model not found: ${options.modelId}`); return; } modelMeshes.forEach((mesh: AbstractMesh) => { if (mesh.material && mesh.material instanceof PBRMaterial) { const material = mesh.material as PBRMaterial; if (material.name === options.target || material.name.startsWith(`${options.target}_`)) { if (!targetMaterials.includes(material)) { targetMaterials.push(material); } } } }); } else { // 没有提供 modelId,全局查找 this.materialDic.Values().forEach(material => { if (material.name === options.target || material.name.startsWith(`${options.target}_`)) { targetMaterials.push(material); } }); } if (targetMaterials.length === 0) { console.warn(`Material not found: ${options.target}${options.modelId ? ` in model ${options.modelId}` : ''}`); return; } // 应用材质属性 targetMaterials.forEach(material => { // 应用颜色 if (options.albedoColor) { const color = Color3.FromHexString(options.albedoColor); material.albedoColor.copyFrom(color); } // 应用反照率纹理 if (options.albedoTexture !== undefined) { if (options.albedoTexture) { material.albedoTexture = new Texture(options.albedoTexture, this.mainApp.appScene.object); } else { material.albedoTexture = null; } } // 应用法线贴图 if (options.normalMap !== undefined) { if (options.normalMap) { material.bumpTexture = new Texture(options.normalMap, this.mainApp.appScene.object); } else { material.bumpTexture = null; } } // 应用金属度贴图 if (options.metallicTexture !== undefined) { if (options.metallicTexture) { material.metallicTexture = new Texture(options.metallicTexture, this.mainApp.appScene.object); } else { material.metallicTexture = null; } } // 应用粗糙度 if (options.roughness !== undefined) { if (material.roughness !== options.roughness) { material.roughness = options.roughness; } } // 应用金属度 if (options.metallic !== undefined) { if (material.metallic !== options.metallic) { material.metallic = options.metallic; } } }); } /** 卷帘门开合:再次调用会反向动作 */ toggleRollerDoor(options?: RollerDoorOptions): void { this.setRollerDoorState(!this.rollerDoorIsOpen, options); } /** 直接设置卷帘门状态 */ setRollerDoorState(open: boolean, options?: RollerDoorOptions): void { const scene = this.mainApp.appScene?.object; if (!scene) { console.warn('Scene not found for roller door'); return; } this.cacheRollerDoorMeshes(options?.meshNames); if (!this.rollerDoorGroup || !this.rollerDoorMeshes.length) { console.warn('Roller door group or meshes not found'); return; } const upY = options?.upY ?? (this.rollerDoorInitialY.get(this.rollerDoorMeshes[0].name) || 0) + 3; const downY = options?.downY ?? (this.rollerDoorInitialY.get(this.rollerDoorMeshes[0].name) || 0); const speed = options?.speed ?? 1; const targetY = open ? upY : downY; if (this.rollerDoorObserver) { scene.onBeforeRenderObservable.remove(this.rollerDoorObserver); this.rollerDoorObserver = null; } this.rollerDoorObserver = scene.onBeforeRenderObservable.add(() => { if (!this.rollerDoorGroup) return; const delta = scene.getEngine().getDeltaTime() / 1000; const step = speed * delta; const currentY = this.rollerDoorGroup.position.y; if (Math.abs(currentY - targetY) < step) { this.rollerDoorGroup.position.y = targetY; if (this.rollerDoorObserver) { scene.onBeforeRenderObservable.remove(this.rollerDoorObserver); this.rollerDoorObserver = null; } this.rollerDoorIsOpen = open; } else { this.rollerDoorGroup.position.y += (targetY > currentY ? step : -step); } }); } /** 查询卷帘门当前是否已开启 */ isRollerDoorOpen(): boolean { return this.rollerDoorIsOpen; } /** * 缓存卷帘门网格 */ private cacheRollerDoorMeshes(meshNames?: string[]): void { const scene = this.mainApp.appScene?.object; if (!scene) return; const targetNames = meshNames || this.rollerDoorNames; this.rollerDoorMeshes = scene.meshes.filter(mesh => targetNames.includes(mesh.name) ) as AbstractMesh[]; if (this.rollerDoorMeshes.length === 0) return; // 记录初始Y坐标 this.rollerDoorMeshes.forEach(mesh => { if (!this.rollerDoorInitialY.has(mesh.name)) { this.rollerDoorInitialY.set(mesh.name, mesh.position.y); } }); // 创建父节点统一控制 if (!this.rollerDoorGroup) { this.rollerDoorGroup = this.rollerDoorMeshes[0]; for (let i = 1; i < this.rollerDoorMeshes.length; i++) { this.rollerDoorMeshes[i].setParent(this.rollerDoorGroup); } } } /** * Y轴剖切:保留指定高度以上或以下的部分 */ setYAxisClip( height: number, keepAbove: boolean = true, onlyMeshNames?: string[], excludeMeshNames?: string[] ): void { const scene = this.mainApp.appScene?.object; if (!scene) return; this.yClipPlane = new Plane(0, keepAbove ? -1 : 1, 0, keepAbove ? height : -height); this.yClipTargets = onlyMeshNames || null; scene.meshes.forEach(mesh => { if (excludeMeshNames && excludeMeshNames.includes(mesh.name)) { return; } if (this.yClipTargets && !this.yClipTargets.includes(mesh.name)) { return; } if (mesh.material) { const materials = Array.isArray(mesh.material) ? (mesh.material as any[]) : [mesh.material]; materials.forEach(mat => { if (!mat.clipPlane) { mat.clipPlane = this.yClipPlane; } }); } }); } /** * 关闭Y轴剖切 */ clearYAxisClip(): void { const scene = this.mainApp.appScene?.object; if (!scene || !this.yClipPlane) return; scene.meshes.forEach(mesh => { if (mesh.material) { const materials = Array.isArray(mesh.material) ? (mesh.material as any[]) : [mesh.material]; materials.forEach(mat => { if (mat.clipPlane === this.yClipPlane) { mat.clipPlane = null; } }); } }); this.yClipPlane = null; this.yClipTargets = null; } }