import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find, BoxCollider2D, Collider2D, Contact2DType, IPhysics2DContact, Intersection2D, Rect, PhysicsSystem2D } from 'cc'; const { ccclass, property } = _decorator; @ccclass('BlockManager') export class BlockManager extends Component { // 预制体数组,存储5个预制体 @property([Prefab]) public blockPrefabs: Prefab[] = []; // 网格容器节点 @property({ type: Node, tooltip: '拖拽GridContainer节点到这里' }) public gridContainer: Node = null; // 是否启用物理碰撞检测 @property({ tooltip: '是否启用物理碰撞检测' }) public usePhysicsCollision: boolean = true; // 已经生成的块 private blocks: Node[] = []; // 当前拖拽的块 private currentDragBlock: Node | null = null; // 拖拽起始位置 private startPos = new Vec2(); // 块的起始位置 private blockStartPos: Vec3 = new Vec3(); // 已占用的网格位置 private occupiedPositions: Set = new Set(); start() { // 如果没有指定GridContainer,尝试找到它 if (!this.gridContainer) { this.gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer'); if (!this.gridContainer) { console.error('找不到GridContainer节点'); } } // 启用物理系统 if (this.usePhysicsCollision) { PhysicsSystem2D.instance.enable = true; } // 生成随机的三个块 this.generateRandomBlocks(); } 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); // 添加碰撞组件 if (this.usePhysicsCollision) { this.addColliders(block); } // 添加触摸事件 this.setupDragEvents(block); } } // 为方块的B1、B2、B3等节点添加碰撞组件 addColliders(block: Node) { const blockParts = this.getBlockParts(block); for (const part of blockParts) { // 检查是否已有碰撞组件 let collider = part.getComponent(BoxCollider2D); if (!collider) { collider = part.addComponent(BoxCollider2D); } // 设置碰撞组件属性 const uiTransform = part.getComponent(UITransform); if (uiTransform) { collider.size.width = uiTransform.contentSize.width * 0.8; // 稍微小一点,避免边缘碰撞 collider.size.height = uiTransform.contentSize.height * 0.8; } else { collider.size.width = 40; // 默认大小 collider.size.height = 40; } // 设置碰撞组和掩码 collider.group = 1 << 0; // 默认组 collider.sensor = true; // 设为传感器,不影响物理行为 // 添加碰撞回调 collider.on(Contact2DType.BEGIN_CONTACT, this.onCollisionEnter, this); } } // 碰撞开始回调 onCollisionEnter(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { // 如果当前正在拖拽方块,标记碰撞状态 if (this.currentDragBlock) { const block = this.findBlockByPart(selfCollider.node); if (block === this.currentDragBlock) { block['isColliding'] = true; } } } // 根据部件节点找到所属的方块 findBlockByPart(partNode: Node): Node | null { for (const block of this.blocks) { const blockParts = this.getBlockParts(block); for (const part of blockParts) { if (part === partNode) { return block; } } } return null; } 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); // 重置碰撞状态 block['isColliding'] = false; }, 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); // 添加触摸结束事件 block.on(Node.EventType.TOUCH_END, (event: EventTouch) => { if (this.currentDragBlock) { // 检查是否有碰撞 if (this.usePhysicsCollision && this.currentDragBlock['isColliding']) { // 有碰撞,返回原位 this.currentDragBlock.position = this.blockStartPos.clone(); console.log('方块放置失败:与其他方块碰撞'); } else { // 尝试将方块放置到网格中 if (!this.tryPlaceBlockToGrid(this.currentDragBlock)) { // 放置失败,返回原位 this.currentDragBlock.position = this.blockStartPos.clone(); } } this.currentDragBlock = null; } }, this); // 添加触摸取消事件 block.on(Node.EventType.TOUCH_CANCEL, () => { if (this.currentDragBlock) { // 触摸取消,返回原位 this.currentDragBlock.position = this.blockStartPos.clone(); this.currentDragBlock = null; } }, this); } // 尝试将方块放置到网格中 tryPlaceBlockToGrid(block: Node): boolean { if (!this.gridContainer) return false; // 找到B1节点(吸附点) const b1Node = this.findB1Node(block); if (!b1Node) return false; // 获取B1节点在世界坐标中的位置 const blockWorldPos = block.parent.getComponent(UITransform).convertToWorldSpaceAR(block.position); const b1WorldPos = new Vec3( blockWorldPos.x + b1Node.position.x, blockWorldPos.y + b1Node.position.y, blockWorldPos.z ); // 将B1节点的世界坐标转换为GridContainer的本地坐标 const gridPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(b1WorldPos); // 检查是否在GridContainer范围内 const gridSize = this.gridContainer.getComponent(UITransform).contentSize; const halfWidth = gridSize.width / 2; const halfHeight = gridSize.height / 2; if (gridPos.x < -halfWidth || gridPos.x > halfWidth || gridPos.y < -halfHeight || gridPos.y > halfHeight) { console.log('方块不在GridContainer范围内'); return false; } // 找到最近的网格节点 const nearestGrid = this.findNearestGridNode(gridPos); if (!nearestGrid) { console.log('找不到合适的网格节点'); return false; } // 检查方块的所有部分是否会与已占用的格子重叠 if (!this.canPlaceBlockAt(block, nearestGrid)) { console.log('方块放置位置已被占用或超出边界'); return false; } // 计算方块应该移动到的位置,使B1对齐到网格 const targetWorldPos = this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(nearestGrid.position); const targetPos = block.parent.getComponent(UITransform).convertToNodeSpaceAR(targetWorldPos); // 设置方块位置 block.position = new Vec3( targetPos.x - b1Node.position.x, targetPos.y - b1Node.position.y, block.position.z ); // 标记占用的格子 this.markOccupiedPositions(block, nearestGrid); console.log('方块放置成功'); return true; } // 检查两个方块是否碰撞(使用几何碰撞检测) checkBlocksCollision(block1: Node, block2: Node): boolean { if (block1 === block2) return false; const parts1 = this.getBlockParts(block1); const parts2 = this.getBlockParts(block2); // 检查每个部分之间的碰撞 for (const part1 of parts1) { const rect1 = this.getNodeRect(part1); for (const part2 of parts2) { const rect2 = this.getNodeRect(part2); // 检查矩形是否相交 if (Intersection2D.rectRect(rect1, rect2)) { return true; } } } return false; } // 获取节点的世界矩形 getNodeRect(node: Node): Rect { const uiTransform = node.getComponent(UITransform); if (!uiTransform) { return new Rect(0, 0, 0, 0); } const size = uiTransform.contentSize; const worldPos = node.parent.getComponent(UITransform).convertToWorldSpaceAR(node.position); return new Rect( worldPos.x - size.width * 0.5, worldPos.y - size.height * 0.5, size.width, size.height ); } // 找到B1节点 findB1Node(block: Node): Node { for (let i = 0; i < block.children.length; i++) { const child = block.children[i]; if (child.name === 'B1') { return child; } } return null; } // 找到最近的网格节点 findNearestGridNode(position: Vec3): Node { if (!this.gridContainer) return null; let nearestNode: Node = null; let minDistance = Number.MAX_VALUE; // 遍历所有网格节点 for (let i = 0; i < this.gridContainer.children.length; i++) { const grid = this.gridContainer.children[i]; if (grid.name.startsWith('Grid_')) { const distance = Vec3.distance(position, grid.position); if (distance < minDistance) { minDistance = distance; nearestNode = grid; } } } // 如果距离太远(超过网格大小的一半),不吸附 if (minDistance > 24) { // 假设网格大小为48x48 return null; } return nearestNode; } // 检查方块是否可以放置在指定位置 canPlaceBlockAt(block: Node, targetGrid: Node): boolean { // 获取方块的所有部分(B1, B2, B3...) const blockParts = this.getBlockParts(block); // 找到B1节点 const b1Node = this.findB1Node(block); if (!b1Node) return false; // 计算B1与目标网格的偏移 const offsetX = targetGrid.position.x - b1Node.position.x; const offsetY = targetGrid.position.y - b1Node.position.y; // 检查每个部分是否与已占用的格子重叠 for (const part of blockParts) { // 计算部分在网格中的位置 const gridX = Math.round(offsetX + part.position.x); const gridY = Math.round(offsetY + part.position.y); const posKey = `${gridX},${gridY}`; // 检查该位置是否已被占用 if (this.occupiedPositions.has(posKey)) { return false; } // 检查是否超出GridContainer边界 const gridNode = this.findGridAtPosition(gridX, gridY); if (!gridNode) { return false; } } return true; } // 根据坐标找到网格节点 findGridAtPosition(x: number, y: number): Node { if (!this.gridContainer) return null; for (let i = 0; i < this.gridContainer.children.length; i++) { const grid = this.gridContainer.children[i]; if (grid.name.startsWith('Grid_')) { if (Math.abs(grid.position.x - x) < 1 && Math.abs(grid.position.y - y) < 1) { return grid; } } } return null; } // 获取方块的所有部分(B1, B2, B3...) getBlockParts(block: Node): Node[] { const parts: Node[] = []; for (let i = 0; i < block.children.length; i++) { const child = block.children[i]; if (child.name.startsWith('B') && /^B\d+$/.test(child.name)) { parts.push(child); } } return parts; } // 标记方块占用的格子 markOccupiedPositions(block: Node, targetGrid: Node) { // 获取方块的所有部分 const blockParts = this.getBlockParts(block); // 找到B1节点 const b1Node = this.findB1Node(block); if (!b1Node) return; // 计算B1与目标网格的偏移 const offsetX = targetGrid.position.x - b1Node.position.x; const offsetY = targetGrid.position.y - b1Node.position.y; // 为每个部分标记占用位置 for (const part of blockParts) { // 计算部分在网格中的位置 const gridX = Math.round(offsetX + part.position.x); const gridY = Math.round(offsetY + part.position.y); const posKey = `${gridX},${gridY}`; // 记录占用信息 this.occupiedPositions.add(posKey); // 存储方块与位置的关联 block['occupiedPositions'] = block['occupiedPositions'] || []; block['occupiedPositions'].push(posKey); } } // 移除方块占用的格子 removeBlockFromGrid(block: Node) { // 获取方块占用的位置 const occupiedPositions = block['occupiedPositions']; if (!occupiedPositions) return; // 移除占用标记 for (const posKey of occupiedPositions) { this.occupiedPositions.delete(posKey); } // 清空方块的占用记录 block['occupiedPositions'] = []; } clearBlocks() { // 移除所有已经生成的块 for (const block of this.blocks) { if (block.isValid) { // 移除占用的格子 this.removeBlockFromGrid(block); // 销毁方块 block.destroy(); } } this.blocks = []; // 清空占用位置 this.occupiedPositions.clear(); } }