import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find } from 'cc'; const { ccclass, property } = _decorator; // 格子信息接口 interface GridInfo { node: Node; position: Vec3; isOccupied: boolean; } @ccclass('BlockManager') export class BlockManager extends Component { // 预制体数组,存储5个预制体 @property([Prefab]) public blockPrefabs: Prefab[] = []; // 网格容器节点 @property({ type: Node, tooltip: '拖拽GridContainer节点到这里' }) public gridContainer: Node = null; // 网格大小 @property public gridSize: number = 48; // 已经生成的块 private blocks: Node[] = []; // 当前拖拽的块 private currentDragBlock: Node | null = null; // 拖拽起始位置 private startPos = new Vec2(); // 块的起始位置 private blockStartPos: Vec3 = new Vec3(); // 网格信息 private grids: GridInfo[] = []; // 已放置的方块占用的格子 private occupiedGrids: Set = new Set(); start() { // 初始化网格信息 this.initGrids(); // 生成随机的三个块 this.generateRandomBlocks(); } // 初始化网格信息 initGrids() { if (!this.gridContainer) { this.gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer'); if (!this.gridContainer) { console.error('找不到GridContainer节点'); return; } } // 清空网格信息 this.grids = []; // 遍历GridContainer的子节点,获取所有格子信息 for (let i = 0; i < this.gridContainer.children.length; i++) { const grid = this.gridContainer.children[i]; if (grid.name.startsWith('Grid_')) { this.grids.push({ node: grid, position: grid.position.clone(), isOccupied: false }); } } console.log(`初始化了 ${this.grids.length} 个网格`); } generateRandomBlocks() { // 清除现有的块 this.clearBlocks(); // 检查是否有预制体可用 if (this.blockPrefabs.length === 0) { console.error('没有可用的预制体'); return; } // 位置偏移量 const offsets = [ new Vec3(-200, 0, 0), new Vec3(0, 0, 0), new Vec3(200, 0, 0) ]; // 生成三个随机块 for (let i = 0; i < 3; i++) { // 随机选择一个预制体 const randomIndex = Math.floor(Math.random() * this.blockPrefabs.length); const prefab = this.blockPrefabs[randomIndex]; // 实例化预制体 const block = instantiate(prefab); this.node.addChild(block); // 设置位置 block.position = offsets[i]; // 添加到块数组 this.blocks.push(block); // 添加触摸事件 this.setupDragEvents(block); } } setupDragEvents(block: Node) { // 添加触摸开始事件 block.on(Node.EventType.TOUCH_START, (event: EventTouch) => { // 记录当前拖拽的块 this.currentDragBlock = block; // 记录触摸起始位置 this.startPos = event.getUILocation(); // 记录块的起始位置 this.blockStartPos.set(block.position); // 将块置于顶层 block.setSiblingIndex(this.node.children.length - 1); // 如果方块已经放置在网格中,则移除占用状态 this.removeBlockFromGrid(block); }, this); // 添加触摸移动事件 block.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => { if (!this.currentDragBlock) return; // 计算触摸点位移 const location = event.getUILocation(); const deltaX = location.x - this.startPos.x; const deltaY = location.y - this.startPos.y; // 更新块的位置 this.currentDragBlock.position = new Vec3( this.blockStartPos.x + deltaX, this.blockStartPos.y + deltaY, this.blockStartPos.z ); // 显示吸附预览 this.showSnapPreview(this.currentDragBlock); }, this); // 添加触摸结束事件 block.on(Node.EventType.TOUCH_END, () => { if (this.currentDragBlock) { // 尝试将方块吸附到网格 this.snapBlockToGrid(this.currentDragBlock); this.currentDragBlock = null; } }, this); // 添加触摸取消事件 block.on(Node.EventType.TOUCH_CANCEL, () => { if (this.currentDragBlock) { // 尝试将方块吸附到网格 this.snapBlockToGrid(this.currentDragBlock); this.currentDragBlock = null; } }, this); } // 显示吸附预览 showSnapPreview(block: Node) { // 找到B1节点(吸附点) const b1Node = this.findB1Node(block); if (!b1Node) return; // 计算B1节点在世界坐标系中的位置 const blockWorldPos = this.node.getComponent(UITransform).convertToWorldSpaceAR(block.position); const b1LocalPos = b1Node.position; const b1WorldPos = new Vec3( blockWorldPos.x + b1LocalPos.x, blockWorldPos.y + b1LocalPos.y, blockWorldPos.z + b1LocalPos.z ); // 将B1节点的世界坐标转换为网格容器的本地坐标 const gridContainerPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(b1WorldPos); // 找到最近的网格 const nearestGrid = this.findNearestGrid(gridContainerPos); if (nearestGrid && this.canPlaceBlockAt(block, nearestGrid)) { // 可以显示预览效果,例如高亮网格等 } } // 将方块吸附到网格 snapBlockToGrid(block: Node) { // 找到B1节点(吸附点) const b1Node = this.findB1Node(block); if (!b1Node) return; // 计算B1节点在世界坐标系中的位置 const blockWorldPos = this.node.getComponent(UITransform).convertToWorldSpaceAR(block.position); const b1LocalPos = b1Node.position; const b1WorldPos = new Vec3( blockWorldPos.x + b1LocalPos.x, blockWorldPos.y + b1LocalPos.y, blockWorldPos.z + b1LocalPos.z ); // 将B1节点的世界坐标转换为网格容器的本地坐标 const gridContainerPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(b1WorldPos); // 找到最近的网格 const nearestGrid = this.findNearestGrid(gridContainerPos); if (!nearestGrid) return; // 检查是否可以放置 if (!this.canPlaceBlockAt(block, nearestGrid)) { // 不能放置,恢复到原始位置 block.position = this.blockStartPos.clone(); return; } // 计算方块应该移动到的位置 const targetWorldPos = this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(nearestGrid.position); const targetNodePos = this.node.getComponent(UITransform).convertToNodeSpaceAR(targetWorldPos); // 设置方块位置,使B1节点对齐到网格 block.position = new Vec3( targetNodePos.x - b1LocalPos.x, targetNodePos.y - b1LocalPos.y, block.position.z ); // 标记占用的格子 this.markOccupiedGrids(block, nearestGrid); } // 找到B1节点 findB1Node(block: Node): Node { // 遍历子节点,查找名为B1的节点 for (let i = 0; i < block.children.length; i++) { const child = block.children[i]; if (child.name === 'B1') { return child; } } return null; } // 找到最近的网格 findNearestGrid(position: Vec3): GridInfo { let nearestGrid: GridInfo = null; let minDistance = Number.MAX_VALUE; for (const grid of this.grids) { const distance = Vec3.distance(position, grid.position); if (distance < minDistance && !grid.isOccupied) { minDistance = distance; nearestGrid = grid; } } // 如果距离太远,不吸附 if (minDistance > this.gridSize / 2) { return null; } return nearestGrid; } // 检查方块是否可以放置在指定网格 canPlaceBlockAt(block: Node, grid: GridInfo): boolean { // 获取方块的所有子节点(B1, B2, B3...) const blockParts = this.getBlockParts(block); // 假设B1已经对齐到目标网格,计算其他部分的位置 const b1Node = this.findB1Node(block); if (!b1Node) return false; // 计算方块应该移动到的位置 const targetWorldPos = this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(grid.position); const targetNodePos = this.node.getComponent(UITransform).convertToNodeSpaceAR(targetWorldPos); // 计算方块的新位置 const newBlockPos = new Vec3( targetNodePos.x - b1Node.position.x, targetNodePos.y - b1Node.position.y, block.position.z ); // 检查每个部分是否会与已占用的格子重叠 for (const part of blockParts) { if (part === b1Node) continue; // B1已经对齐到目标网格 // 计算部分在新位置下的世界坐标 const partWorldPos = new Vec3( newBlockPos.x + part.position.x, newBlockPos.y + part.position.y, newBlockPos.z + part.position.z ); const partWorldPosAR = this.node.getComponent(UITransform).convertToWorldSpaceAR(partWorldPos); const gridContainerPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(partWorldPosAR); // 找到该部分对应的网格 const partGrid = this.findExactGrid(gridContainerPos); if (!partGrid) { // 超出网格范围 return false; } // 检查该网格是否已被占用 if (partGrid.isOccupied) { return false; } } return true; } // 找到精确匹配的网格 findExactGrid(position: Vec3): GridInfo { for (const grid of this.grids) { const distance = Vec3.distance(position, grid.position); if (distance < this.gridSize / 2) { return grid; } } return null; } // 获取方块的所有部分(B1, B2, B3...) getBlockParts(block: Node): Node[] { const parts: Node[] = []; // 遍历子节点,查找名称以B开头的节点 for (let i = 0; i < block.children.length; i++) { const child = block.children[i]; if (child.name.startsWith('B') && child.name.length > 1) { parts.push(child); } } return parts; } // 标记方块占用的格子 markOccupiedGrids(block: Node, mainGrid: GridInfo) { // 获取方块的所有部分 const blockParts = this.getBlockParts(block); // 为每个部分找到对应的网格并标记为已占用 for (const part of blockParts) { // 计算部分在世界坐标系中的位置 const blockWorldPos = this.node.getComponent(UITransform).convertToWorldSpaceAR(block.position); const partLocalPos = part.position; const partWorldPos = new Vec3( blockWorldPos.x + partLocalPos.x, blockWorldPos.y + partLocalPos.y, blockWorldPos.z + partLocalPos.z ); // 将部分的世界坐标转换为网格容器的本地坐标 const gridContainerPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(partWorldPos); // 找到该部分对应的网格 const grid = this.findExactGrid(gridContainerPos); if (grid) { grid.isOccupied = true; // 记录占用信息,格式为"blockID:gridID" this.occupiedGrids.add(`${block.uuid}:${grid.node.uuid}`); } } } // 移除方块占用的格子 removeBlockFromGrid(block: Node) { // 遍历所有占用记录,找到与当前方块相关的记录 const toRemove: string[] = []; this.occupiedGrids.forEach(record => { if (record.startsWith(`${block.uuid}:`)) { const gridId = record.split(':')[1]; // 找到对应的网格并标记为未占用 for (const grid of this.grids) { if (grid.node.uuid === gridId) { grid.isOccupied = false; break; } } toRemove.push(record); } }); // 移除记录 toRemove.forEach(record => { this.occupiedGrids.delete(record); }); } clearBlocks() { // 移除所有已经生成的块 for (const block of this.blocks) { if (block.isValid) { // 移除占用的格子 this.removeBlockFromGrid(block); // 销毁方块 block.destroy(); } } this.blocks = []; // 重置所有网格状态 for (const grid of this.grids) { grid.isOccupied = false; } this.occupiedGrids.clear(); } }