Files
zhengte.babylonjs-sdk/src/babylonjs/GameManager.full.ts
2026-06-04 19:31:40 +08:00

350 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<PBRMaterial>;
private meshDic: Dictionary<any>;
// 卷帘门相关(如未使用可删除)
private rollerDoorMeshes: AbstractMesh[];
private rollerDoorGroup: AbstractMesh | null;
private rollerDoorInitialY: Map<string, number>;
private rollerDoorObserver: Nullable<Observer<Scene>>;
private rollerDoorIsOpen: boolean;
private rollerDoorNames: string[];
// Y轴剖切相关如未使用可删除
private yClipPlane: Plane | null;
private yClipTargets: string[] | null;
constructor(mainApp: any) {
super(mainApp);
this.materialDic = new Dictionary<PBRMaterial>();
this.meshDic = new Dictionary<any>();
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;
}
}