加入剖切
This commit is contained in:
@ -1,8 +1,21 @@
|
||||
import { Mesh, PBRMaterial, Texture, AbstractMesh } from "@babylonjs/core";
|
||||
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';
|
||||
import { AppConfig } from './AppConfig';
|
||||
|
||||
type RollerDoorOptions = {
|
||||
/** 目标升起高度,缺省为初始 y + 3 */
|
||||
upY?: number;
|
||||
/** 落下终点,缺省为初始 y */
|
||||
downY?: number;
|
||||
/** 运动速度(单位/秒),缺省 1 */
|
||||
speed?: number;
|
||||
/** 自定义门体网格名列表,不传使用默认两个卷帘门 */
|
||||
meshNames?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 游戏管理器类 - 负责管理游戏逻辑、材质和纹理
|
||||
*/
|
||||
@ -10,6 +23,14 @@ export class GameManager extends Monobehiver {
|
||||
private materialDic: Dictionary<PBRMaterial>;
|
||||
private meshDic: Dictionary<any>;
|
||||
private oldTextureDic: Dictionary<any>;
|
||||
private rollerDoorMeshes: AbstractMesh[];
|
||||
private rollerDoorInitialY: Map<string, number>;
|
||||
private rollerDoorObserver: Nullable<Observer<Scene>>;
|
||||
private rollerDoorIsOpen: boolean;
|
||||
private rollerDoorNames: string[];
|
||||
private yClipPlane: Plane | null;
|
||||
private yClipTargets: string[] | null;
|
||||
private clipPlaneVisualization: Mesh | null;
|
||||
|
||||
// 记录加载失败的贴图
|
||||
private failedTextures: Array<{
|
||||
@ -25,9 +46,22 @@ export class GameManager extends Monobehiver {
|
||||
this.materialDic = new Dictionary<PBRMaterial>();
|
||||
this.meshDic = new Dictionary<any>();
|
||||
this.oldTextureDic = new Dictionary<any>();
|
||||
this.rollerDoorMeshes = [];
|
||||
this.rollerDoorInitialY = new Map();
|
||||
this.rollerDoorObserver = null;
|
||||
this.rollerDoorIsOpen = false;
|
||||
this.rollerDoorNames = ["Box006.001", "Box005.001"];
|
||||
this.yClipPlane = null;
|
||||
this.yClipTargets = null;
|
||||
this.clipPlaneVisualization = null;
|
||||
this.failedTextures = [];
|
||||
}
|
||||
|
||||
/** 调试:返回当前场景中所有网格名称 */
|
||||
listMeshNames(): string[] {
|
||||
return this.meshDic.Keys();
|
||||
}
|
||||
|
||||
/** 初始化游戏管理器 */
|
||||
async Awake() {
|
||||
const scene = this.mainApp.appScene?.object;
|
||||
@ -52,6 +86,7 @@ export class GameManager extends Monobehiver {
|
||||
this.meshDic.Set(mesh.name, mesh);
|
||||
}
|
||||
}
|
||||
this.cacheRollerDoorMeshes();
|
||||
console.log('材质字典:', this.materialDic);
|
||||
}
|
||||
|
||||
@ -331,6 +366,165 @@ export class GameManager extends Monobehiver {
|
||||
}
|
||||
}
|
||||
|
||||
/** 卷帘门开合:再次调用会反向动作 */
|
||||
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.rollerDoorMeshes.length) {
|
||||
console.warn('Roller door meshes not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const speed = Math.max(options?.speed ?? 1, 0.01);
|
||||
|
||||
const targets = this.rollerDoorMeshes.map(mesh => {
|
||||
const baseY = this.rollerDoorInitialY.get(mesh.name) ?? mesh.position.y;
|
||||
const targetY = open ? (options?.upY ?? baseY + 3) : (options?.downY ?? baseY);
|
||||
const direction = targetY >= mesh.position.y ? 1 : -1;
|
||||
return { mesh, targetY, direction };
|
||||
});
|
||||
|
||||
const alreadyAtTarget = targets.every(({ mesh, targetY }) => Math.abs(mesh.position.y - targetY) < 0.001);
|
||||
if (alreadyAtTarget) {
|
||||
this.rollerDoorIsOpen = open;
|
||||
return;
|
||||
}
|
||||
|
||||
this.rollerDoorIsOpen = open;
|
||||
this.stopRollerDoorAnimation();
|
||||
|
||||
this.rollerDoorObserver = scene.onBeforeRenderObservable.add(() => {
|
||||
const dt = scene.getEngine().getDeltaTime() / 1000;
|
||||
const step = speed * dt;
|
||||
let finished = true;
|
||||
|
||||
for (const item of targets) {
|
||||
const current = item.mesh.position.y;
|
||||
const dir = item.direction;
|
||||
let next = current + dir * step;
|
||||
|
||||
if ((dir > 0 && next >= item.targetY) || (dir < 0 && next <= item.targetY)) {
|
||||
next = item.targetY;
|
||||
} else {
|
||||
finished = false;
|
||||
}
|
||||
|
||||
item.mesh.position.y = next;
|
||||
}
|
||||
|
||||
if (finished) {
|
||||
this.stopRollerDoorAnimation();
|
||||
this.rollerDoorIsOpen = open;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 当前卷帘门是否开启 */
|
||||
isRollerDoorOpen(): boolean {
|
||||
return this.rollerDoorIsOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置基于 Y 轴的剖切平面,keepAbove=true 时保留平面以上部分
|
||||
* 使用场景级别的 clipPlane,作用于所有网格
|
||||
*/
|
||||
setYAxisClip(
|
||||
height: number,
|
||||
keepAbove = true,
|
||||
onlyMeshNames?: string[],
|
||||
excludeMeshNames?: string[]
|
||||
): void {
|
||||
const scene = this.mainApp.appScene?.object;
|
||||
if (!scene) {
|
||||
console.warn('Scene not found for clipping');
|
||||
return;
|
||||
}
|
||||
const normal = new Vector3(0, keepAbove ? 1 : -1, 0);
|
||||
this.yClipPlane = Plane.FromPositionAndNormal(new Vector3(0, height, 0), normal);
|
||||
|
||||
// 直接设置场景的剖切平面,作用于所有网格
|
||||
scene.clipPlane = this.yClipPlane;
|
||||
|
||||
// 创建可视化平面
|
||||
this.createClipPlaneVisualization(height, scene);
|
||||
|
||||
console.log('[clipping] Scene clipPlane set:', { height, keepAbove, normal: normal.asArray() });
|
||||
}
|
||||
|
||||
/** 关闭 Y 轴剖切 */
|
||||
clearYAxisClip(): void {
|
||||
const scene = this.mainApp.appScene?.object;
|
||||
if (scene) {
|
||||
scene.clipPlane = null;
|
||||
}
|
||||
this.yClipPlane = null;
|
||||
this.yClipTargets = null;
|
||||
|
||||
// 移除可视化平面
|
||||
if (this.clipPlaneVisualization) {
|
||||
this.clipPlaneVisualization.dispose();
|
||||
this.clipPlaneVisualization = null;
|
||||
}
|
||||
}
|
||||
|
||||
private cacheRollerDoorMeshes(customNames?: string[]): void {
|
||||
const names = customNames?.length ? customNames : this.rollerDoorNames;
|
||||
this.rollerDoorMeshes = [];
|
||||
for (const name of names) {
|
||||
const mesh = this.meshDic.Get(name);
|
||||
if (mesh) {
|
||||
this.rollerDoorMeshes.push(mesh);
|
||||
if (!this.rollerDoorInitialY.has(name)) {
|
||||
this.rollerDoorInitialY.set(name, mesh.position.y);
|
||||
}
|
||||
} else {
|
||||
console.warn(`Roller door mesh not found: ${name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private stopRollerDoorAnimation(): void {
|
||||
const scene = this.mainApp.appScene?.object;
|
||||
if (scene && this.rollerDoorObserver) {
|
||||
scene.onBeforeRenderObservable.remove(this.rollerDoorObserver);
|
||||
}
|
||||
this.rollerDoorObserver = null;
|
||||
}
|
||||
|
||||
/** 创建剖切平面的可视化 */
|
||||
private createClipPlaneVisualization(height: number, scene: Scene): void {
|
||||
// 移除旧的可视化平面
|
||||
if (this.clipPlaneVisualization) {
|
||||
this.clipPlaneVisualization.dispose();
|
||||
}
|
||||
|
||||
// 创建一个半透明的平面网格
|
||||
const plane = Mesh.CreatePlane("clipPlaneVisualization", 20, scene);
|
||||
plane.position.y = height;
|
||||
plane.rotation.x = Math.PI / 2; // 旋转使其水平
|
||||
|
||||
// 创建半透明红色材质
|
||||
const mat = new PBRMaterial("clipPlaneMat", scene);
|
||||
mat.albedoColor = new Color3(1, 0, 0); // 红色
|
||||
mat.alpha = 0.3; // 半透明
|
||||
mat.backFaceCulling = false; // 双面显示
|
||||
plane.material = mat;
|
||||
|
||||
this.clipPlaneVisualization = plane;
|
||||
console.log('[clipping] Visualization plane created at height:', height);
|
||||
}
|
||||
|
||||
/** 获取公共URL */
|
||||
private getPublicUrl(): string {
|
||||
// 尝试从环境变量获取
|
||||
@ -343,6 +537,12 @@ export class GameManager extends Monobehiver {
|
||||
|
||||
/** 清理资源 */
|
||||
dispose() {
|
||||
this.stopRollerDoorAnimation();
|
||||
this.clearYAxisClip();
|
||||
this.rollerDoorMeshes = [];
|
||||
this.rollerDoorInitialY.clear();
|
||||
this.rollerDoorIsOpen = false;
|
||||
|
||||
// 清理所有材质资源
|
||||
this.materialDic.Values().forEach((material) => {
|
||||
if (material && material.dispose) {
|
||||
|
||||
Reference in New Issue
Block a user