|
|
@@ -1,12 +1,30 @@
|
|
|
-import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2 } from 'cc';
|
|
|
+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[] = [];
|
|
|
// 当前拖拽的块
|
|
|
@@ -15,12 +33,46 @@ export class BlockManager extends Component {
|
|
|
private startPos = new Vec2();
|
|
|
// 块的起始位置
|
|
|
private blockStartPos: Vec3 = new Vec3();
|
|
|
+ // 网格信息
|
|
|
+ private grids: GridInfo[] = [];
|
|
|
+ // 已放置的方块占用的格子
|
|
|
+ private occupiedGrids: Set<string> = 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();
|
|
|
@@ -71,6 +123,9 @@ export class BlockManager extends Component {
|
|
|
|
|
|
// 将块置于顶层
|
|
|
block.setSiblingIndex(this.node.children.length - 1);
|
|
|
+
|
|
|
+ // 如果方块已经放置在网格中,则移除占用状态
|
|
|
+ this.removeBlockFromGrid(block);
|
|
|
}, this);
|
|
|
|
|
|
// 添加触摸移动事件
|
|
|
@@ -88,26 +143,279 @@ export class BlockManager extends Component {
|
|
|
this.blockStartPos.y + deltaY,
|
|
|
this.blockStartPos.z
|
|
|
);
|
|
|
+
|
|
|
+ // 显示吸附预览
|
|
|
+ this.showSnapPreview(this.currentDragBlock);
|
|
|
}, this);
|
|
|
|
|
|
// 添加触摸结束事件
|
|
|
block.on(Node.EventType.TOUCH_END, () => {
|
|
|
- this.currentDragBlock = null;
|
|
|
+ if (this.currentDragBlock) {
|
|
|
+ // 尝试将方块吸附到网格
|
|
|
+ this.snapBlockToGrid(this.currentDragBlock);
|
|
|
+ this.currentDragBlock = null;
|
|
|
+ }
|
|
|
}, this);
|
|
|
|
|
|
// 添加触摸取消事件
|
|
|
block.on(Node.EventType.TOUCH_CANCEL, () => {
|
|
|
- this.currentDragBlock = null;
|
|
|
+ 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();
|
|
|
}
|
|
|
}
|