优化Gamemanager
This commit is contained in:
349
src/babylonjs/GameManager.full.ts
Normal file
349
src/babylonjs/GameManager.full.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user