| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- 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<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();
-
- // 检查是否有预制体可用
- 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();
- }
- }
|