优化Gamemanager

This commit is contained in:
2026-06-04 19:31:40 +08:00
parent 14287777a4
commit 6e0fefd3c9
6 changed files with 1612 additions and 1456 deletions

View File

@ -0,0 +1,349 @@
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;
}
}