This commit is contained in:
2026-05-25 10:43:25 +08:00
parent 84c8752e0b
commit 44925388af
5 changed files with 320 additions and 15 deletions

View File

@ -453,7 +453,7 @@
<div class="category-content expanded"> <div class="category-content expanded">
<div class="option-group"> <div class="option-group">
<button class="option-btn" data-option="color-1">SPFPDS13FTW</button> <button class="option-btn" data-option="color-1">SPFPDS13FTW</button>
<button class="option-btn" data-option="color-2">SPFPDS13FTC</button> <button class="option-btn" data-option="color-2">SPFSW13FTC</button>
</div> </div>
</div> </div>
</div> </div>
@ -911,23 +911,62 @@
// 生成放置区域按钮事件 // 生成放置区域按钮事件
let dropZoneVisible = false; let dropZoneVisible = false;
document.getElementById('dropzone-btn').addEventListener('click', () => { document.getElementById('dropzone-btn').addEventListener('click', async () => {
if (!dropZoneVisible) {
const { wallName, index } = dropzone_data;
// 先正常放置模型
await window.AppLogic.getEvent(dropzone_data, sku);
// 更新按钮文字 // 检查该墙面是否已满
document.getElementById('dropzone-btn').textContent = '隐藏放置区域'; const zones = kernel.dropZone.getPlacementZones();
console.log('已生成并显示放置区域'); const wallZones = zones.filter(z => z.wallName === wallName);
} else {
// 隐藏放置区域
kernel.dropZone.hideAll();
dropZoneVisible = false;
// 更新按钮文字 // 获取该墙面已放置的模型
document.getElementById('dropzone-btn').textContent = '生成放置区域'; const placedModels = kernel.dropZone.getPlacedModels(wallName);
console.log('已隐藏放置区域');
// 如果该墙面所有区域都已占用
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('已隐藏放置区域');
// }
}); });
// 初始化放置区域配置数据(只需设置一次) // 初始化放置区域配置数据(只需设置一次)

View File

@ -21,6 +21,7 @@ export class AppDropZone {
private scene: Scene; private scene: Scene;
private placementWall: AppPlacementWall; private placementWall: AppPlacementWall;
private appModel: AppModel | null = null; private appModel: AppModel | null = null;
private mainApp: any = null;
// 内部映射:放置区域 -> 模型ID // 内部映射:放置区域 -> 模型ID
private zoneModelMap: Map<string, string> = new Map(); private zoneModelMap: Map<string, string> = new Map();
@ -46,6 +47,13 @@ export class AppDropZone {
this.appModel = appModel; this.appModel = appModel;
} }
/**
* 设置 MainApp 引用(内部使用)
*/
setMainApp(mainApp: any): void {
this.mainApp = mainApp;
}
/** /**
* 设置放置区域数据 * 设置放置区域数据
* @param config 配置参数 * @param config 配置参数
@ -236,6 +244,235 @@ export class AppDropZone {
// 记录新模型 // 记录新模型
this.zoneModelMap.set(zoneKey, modelId); this.zoneModelMap.set(zoneKey, modelId);
console.log(`已记录模型 ${modelId} 到区域 ${zoneKey}`); 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} 自动排列完成`);
} }
/** /**

View File

@ -29,8 +29,6 @@ export class AppEngin extends Monobehiver {
this.object.setSize(window.innerWidth, window.innerHeight); this.object.setSize(window.innerWidth, window.innerHeight);
this.object.setHardwareScalingLevel(1); // 1:1像素比例 this.object.setHardwareScalingLevel(1); // 1:1像素比例
} }
/** 处理窗口大小变化 */ /** 处理窗口大小变化 */

View File

@ -306,6 +306,29 @@ export class AppModel extends Monobehiver {
// 配置拖拽功能 // 配置拖拽功能
if (drag) { if (drag) {
this.mainApp.appModelDrag?.configureDrag(modelName + '_' + modelId, 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 的字典 // 更新 GameManager 的字典
@ -507,6 +530,12 @@ export class AppModel extends Monobehiver {
this.modelDic.Remove(modelName); this.modelDic.Remove(modelName);
this.modelMetadataDic.Remove(modelName); this.modelMetadataDic.Remove(modelName);
this.mainApp.gameManager?.updateDictionaries(); this.mainApp.gameManager?.updateDictionaries();
// 通知 AppDropZone 模型被删除
if (this.mainApp.appDropZone && typeof this.mainApp.appDropZone.notifyModelRemoved === 'function') {
this.mainApp.appDropZone.notifyModelRemoved(modelName);
}
return true; return true;
} }

View File

@ -100,6 +100,8 @@ export class MainApp {
this.appDropZone = new AppDropZone(this.appScene.object); this.appDropZone = new AppDropZone(this.appScene.object);
// 设置模型管理器引用 // 设置模型管理器引用
this.appDropZone.setModelManager(this.appModel); this.appDropZone.setModelManager(this.appModel);
// 设置 MainApp 引用,以便访问 appModelDrag
this.appDropZone.setMainApp(this);
this.update(); this.update();
EventBridge.sceneReady({ scene: this.appScene.object }); EventBridge.sceneReady({ scene: this.appScene.object });
} }