From 44925388afbf69c9350b8bfa3672ad540f44ed20 Mon Sep 17 00:00:00 2001 From: zguiy <1415466602@qq.com> Date: Mon, 25 May 2026 10:43:25 +0800 Subject: [PATCH] 1 --- index.html | 65 ++++++++-- src/babylonjs/AppDropZone.ts | 237 +++++++++++++++++++++++++++++++++++ src/babylonjs/AppEngin.ts | 2 - src/babylonjs/AppModel.ts | 29 +++++ src/babylonjs/MainApp.ts | 2 + 5 files changed, 320 insertions(+), 15 deletions(-) diff --git a/index.html b/index.html index ef795a6..7676253 100644 --- a/index.html +++ b/index.html @@ -453,7 +453,7 @@
- +
@@ -911,23 +911,62 @@ // 生成放置区域按钮事件 let dropZoneVisible = false; - document.getElementById('dropzone-btn').addEventListener('click', () => { - if (!dropZoneVisible) { + document.getElementById('dropzone-btn').addEventListener('click', async () => { + const { wallName, index } = dropzone_data; + // 先正常放置模型 + await window.AppLogic.getEvent(dropzone_data, sku); - // 更新按钮文字 - document.getElementById('dropzone-btn').textContent = '隐藏放置区域'; - console.log('已生成并显示放置区域'); - } else { - // 隐藏放置区域 - kernel.dropZone.hideAll(); - dropZoneVisible = false; + // 检查该墙面是否已满 + const zones = kernel.dropZone.getPlacementZones(); + const wallZones = zones.filter(z => z.wallName === wallName); - // 更新按钮文字 - document.getElementById('dropzone-btn').textContent = '生成放置区域'; - console.log('已隐藏放置区域'); + // 获取该墙面已放置的模型 + const placedModels = kernel.dropZone.getPlacedModels(wallName); + + // 如果该墙面所有区域都已占用 + if (placedModels.length === wallZones.length) { + console.log(`${wallName} 墙面已满,自动排列模型`); + + // 按区域索引排序 + const sortedZones = wallZones.sort((a, b) => a.index - b.index); + + // 重新排列模型到对应区域 + placedModels.forEach((model, idx) => { + const targetZone = sortedZones[idx]; + const { position, rotation } = targetZone.transform; + + // 移动模型到目标位置 + kernel.transform.position({ + modelId: model.modelId, + vector3: position + }); + + kernel.transform.rotation({ + modelId: model.modelId, + vector3: rotation + }); + + // 禁用该模型的拖拽 + kernel.model.setDragEnabled(model.modelId, false); + }); + + console.log('模型已自动排列并禁用拖拽'); } + // if (!dropZoneVisible) { + // // 更新按钮文字 + // document.getElementById('dropzone-btn').textContent = '隐藏放置区域'; + // console.log('已生成并显示放置区域'); + // } else { + // // 隐藏放置区域 + // kernel.dropZone.hideAll(); + // dropZoneVisible = false; + + // // 更新按钮文字 + // document.getElementById('dropzone-btn').textContent = '生成放置区域'; + // console.log('已隐藏放置区域'); + // } }); // 初始化放置区域配置数据(只需设置一次) diff --git a/src/babylonjs/AppDropZone.ts b/src/babylonjs/AppDropZone.ts index d6c00e9..938fee8 100644 --- a/src/babylonjs/AppDropZone.ts +++ b/src/babylonjs/AppDropZone.ts @@ -21,6 +21,7 @@ export class AppDropZone { private scene: Scene; private placementWall: AppPlacementWall; private appModel: AppModel | null = null; + private mainApp: any = null; // 内部映射:放置区域 -> 模型ID private zoneModelMap: Map = new Map(); @@ -46,6 +47,13 @@ export class AppDropZone { this.appModel = appModel; } + /** + * 设置 MainApp 引用(内部使用) + */ + setMainApp(mainApp: any): void { + this.mainApp = mainApp; + } + /** * 设置放置区域数据 * @param config 配置参数 @@ -236,6 +244,235 @@ export class AppDropZone { // 记录新模型 this.zoneModelMap.set(zoneKey, modelId); console.log(`已记录模型 ${modelId} 到区域 ${zoneKey}`); + + // 检查该墙面是否已满,如果满了则自动排列 + this.checkAndAutoArrange(wallName); + } + + /** + * 通知模型被删除(外部调用,用于更新映射和重新启用拖拽) + * @param modelId 被删除的模型ID + */ + notifyModelRemoved(modelId: string): void { + console.log(`[模型删除通知] 模型 ${modelId} 被删除`); + + // 找到该模型所在的墙面和索引 + let removedWallName: string | null = null; + let removedZoneKey: string | null = null; + + this.zoneModelMap.forEach((id, zoneKey) => { + if (id === modelId) { + removedZoneKey = zoneKey; + // 从 zoneKey 中提取墙面名称,格式为 "wallName[index]" + const match = zoneKey.match(/^(.+)\[(\d+)\]$/); + if (match) { + removedWallName = match[1]; + } + } + }); + + if (removedZoneKey) { + // 从映射中删除 + this.zoneModelMap.delete(removedZoneKey); + console.log(`[模型删除通知] 已从映射中删除: ${removedZoneKey}`); + } + + if (removedWallName) { + // 检查该墙面是否不满了,如果不满则重新启用拖拽 + this.checkAndReenableDrag(removedWallName); + } + } + + /** + * 检查墙面是否不满,如果不满则重新启用该墙面所有模型的拖拽 + * @param wallName 墙面名称 + */ + private checkAndReenableDrag(wallName: string): void { + const currentDivisions = this.wallDivisionsMap.get(wallName); + if (!currentDivisions) return; + + // 统计该墙面已放置的模型数量 + let placedCount = 0; + const placedModelIds: string[] = []; + this.zoneModelMap.forEach((modelId, zoneKey) => { + if (zoneKey.startsWith(`${wallName}[`)) { + placedCount++; + placedModelIds.push(modelId); + } + }); + + console.log(`[拖拽检查] 墙面 ${wallName} 当前模型数: ${placedCount}/${currentDivisions}`); + + // 如果墙面不满,重新启用所有模型的拖拽 + if (placedCount < currentDivisions) { + console.log(`[拖拽检查] 墙面 ${wallName} 未满,重新启用拖拽`); + placedModelIds.forEach(modelId => { + if (this.mainApp && this.mainApp.appModelDrag && typeof this.mainApp.appModelDrag.setDragEnabled === 'function') { + this.mainApp.appModelDrag.setDragEnabled(modelId, true); + console.log(`[拖拽检查] ✓ 已启用模型 ${modelId} 的拖拽功能`); + } + }); + } + } + + /** + * 检查墙面是否已满 + * @param wallName 墙面名称 + * @returns 是否已满 + */ + isWallFull(wallName: string): boolean { + const currentDivisions = this.wallDivisionsMap.get(wallName); + if (!currentDivisions) return false; + + // 统计该墙面已放置的模型数量 + let placedCount = 0; + this.zoneModelMap.forEach((modelId, zoneKey) => { + if (zoneKey.startsWith(`${wallName}[`)) { + placedCount++; + } + }); + + return placedCount >= currentDivisions; + } + + /** + * 检查墙面是否已满,如果满了则自动排列模型 + * @param wallName 墙面名称 + */ + private checkAndAutoArrange(wallName: string): void { + const currentDivisions = this.wallDivisionsMap.get(wallName); + console.log(`[自动排列检查] 墙面: ${wallName}, 分割数: ${currentDivisions}`); + + if (!currentDivisions) { + console.log(`[自动排列检查] 墙面 ${wallName} 没有分割数配置,跳过`); + return; + } + + // 统计该墙面已放置的模型数量 + let placedCount = 0; + const placedModels: string[] = []; + this.zoneModelMap.forEach((modelId, zoneKey) => { + if (zoneKey.startsWith(`${wallName}[`)) { + placedCount++; + placedModels.push(`${zoneKey} -> ${modelId}`); + } + }); + + console.log(`[自动排列检查] 墙面 ${wallName} 已放置模型数: ${placedCount}/${currentDivisions}`); + console.log(`[自动排列检查] 已放置的模型:`, placedModels); + + // 如果该墙面已满(放置数量等于分割数),执行自动排列 + if (placedCount === currentDivisions) { + console.log(`[自动排列] 墙面 ${wallName} 已满(${placedCount}/${currentDivisions}),开始执行自动排列`); + this.autoArrangeWall(wallName); + } else { + console.log(`[自动排列检查] 墙面 ${wallName} 未满,不执行自动排列`); + } + } + + /** + * 自动排列墙面上的所有模型 + * @param wallName 墙面名称 + */ + private autoArrangeWall(wallName: string): void { + console.log(`[自动排列] 开始排列墙面: ${wallName}`); + + // 获取该墙面的所有放置区域 + const wallZones = this.getZonesByWall(wallName); + console.log(`[自动排列] 墙面 ${wallName} 的放置区域数量: ${wallZones.length}`); + + if (!wallZones.length) { + console.log(`[自动排列] 墙面 ${wallName} 没有放置区域,退出`); + return; + } + + // 收集该墙面已放置的模型信息 + const placedModels: Array<{ modelId: string; currentIndex: number }> = []; + this.zoneModelMap.forEach((modelId, zoneKey) => { + const match = zoneKey.match(new RegExp(`^${wallName}\\[(\\d+)\\]$`)); + if (match) { + const currentIndex = parseInt(match[1]); + placedModels.push({ + modelId: modelId, + currentIndex: currentIndex + }); + console.log(`[自动排列] 找到模型: ${modelId}, 当前索引: ${currentIndex}`); + } + }); + + console.log(`[自动排列] 收集到 ${placedModels.length} 个模型`); + + // 按当前索引排序 + placedModels.sort((a, b) => a.currentIndex - b.currentIndex); + console.log(`[自动排列] 排序后的模型顺序:`, placedModels.map(m => `${m.modelId}(索引${m.currentIndex})`)); + + // 重新排列:将模型按顺序放置到 0, 1, 2... 的位置 + placedModels.forEach((model, newIndex) => { + console.log(`[自动排列] 处理模型 ${model.modelId}: 当前索引=${model.currentIndex}, 目标索引=${newIndex}`); + + // 获取目标放置区域 + const targetZone = wallZones[newIndex]; + if (!targetZone) { + console.warn(`[自动排列] ✗ 找不到索引 ${newIndex} 的放置区域`); + return; + } + + if (this.appModel) { + // 计算新位置(从放置区域的中心点加上法线偏移) + const offsetDistance = 0.05; + const targetPosition = targetZone.center.add(targetZone.normal.scale(offsetDistance)); + + // 计算旋转角度(根据法线方向) + const targetDirection = targetZone.normal.scale(-1); + const angle = Math.atan2(targetDirection.x, targetDirection.z); + + console.log(`[自动排列] 目标区域 ${newIndex} 的位置:`, { + center: targetZone.center, + normal: targetZone.normal, + targetPosition: targetPosition, + rotation: angle * 180 / Math.PI + }); + + // 移动模型到新位置 + const meshes = this.appModel.getCachedMeshes(model.modelId); + if (meshes && meshes.length > 0) { + const rootMesh = meshes[0]; + + // 更新位置 + rootMesh.position.copyFrom(targetPosition); + + // 更新旋转 + rootMesh.rotation.y = angle; + + console.log(`[自动排列] ✓ 模型 ${model.modelId} 已移动到索引 ${newIndex} 的位置`); + } else { + console.warn(`[自动排列] ✗ 找不到模型 ${model.modelId} 的网格`); + } + + // 更新映射(无论是否移动,都要确保映射正确) + if (model.currentIndex !== newIndex) { + const oldKey = `${wallName}[${model.currentIndex}]`; + const newKey = `${wallName}[${newIndex}]`; + this.zoneModelMap.delete(oldKey); + this.zoneModelMap.set(newKey, model.modelId); + console.log(`[自动排列] 更新映射: ${oldKey} -> ${newKey}`); + } + } + }); + + // 禁用该墙面所有模型的拖拽功能 + console.log(`[自动排列] 开始禁用拖拽功能`); + placedModels.forEach(model => { + // 安全检查:确保 mainApp 和 appModelDrag 都存在 + if (this.mainApp && this.mainApp.appModelDrag && typeof this.mainApp.appModelDrag.setDragEnabled === 'function') { + this.mainApp.appModelDrag.setDragEnabled(model.modelId, false); + console.log(`[自动排列] ✓ 已禁用模型 ${model.modelId} 的拖拽功能`); + } else { + console.warn(`[自动排列] ✗ 无法禁用模型 ${model.modelId} 的拖拽功能:appModelDrag 未初始化`); + } + }); + + console.log(`[自动排列] 墙面 ${wallName} 自动排列完成`); } /** diff --git a/src/babylonjs/AppEngin.ts b/src/babylonjs/AppEngin.ts index 3076219..67e981f 100644 --- a/src/babylonjs/AppEngin.ts +++ b/src/babylonjs/AppEngin.ts @@ -29,8 +29,6 @@ export class AppEngin extends Monobehiver { this.object.setSize(window.innerWidth, window.innerHeight); this.object.setHardwareScalingLevel(1); // 1:1像素比例 - - } /** 处理窗口大小变化 */ diff --git a/src/babylonjs/AppModel.ts b/src/babylonjs/AppModel.ts index a065283..3429ed0 100644 --- a/src/babylonjs/AppModel.ts +++ b/src/babylonjs/AppModel.ts @@ -306,6 +306,29 @@ export class AppModel extends Monobehiver { // 配置拖拽功能 if (drag) { this.mainApp.appModelDrag?.configureDrag(modelName + '_' + modelId, drag); + + // 检查该模型所在的墙面是否已满,如果满了则禁用拖拽 + if (this.mainApp.appDropZone) { + // 从 zoneModelMap 中查找该模型所在的墙面 + let modelWallName: string | null = null; + const fullModelId = modelName + '_' + modelId; + + // 遍历 zoneModelMap 查找该模型 + this.mainApp.appDropZone['zoneModelMap']?.forEach((id: string, zoneKey: string) => { + if (id === fullModelId) { + const match = zoneKey.match(/^(.+)\[(\d+)\]$/); + if (match) { + modelWallName = match[1]; + } + } + }); + + // 如果找到墙面且墙面已满,禁用拖拽 + if (modelWallName && this.mainApp.appDropZone.isWallFull(modelWallName)) { + console.log(`[拖拽控制] 墙面 ${modelWallName} 已满,禁用模型 ${fullModelId} 的拖拽`); + this.mainApp.appModelDrag?.setDragEnabled(fullModelId, false); + } + } } // 更新 GameManager 的字典 @@ -507,6 +530,12 @@ export class AppModel extends Monobehiver { this.modelDic.Remove(modelName); this.modelMetadataDic.Remove(modelName); this.mainApp.gameManager?.updateDictionaries(); + + // 通知 AppDropZone 模型被删除 + if (this.mainApp.appDropZone && typeof this.mainApp.appDropZone.notifyModelRemoved === 'function') { + this.mainApp.appDropZone.notifyModelRemoved(modelName); + } + return true; } diff --git a/src/babylonjs/MainApp.ts b/src/babylonjs/MainApp.ts index fade6a1..b17c696 100644 --- a/src/babylonjs/MainApp.ts +++ b/src/babylonjs/MainApp.ts @@ -100,6 +100,8 @@ export class MainApp { this.appDropZone = new AppDropZone(this.appScene.object); // 设置模型管理器引用 this.appDropZone.setModelManager(this.appModel); + // 设置 MainApp 引用,以便访问 appModelDrag + this.appDropZone.setMainApp(this); this.update(); EventBridge.sceneReady({ scene: this.appScene.object }); }