Files
slide_block/assets/scripts/DragManager.ts
1970-01-01 08:27:52 +08:00

233 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}