import { Node, Vec3, tween } from "cc"; export interface DragCell { r: number; c: number; type: number; node: Node; } export interface DragGroup { cells: DragCell[]; dirR: number; dirC: number; maxSteps: number; } export class DragManager { private board: number[][]; private blockNodes: (Node | null)[][]; private rows: number; private cols: number; private blockSize: number; constructor( board: number[][], blockNodes: (Node | null)[][], rows: number, cols: number, blockSize: number, ) { this.board = board; this.blockNodes = blockNodes; this.rows = rows; this.cols = cols; this.blockSize = blockSize; } /** 查找拖动组(从点击格子 + 方向出发) */ findDragGroup( r: number, c: number, dirR: number, dirC: number, ): { r: number; c: number }[] { if (this.board[r][c] < 0) return []; const group: { r: number; c: number }[] = []; // 包含起点 group.push({ r, c }); if (dirR === 0 && dirC === 0) { // 无方向,返回只有自己 return group; } if (dirR !== 0) { // 垂直方向,向前方向查找 // 前面方向就是 dirR 方向(正负决定上或下) let rr = r + dirR; while (rr >= 0 && rr < this.rows) { if (this.board[rr][c] >= 0) { if (dirR > 0) { // 向下,push后面 group.push({ r: rr, c }); } else { // 向上,unshift前面 group.unshift({ r: rr, c }); } rr += dirR; } else { break; } } } else if (dirC !== 0) { // 水平方向,向前方向查找 // 前面方向就是 dirC 方向(正负决定左或右) let cc = c + dirC; while (cc >= 0 && cc < this.cols) { if (this.board[r][cc] >= 0) { if (dirC > 0) { // 向右,push后面 group.push({ r, c: cc }); } else { // 向左,unshift前面 group.unshift({ r, c: cc }); } cc += dirC; } else { break; } } } return group; } /** 计算最多能移动多少格 */ countEmptyStepsGroup( group: { r: number; c: number }[], dirR: number, dirC: number, ): number { if (group.length === 0) return 0; // 按滑动方向,找整组前方连续空格最小值 if (dirR !== 0) { // 纵向滑动,找组中最顶部或最底部行号 const rows = group.map((g) => g.r); const col = group[0].c; const boundaryRow = dirR === 1 ? Math.max(...rows) : Math.min(...rows); let count = 0; let checkRow = boundaryRow + dirR; while ( checkRow >= 0 && checkRow < this.rows && this.board[checkRow][col] === -1 ) { count++; checkRow += dirR; } return count; } else { // 横向滑动,找组中最左或最右列号 const cols = group.map((g) => g.c); const row = group[0].r; const boundaryCol = dirC === 1 ? Math.max(...cols) : Math.min(...cols); let count = 0; let checkCol = boundaryCol + dirC; while ( checkCol >= 0 && checkCol < this.cols && this.board[row][checkCol] === -1 ) { count++; checkCol += dirC; } return count; } } /** 执行拖动中的偏移 */ public applyDragOffset( draggingGroup, maxSteps: number, dirR: number, dirC: number, dy: number, dx: number, ) { // 计算偏移量,滑动方向相反时置0 let offset = dirR !== 0 ? dy : dx; if ( (dirR === -1 && offset <= 0) || (dirR === 1 && offset >= 0) || (dirC === 1 && offset <= 0) || (dirC === -1 && offset >= 0) ) offset = 0; const maxOffset = maxSteps * this.blockSize; if (Math.abs(offset) > maxOffset) offset = maxOffset * Math.sign(offset); // 整组方块移动 draggingGroup.forEach(({ r, c }) => { const block = this.blockNodes[r][c]; if (!block) return; const startPos = this.getPos(r, c); if (dirR !== 0) { block.setPosition(startPos.x, startPos.y + offset, 0); } else { block.setPosition(startPos.x + offset, startPos.y, 0); } }); } getPos(row: number, col: number): Vec3 { const x = col * this.blockSize - (this.cols * this.blockSize) / 2 + this.blockSize / 2; const y = (this.rows * this.blockSize) / 2 - row * this.blockSize - this.blockSize / 2; return new Vec3(x, y, 0); } /** 松手:确认移动 or 回弹 */ public async settleDrag( blocksInfo: DragCell[], reset: boolean, steps?: number, slidingDir?, ) { if (reset) { await this.resetGroup(blocksInfo); } else { await this.applyMove(blocksInfo, steps, slidingDir); } } /** 执行动画移动 */ private async applyMove(blocksInfo: DragCell[], steps: number, slidingDir) { blocksInfo.forEach(({ r, c, type, node }) => { const tr = r + slidingDir.dirR * steps; const tc = c + slidingDir.dirC * steps; this.board[tr][tc] = type; this.blockNodes[tr][tc] = node; if (node) node.setPosition(this.getPos(tr, tc)); }); } /** 回弹动画 */ private async resetGroup(blocksInfo) { const promises = blocksInfo.map(({ r, c, type, node }) => { const targetPos = this.getPos(r, c); this.board[r][c] = type; this.blockNodes[r][c] = node; return new Promise((resolve) => { tween(node) .to(0.15, { position: targetPos }) .call(() => resolve()) // ✅ 修复 .start(); }); }); await Promise.all(promises); } }