diff --git a/index.html b/index.html index 1217ab8..04df6c1 100644 --- a/index.html +++ b/index.html @@ -83,10 +83,10 @@ // 切换卷帘门开关 kernel.door.toggle({ speed: 1.2 }); - // Y轴剖切,调整这个高度值来找到合适的剖切位置 - const clipHeight = 30; // 增加高度,原来是3.5 + // Y轴剖切,只作用于卷帘门网格,保留下方,剖掉上方 + const clipHeight = 28; // 调整这个值找到合适的剖切高度 console.log('设置剖切:', clipHeight); - kernel.clipping.setY(clipHeight, true); + kernel.clipping.setY(clipHeight, true, ['Box005.001', 'Box006.001']); // 验证剖切是否生效 setTimeout(() => { diff --git a/src/babylonjs/GameManager.ts b/src/babylonjs/GameManager.ts index 5c5af15..3ba3e28 100644 --- a/src/babylonjs/GameManager.ts +++ b/src/babylonjs/GameManager.ts @@ -74,7 +74,7 @@ export class GameManager extends Monobehiver { for (const mat of scene.materials) { if (!this.materialDic.Has(mat.name)) { // 初始化材质属性 - mat.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHABLEND; + // mat.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHABLEND; this.materialDic.Set(mat.name, mat as PBRMaterial); } } @@ -390,9 +390,28 @@ export class GameManager extends Monobehiver { 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); + let targetY: number; + + if (open) { + // 上升时:如果指定了绝对高度就用绝对高度,否则用相对高度 + if (options?.upY !== undefined) { + targetY = options.upY; + } else { + // 找到所有门中最高的初始位置,让所有门都升到这个高度+3 + const maxBaseY = Math.max(...this.rollerDoorMeshes.map(m => + this.rollerDoorInitialY.get(m.name) ?? m.position.y + )); + targetY = maxBaseY + 3; + } + } else { + // 下降时:回到各自的初始位置 + targetY = options?.downY ?? baseY; + } + const direction = targetY >= mesh.position.y ? 1 : -1; - return { mesh, targetY, direction }; + const distance = Math.abs(targetY - mesh.position.y); + console.log(`[door] ${mesh.name}: current=${mesh.position.y.toFixed(2)}, target=${targetY.toFixed(2)}, distance=${distance.toFixed(2)}`); + return { mesh, targetY, direction, distance }; }); const alreadyAtTarget = targets.every(({ mesh, targetY }) => Math.abs(mesh.position.y - targetY) < 0.001); @@ -401,17 +420,23 @@ export class GameManager extends Monobehiver { return; } + // 找到最大移动距离,用于计算统一的移动时间 + const maxDistance = Math.max(...targets.map(t => t.distance), 0.001); + 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; + + // 根据每个门的移动距离占比调整速度,确保同时到达 + const speedMultiplier = item.distance / maxDistance; + const step = speed * dt * speedMultiplier; let next = current + dir * step; if ((dir > 0 && next >= item.targetY) || (dir < 0 && next <= item.targetY)) { @@ -437,7 +462,7 @@ export class GameManager extends Monobehiver { /** * 设置基于 Y 轴的剖切平面,keepAbove=true 时保留平面以上部分 - * 使用场景级别的 clipPlane,作用于所有网格 + * onlyMeshNames 指定只作用于哪些网格,其他网格不受影响 */ setYAxisClip( height: number, @@ -453,13 +478,15 @@ export class GameManager extends Monobehiver { const normal = new Vector3(0, keepAbove ? 1 : -1, 0); this.yClipPlane = Plane.FromPositionAndNormal(new Vector3(0, height, 0), normal); - // 直接设置场景的剖切平面,作用于所有网格 - scene.clipPlane = this.yClipPlane; + // 如果指定了特定网格,只对这些网格应用剖切 + if (onlyMeshNames?.length) { + this.applyClipPlaneToMeshes(this.yClipPlane, onlyMeshNames); + } else { + // 否则使用场景级别的剖切,作用于所有网格 + scene.clipPlane = this.yClipPlane; + } - // 创建可视化平面 - this.createClipPlaneVisualization(height, scene); - - console.log('[clipping] Scene clipPlane set:', { height, keepAbove, normal: normal.asArray() }); + console.log('[clipping] Scene clipPlane set:', { height, keepAbove, normal: normal.asArray(), targets: onlyMeshNames || 'all' }); } /** 关闭 Y 轴剖切 */ @@ -471,11 +498,13 @@ export class GameManager extends Monobehiver { this.yClipPlane = null; this.yClipTargets = null; - // 移除可视化平面 - if (this.clipPlaneVisualization) { - this.clipPlaneVisualization.dispose(); - this.clipPlaneVisualization = null; - } + // 清除所有网格材质上的 clipPlane + this.meshDic.Values().forEach((mesh) => { + const mat = mesh.material as any; + if (mat && 'clipPlane' in mat) { + mat.clipPlane = null; + } + }); } private cacheRollerDoorMeshes(customNames?: string[]): void { @@ -502,27 +531,34 @@ export class GameManager extends Monobehiver { this.rollerDoorObserver = null; } - /** 创建剖切平面的可视化 */ - private createClipPlaneVisualization(height: number, scene: Scene): void { - // 移除旧的可视化平面 - if (this.clipPlaneVisualization) { - this.clipPlaneVisualization.dispose(); + /** 将 clipPlane 只作用到指定网格的材质 */ + private applyClipPlaneToMeshes(plane: Plane, targetNames: string[]): void { + const targetSet = new Set(targetNames); + let appliedCount = 0; + + this.meshDic.Values().forEach((mesh) => { + const mat = mesh.material as any; + if (!mat) { + console.log('[clipping] Mesh has no material:', mesh.name); + return; + } + + if (targetSet.has(mesh.name)) { + // 目标网格:应用剖切 + mat.clipPlane = plane; + appliedCount++; + console.log('[clipping] Applied to mesh:', mesh.name, 'material:', mat.name); + } else { + // 非目标网格:清除剖切 + mat.clipPlane = null; + } + }); + + console.log('[clipping] Total meshes processed:', this.meshDic.Keys().length, 'Applied to:', appliedCount); + if (appliedCount === 0) { + console.warn('[clipping] No meshes found with names:', targetNames); + console.log('[clipping] Available mesh names:', this.meshDic.Keys()); } - - // 创建一个半透明的平面网格 - 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 */