/* * @Author: mojunshou 1637302775@qq.com * @Date: 2025-04-20 10:00:00 * @LastEditors: mojunshou 1637302775@qq.com * @LastEditTime: 2025-04-20 10:00:00 * @Description: 消除游戏方块管理器 */ import { EventTouch, JsonAsset, Node, Prefab, UITransform, Vec2, Vec3, tween, instantiate } from "cc"; import { oops } from "db://oops-framework/core/Oops"; import { BrickData, GridConfigData, GridData } from "./EliminateTypes"; export class EliminateBrickManager { private brickNode: Node | null = null; // 方块容器 private rotateNode: Node | null = null; // 旋转容器 private rotatePrefab: Node | null = null; // 旋转预制体 private moveNode: Node | null = null; // 移动容器 private itemSize: number = 76.25; // 格子大小 private itemPrefabs: Prefab[] = []; // 方块预制体列表 private rotateFaultTolerant = 10; // 旋转容错 private yOffset = 100; // Y轴偏移 private aniBrickRotate = 0; // 旋转动画时间 private bricksList: BrickData[] = []; // 所有可用方块 private brickConfig: { bricks?: any } = {}; // 方块配置 constructor( brickNode: Node, rotateNode: Node, moveNode: Node, itemPrefabs: Prefab[], rotatePrefab: Node, itemSize: number ) { this.brickNode = brickNode; this.rotateNode = rotateNode; this.moveNode = moveNode; this.itemPrefabs = itemPrefabs; this.rotatePrefab = rotatePrefab; this.itemSize = itemSize; } /** * 加载方块配置文件 */ public async loadConfig(): Promise { let json_name: string = "gui/eliminate/config/GridConfig"; return new Promise((resolve, reject) => { oops.res.load(json_name, JsonAsset, (err: Error | null, res: any) => { if (res) { this.brickConfig = res.json; resolve(); } else { console.log("JSON数据加载失败,请检查文件"); reject(err); } }); }); } /** * 初始化方块列表 */ public initBricks(count: number): void { if (this.brickNode) { this.brickNode.destroyAllChildren(); } this.bricksList.length = 0; for (let i = 1; i <= count; i++) { this.addBrick(i); } } /** * 添加一个砖块 */ public addBrick(index: number, isGuideMode: boolean = false): BrickData { // 随机选择砖块类型 const brickConfigs = Object.keys(this.brickConfig['bricks']); let brickKey; if (isGuideMode) { brickKey = "Brick1"; } else { const randomIndex = Math.floor(Math.random() * brickConfigs.length); brickKey = brickConfigs[randomIndex]; } // 随机选择颜色 const randomColorIndex = Math.floor(Math.random() * 3) + 1; // 创建砖块 const brickNode = this.generateBrick(brickKey, randomColorIndex); if (!brickNode || !this.brickNode) { throw new Error("无法生成砖块"); } this.brickNode.addChild(brickNode); // 设置砖块位置 const brickConfig = this.brickConfig['bricks'][brickKey]; const rotateFlag = brickConfig.rotateFlag || false; // 创建砖块数据对象 const brickData: BrickData = { index: index, brickKey: brickKey, rotateFlag: rotateFlag, gridConfig: JSON.parse(JSON.stringify(brickConfig.gridConfig)), deg: 0, brickNode: brickNode, brickInitPos: new Vec3(this.calculateInitPosition(index)), type: randomColorIndex, rotateNode: null }; // 设置初始位置 brickNode.setWorldPosition(brickData.brickInitPos); brickNode.scale.set(0.8, 0.8, 0.8); // 创建旋转节点 if (rotateFlag) { this.createRotateNode(brickData); } // 添加到方块列表 this.bricksList.push(brickData); return brickData; } /** * 生成方块节点 */ public generateBrick(brickKey: string, colorIndex: number): Node { const brickConfig = this.brickConfig['bricks'][brickKey]; let rowMin = 0; let rowMax = 0; let columnMin = 0; let columnMax = 0; // 计算方块尺寸 brickConfig['gridConfig'].forEach((gridConfigData: GridConfigData) => { if (gridConfigData.row < rowMin) { rowMin = gridConfigData.row; } else if (gridConfigData.row > rowMax) { rowMax = gridConfigData.row; } if (gridConfigData.column < columnMin) { columnMin = gridConfigData.column; } else if (gridConfigData.column > columnMax) { columnMax = gridConfigData.column; } }); const rowNum = (rowMax - rowMin + 1); const columnNum = (columnMax - columnMin + 1); // 创建方块节点 const brickNode = new Node(); brickNode.name = brickKey; // 设置方块大小 const transformCom: UITransform = brickNode.addComponent(UITransform); transformCom.setContentSize( this.itemSize * columnNum, this.itemSize * rowNum ); transformCom.setAnchorPoint(0.5, 0.5); // 创建子格子 const gridPrefab = this.itemPrefabs[colorIndex]; brickConfig['gridConfig'].forEach((gridConfigData: GridConfigData) => { const gridNode = new Node(); gridNode.name = 'grid'; brickNode.addChild(gridNode); // 设置子格子大小 gridNode.addComponent(UITransform).setContentSize(this.itemSize, this.itemSize); // 设置子格子位置 const x = this.itemSize * gridConfigData.column - this.itemSize * columnMin; const y = this.itemSize * gridConfigData.row - this.itemSize * rowMin; gridNode.setPosition(x, y); // 创建格子显示节点 const node = instantiate(gridPrefab); gridNode.addChild(node); // 设置格子显示节点大小 const uiTransform = node.getComponent(UITransform); if (uiTransform) { uiTransform.setContentSize( this.itemSize, this.itemSize, ); } // 设置位置 node.setPosition(Vec3.ZERO); // 确保事件冒泡 node.on(Node.EventType.TOUCH_START, (event: EventTouch) => { event.preventSwallow = true; }, this); node.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => { event.preventSwallow = true; }, this); node.on(Node.EventType.TOUCH_END, (event: EventTouch) => { event.preventSwallow = true; }, this); }); return brickNode; } /** * 创建引导砖块 */ public createGuideBrick( gridConfig: GridConfigData[], brickKey: string, colorIndex: number, index: number, rotateFlag = false ): BrickData { const brickData = { index, brickKey: brickKey, rotateFlag, gridConfig, deg: 0, brickNode: new Node(), gridColorKey: "colorKey", brickInitPos: new Vec3(), type: colorIndex, rotateNode: new Node(), }; const node = this.generateBrick(brickKey, colorIndex); if (this.brickNode) { this.brickNode.addChild(node); } const transform = this.brickNode?.getComponent(UITransform); if (transform) { const midX = transform.width / 2; node.setPosition(0, 0); brickData.brickNode = node; brickData.brickInitPos = node.getWorldPosition(); this.bricksList.push(brickData); this.brickAddEvent(brickData, () => { }); } if (brickData.rotateFlag && this.rotatePrefab && this.rotateNode) { brickData.rotateNode = instantiate(this.rotatePrefab); this.rotateNode.addChild(brickData.rotateNode); //先隐藏 brickData.rotateNode.active = false; brickData.rotateNode.setWorldPosition(this.brickNode!.getWorldPosition()); } return brickData; } /** * 给砖块添加事件 */ public brickAddEvent(brickData: BrickData, onDragHandler: (brickData: BrickData, position: Vec3, startPos: Vec2, delta: Vec2) => void): void { const brickNode = brickData.brickNode; if (!brickNode) { console.error("brickNode为空,无法添加事件"); return; } // 记录触摸开始位置 let touchStartPos = new Vec2(); // 触摸开始事件 brickNode.on(Node.EventType.TOUCH_START, (event: EventTouch) => { // 记录触摸起始位置 touchStartPos.set(event.getUILocation()); }, this); // 触摸移动事件 brickNode.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => { const currentPos = event.getUILocation(); const delta = new Vec2( currentPos.x - touchStartPos.x, currentPos.y - touchStartPos.y ); // 如果移动距离太小,忽略 if (delta.length() <= this.rotateFaultTolerant) { return; } // 调用拖动处理回调 onDragHandler(brickData, event.getUILocation().toVec3().add3f(0, this.yOffset, 0), touchStartPos, delta); }, this); } /** * 旋转方块 */ public brickGridRotate(brickData: BrickData): Promise { return new Promise((resolve) => { const next = this.nextGridRotate(brickData.gridConfig, brickData.deg); brickData.deg = next.deg; brickData.gridConfig = next.gridConfig; if (brickData.brickNode) { tween(brickData.brickNode) .to(this.aniBrickRotate, { angle: next.deg }) .call(() => { resolve(true); }) .start(); } else { resolve(true); } }); } /** * 计算下一个旋转状态 */ public nextGridRotate(gridConfig: GridConfigData[], deg: number): { gridConfig: GridConfigData[], deg: number } { const newGridConfig: GridConfigData[] = []; // 顺时针旋转 let newDeg = deg - 90; gridConfig.forEach((gridConfigData) => { // 例如(1,2) => (-2,1),可以画图分析 newGridConfig.push({ row: -gridConfigData.column, column: gridConfigData.row }); }); return { gridConfig: newGridConfig, deg: newDeg }; } /** * 获取所有方块 */ public getBricksList(): BrickData[] { return this.bricksList; } /** * 设置所有方块 */ public setBricksList(bricks: BrickData[]): void { this.bricksList = bricks; } /** * 移除方块 */ public removeBrick(brickData: BrickData): void { const index = this.bricksList.findIndex(item => item === brickData); if (index > -1) { this.bricksList.splice(index, 1); } } /** * 清理所有方块 */ public clearAllBricks(): void { if (this.brickNode) { this.brickNode.destroyAllChildren(); } this.bricksList = []; } /** * 清理旋转节点 */ public clearAllRotateNodes(): void { if (this.rotateNode) { this.rotateNode.destroyAllChildren(); } } /** * 给砖块添加拖动结束事件 */ public brickEndDrag( brickData: BrickData, onEndDragHandler: (brickData: BrickData, canPlace: boolean) => void ): void { const brickNode = brickData.brickNode; if (!brickNode) { console.error("brickNode为空,无法添加拖动结束事件"); return; } // 添加触摸结束事件 const touchEndHandler = (event: EventTouch) => { // 获取当前触摸位置 const currentPos = event.getUILocation(); // 调用处理函数 onEndDragHandler(brickData, true); }; brickNode.on(Node.EventType.TOUCH_END, touchEndHandler, this); brickNode.on(Node.EventType.TOUCH_CANCEL, touchEndHandler, this); } /** * 给砖块添加拖动开始事件 */ public brickStartDrag( brickData: BrickData, onStartDragHandler: (brickData: BrickData, startPos: Vec3) => void ): void { const brickNode = brickData.brickNode; if (!brickNode) { console.error("brickNode为空,无法添加拖动开始事件"); return; } // 添加触摸开始事件 brickNode.on(Node.EventType.TOUCH_START, (event: EventTouch) => { // 记录触摸起始位置 const startPos = brickNode.getWorldPosition().clone(); // 调用处理函数 onStartDragHandler(brickData, startPos); }, this); } /** * 计算初始位置 */ private calculateInitPosition(index: number): Vec3 { // 计算位置 let offset = 220; const brickNum = this.bricksList.length + 1; // +1 因为当前砖块还未添加到列表 if (brickNum % 2 === 1) { const middleNum = Math.floor(brickNum / 2) + 1; if (index < middleNum) { offset = -offset; } else if (index === middleNum) { offset = 0; } } else { const middleNum = brickNum / 2; if (index <= middleNum) { offset = -offset; } } return new Vec3(offset, 0, 0); } /** * 创建旋转节点 */ private createRotateNode(brickData: BrickData): void { if (!this.rotatePrefab || !this.rotateNode || !brickData.brickNode) { return; } brickData.rotateNode = instantiate(this.rotatePrefab); this.rotateNode.addChild(brickData.rotateNode); // 先隐藏 brickData.rotateNode.active = false; brickData.rotateNode.setWorldPosition(brickData.brickNode.getWorldPosition()); } }