Files

233 lines
5.7 KiB
TypeScript
Raw Permalink Normal View History

1970-01-01 08:27:52 +08:00
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<void>((resolve) => {
tween(node)
.to(0.15, { position: targetPos })
.call(() => resolve()) // ✅ 修复
.start();
});
});
await Promise.all(promises);
}
}