import { _decorator, Component, Node, SpriteFrame, EventTouch, Graphics, Vec2, Vec3, tween, Sprite, Layout, UITransform, Size, view, Color, Prefab, instantiate } from 'cc'; import { CellState, ObstacleType, PatternType } from '../../util/Enum'; import { Board } from './Board'; import { aa } from 'db://assets/scripts/aa'; import { UI_Win } from '../../ui/UIDialog/win/UI_Win'; import { ContainerView } from '../container/ContainerView'; import { Container } from '../container/Container'; const { ccclass, property } = _decorator; // 线条数据结构 interface Line { points: Vec2[]; // 线条经过的所有点 cells: { x: number, y: number }[]; // 线条经过的所有格子坐标 color?: Color; width?: number; startCell: { x: number, y: number }; // 起始格子坐标 endCell?: { x: number, y: number }; // 结束格子坐标 patternType?: PatternType; // 图案类型 linkNodes?: Node[]; // 该线条对应的所有链接节点 } @ccclass('BoardView') export class BoardView extends Component { board: Board @property(Node) grid: Node = null; @property(Node) gridfather: Node = null; @property([SpriteFrame]) emptyCell: SpriteFrame[] = []; @property(Prefab) // 地图格子预制体 cellPrefab: Prefab = null; @property([Color]) // 颜色属性数组 patternColors: Color[] = []; @property(Prefab) Knitt: Prefab = null;//毛线预制体 @property(Prefab) knittPrefab: Prefab = null;//毛线球预制体 @property(Node) KnittFlow: Node = null;//毛线节点 @property(Prefab) KnittStart: Prefab = null;//毛线起点预制体 @property(Prefab) KnittLink: Prefab = null;//毛线线条预制体 @property(Prefab) KnittTurn: Prefab = null;//毛线拐弯预制体 @property(Node) KnittLinkNode: Node = null;//毛线线条节点 // 地图边界属性Node private mapBounds: { left: number, right: number, top: number, bottom: number } | null = null; // 数据模型 private boardModel: Board = new Board(); private containerView: ContainerView = null; // 视图相关属性 private isDrawing: boolean = false; private currentLine: Line = null; private historyLines: Line[] = []; private cellSize: number = 80; private mapOffset: Vec2 = new Vec2(); private startCell: { x: number, y: number } = null; private currentCell: { x: number, y: number } = null; private visitedCells: { x: number, y: number }[] = []; private startPatternType: PatternType | null = null; private cellNodes: Node[][] = []; private linkNodes: Node[] = []; // 存储所有Link节点 private currentLinkNode: Node = null; // 当前正在绘制的Link节点 private pathGroups: { path: number[], groupIndex: number }[] = []; // 存储路径组信息 private isReversed: boolean = false; // 标记是否反向绘制 private knittPositions: Map = new Map(); // 存储每个图案类型的Knitt位置 init(board: Board) { this.board = board; this.boardModel.boardSize = this.boardModel.boardRows * this.boardModel.boardCols; this.adjustMapSize(); this.boardModel.initBoard(this.boardModel.boardRows, this.boardModel.boardCols); this.calculateMapOffset(); this.initCellNodes(); this.calculateMapBounds(); this.updateBoardView(); this.setKnittConfig(); } // 初始化格子节点 private initCellNodes() { this.cellNodes = []; for (let y = 0; y < this.boardModel.boardRows; y++) { this.cellNodes[y] = []; for (let x = 0; x < this.boardModel.boardCols; x++) { const cellNode = instantiate(this.cellPrefab); cellNode.name = `cell_${x}_${y}`; cellNode.setParent(this.grid); const posX = x * this.cellSize - this.mapOffset.x; const posY = -y * this.cellSize + this.mapOffset.y; cellNode.setPosition(posX, posY); this.cellNodes[y][x] = cellNode; const contentNode = cellNode.getChildByName('cottent'); if (contentNode) { contentNode.active = false; } } } } private calculateMapOffset() { if (!this.grid) return; const transform = this.grid.getComponent(UITransform); if (!transform) return; // 计算居中偏移量 const mapWidth = this.boardModel.boardCols * this.cellSize; const mapHeight = this.boardModel.boardRows * this.cellSize; this.mapOffset.x = mapWidth / 2 - this.cellSize / 2; this.mapOffset.y = mapHeight / 2 - this.cellSize / 2; } private calculateMapBounds() { if (!this.grid) return; const transform = this.grid.getComponent(UITransform); if (!transform) return; const worldPos = this.grid.worldPosition; const width = transform.width; const height = transform.height; this.mapBounds = { left: worldPos.x - width / 2, right: worldPos.x + width / 2, top: worldPos.y + height / 2, bottom: worldPos.y - height / 2 }; } private adjustMapSize() { if (!this.gridfather) return; if (!this.grid) return; const gridFatherTransform = this.gridfather.getComponent(UITransform); if (gridFatherTransform) { const idealWidth = (this.boardModel.boardCols * this.cellSize) + 60; const idealHeight = (this.boardModel.boardRows * this.cellSize) + 60; gridFatherTransform.setContentSize(new Size(idealWidth, idealHeight)); } const gridTransform = this.grid.getComponent(UITransform); if (gridTransform) { const idealWidth = this.boardModel.boardCols * this.cellSize; const idealHeight = this.boardModel.boardRows * this.cellSize; gridTransform.setContentSize(new Size(idealWidth, idealHeight)); } } updateBoardView() { // 更新棋盘视图逻辑 } private setKnittConfig() { const container = new Container(); container.setPathConfig("9,8,15,22,29,36;18,25,24,31;40,39,32;13,6,5,4,3,2,1,0,7,14,21,28,35,42,43,44,45,46,47,48,41;20,27,34;33,26,19,12,11,10,17,16,23,30,37,38;"); this.pathGroups = container.pathGroups; this.containerView = this.node.addComponent(ContainerView); this.containerView.init(container); // this.containerView.setPatternColors(this.patternColors); this.containerView.setemptyCell(this.emptyCell); this.containerView.Knitt = this.knittPrefab; this.containerView.KnittLink = this.KnittLink; this.containerView.KnittFlow = this.KnittFlow; // 设置触摸事件回调 this.containerView.setTouchCallbacks( this.onKnittTouchStart.bind(this), this.onKnittTouchMove.bind(this), this.onKnittTouchEnd.bind(this) ); this.containerView.setupKnittPaths(this.cellNodes, this.boardModel.boardCols, this.boardModel.boardRows); } // Knitt触摸开始回调 private onKnittTouchStart(cellIndex: number, groupIndex: number, event: EventTouch) { console.log(`Knitt触摸开始: 格子索引=${cellIndex}, 组索引=${groupIndex}`); const { row, col } = this.containerView.getRowColFromIndex(cellIndex, this.boardModel.boardCols); // 检查是否是路径组的起点或终点 const pathGroup = this.pathGroups.find(group => group.groupIndex === groupIndex); if (!pathGroup) return; const startIndex = pathGroup.path[0]; const endIndex = pathGroup.path[pathGroup.path.length - 1]; const { row: startRow, col: startCol } = this.containerView.getRowColFromIndex(startIndex, this.boardModel.boardCols); const { row: endRow, col: endCol } = this.containerView.getRowColFromIndex(endIndex, this.boardModel.boardCols); // 检查是否点击的是起点或终点 const isStartPoint = (col === startCol && row === startRow); const isEndPoint = (col === endCol && row === endRow); if (!isStartPoint && !isEndPoint) { console.log(`点击的不是起点或终点,不允许开始绘制`); return; } // 检查是否已经完成了该路径组的绘制 const existingLineIndex = this.historyLines.findIndex(line => line.patternType === groupIndex); if (existingLineIndex !== -1) { // 如果路径已完成,清除该路径的所有链接节点 this.clearPathGroup(groupIndex); return; // 清除后直接返回,不开始新的绘制 } // 确定绘制方向 this.isReversed = isEndPoint; // 如果点击的是终点,则反向绘制 this.startDrawing(col, row, groupIndex); } // 清除指定路径组的所有链接节点 private clearPathGroup(patternType: PatternType) { // 从historyLines中移除该路径组的线条 const lineIndex = this.historyLines.findIndex(line => line.patternType === patternType); if (lineIndex !== -1) { const line = this.historyLines[lineIndex]; // 清除该线条对应的所有链接节点 if (line.linkNodes) { line.linkNodes.forEach(linkNode => { if (linkNode && linkNode.isValid) { linkNode.destroy(); } }); // 从总链接节点数组中移除 this.linkNodes = this.linkNodes.filter(linkNode => line.linkNodes.indexOf(linkNode) === -1 ); } this.historyLines.splice(lineIndex, 1); } console.log(`清除路径组${patternType}的绘制`); } // Knitt触摸移动回调 private onKnittTouchMove(cellIndex: number, groupIndex: number, event: EventTouch) { if (!this.isDrawing) return; const pos = event.getUILocation(); const touchPoint = new Vec2(pos.x, pos.y); this.updateDrawing(touchPoint, groupIndex); } // Knitt触摸结束回调 private onKnittTouchEnd(cellIndex: number, groupIndex: number, event: EventTouch) { if (!this.isDrawing) return; const { row, col } = this.containerView.getRowColFromIndex(cellIndex, this.boardModel.boardCols); this.endDrawing(col, row, groupIndex); } // ... existing code ... // 开始绘制 private startDrawing(x: number, y: number, patternType: PatternType) { // 重置之前的绘制状态(如果有) this.resetDrawingState(); this.isDrawing = true; this.startCell = { x, y }; this.startPatternType = patternType; this.currentCell = { x, y }; this.visitedCells = [{ x, y }]; this.currentLine = { points: [this.getCellCenterPoint(x, y)], cells: [{ x, y }], color: this.patternColors[patternType], width: 40, startCell: { x, y }, patternType: patternType, linkNodes: [] // 初始化链接节点数组 }; // 创建起始点(使用Knitt作为头部) const startPos = this.getCellCenterPoint(x, y); const startNode = instantiate(this.Knitt); // 使用Knitt作为头部 startNode.setParent(this.KnittLinkNode); // 设置起始点位置 startNode.setPosition(startPos.x, startPos.y); // 设置颜色 this.setupLinkColor(startNode, patternType); // 设置初始方向(默认朝右,可根据需要调整) startNode.setRotationFromEuler(0, 0, 0); // 保存链接节点引用 this.linkNodes.push(startNode); this.currentLine.linkNodes.push(startNode); console.log(`开始绘制路径: 起点(${x},${y}), 图案类型${patternType}, 反向:${this.isReversed}`); } // ... existing code ... // ... existing code ... // 更新绘制 private updateDrawing(touchPoint: Vec2, patternType: PatternType) { const nearestCell = this.findNearestCell(touchPoint); if (!nearestCell) return; const { x, y } = nearestCell; // 检查是否可以连接 if (this.canConnectToCell(x, y, patternType)) { // 检查路径是否与其他已完成路径交叉 const crossedLine = this.getLineCrossedByPath(this.currentCell.x, this.currentCell.y, x, y); if (crossedLine) { // 如果交叉,清除被交叉的路径 this.clearPathGroup(crossedLine.patternType); } // 检查是否是回退操作(回到已访问的单元格) const existingIndex = this.visitedCells.findIndex(cell => cell.x === x && cell.y === y); if (existingIndex !== -1) { // 回退操作 - 移除回退路径上的链接段 this.handlePathBacktrack(existingIndex); } else { // 前进操作 - 创建新的链接段 this.createLinkSegment(this.currentCell.x, this.currentCell.y, x, y, patternType); // 如果这是第一次移动,设置起始点的方向 if (this.currentLine.cells.length === 1) { this.updateStartNodeDirection(this.currentCell.x, this.currentCell.y, x, y); } } this.currentCell = { x, y }; // 更新访问过的单元格列表 if (existingIndex !== -1) { // 如果是回退,截断访问列表 this.visitedCells = this.visitedCells.slice(0, existingIndex + 1); // 同时截断线条点列表 this.currentLine.points = this.currentLine.points.slice(0, existingIndex + 1); this.currentLine.cells = this.currentLine.cells.slice(0, existingIndex + 1); // 同时截断链接节点列表 if (this.currentLine.linkNodes) { const removeCount = this.currentLine.linkNodes.length - existingIndex; for (let i = 0; i < removeCount; i++) { const linkNode = this.currentLine.linkNodes.pop(); if (linkNode && linkNode.isValid) { linkNode.destroy(); } } } } else { // 如果是前进,添加新单元格到访问列表 this.visitedCells.push({ x, y }); this.currentLine.points.push(this.getCellCenterPoint(x, y)); this.currentLine.cells.push({ x, y }); } // 检查是否连接到终点 if (this.isEndPoint(x, y, patternType)) { this.currentLine.endCell = { x, y }; this.completeDrawing(patternType); } } } // ... existing code ... // ... existing code ... // 更新起始节点方向 private updateStartNodeDirection(fromX: number, fromY: number, toX: number, toY: number) { if (this.currentLine.linkNodes.length > 0) { const startNode = this.currentLine.linkNodes[0]; // 起始节点是第一个节点 if (startNode && startNode.isValid) { // 计算方向 const dx = toX - fromX; const dy = toY - fromY; const angle = Math.atan2(dy, dx) * 180 / Math.PI; // 设置起始节点方向 startNode.setRotationFromEuler(0, 0, angle); } } } // ... existing code ... // 处理路径回退 private handlePathBacktrack(backIndex: number) { // 计算需要移除的链接段数量 const removeCount = this.visitedCells.length - backIndex - 1; // 从linkNodes中移除对应的链接段 for (let i = 0; i < removeCount; i++) { if (this.linkNodes.length > 0) { const linkNode = this.linkNodes.pop(); if (linkNode && linkNode.isValid) { linkNode.destroy(); } } } console.log(`路径回退: 移除${removeCount}个链接段`); } // ... existing code ... // 创建链接段 private createLinkSegment(fromX: number, fromY: number, toX: number, toY: number, patternType: PatternType) { // 创建从起点到终点的线段 const fromPos = this.getCellCenterPoint(fromX, fromY); const toPos = this.getCellCenterPoint(toX, toY); // 先将前一个"线头"转换为"线身" if (this.currentLine.linkNodes.length > 0) { const prevHeadNode = this.currentLine.linkNodes[this.currentLine.linkNodes.length - 1]; if (prevHeadNode && prevHeadNode.isValid) { // 销毁旧的线头节点 prevHeadNode.destroy(); // 从数组中移除 this.currentLine.linkNodes.pop(); this.linkNodes.pop(); } } // 检查是否是拐角(需要至少3个点才能判断是否拐角) let isTurn = false; if (this.currentLine.cells.length >= 2) { const prevCell = this.currentLine.cells[this.currentLine.cells.length - 2]; const currentCell = this.currentLine.cells[this.currentLine.cells.length - 1]; // 判断是否拐角:前一个方向和当前方向不同 const prevDirX = currentCell.x - prevCell.x; const prevDirY = currentCell.y - prevCell.y; const currentDirX = toX - currentCell.x; const currentDirY = toY - currentCell.y; // 如果方向改变(叉积不为0),则是拐角 if (prevDirX * currentDirY - prevDirY * currentDirX !== 0) { isTurn = true; } } // 根据是否拐角选择不同的预制体 const bodyPrefab = isTurn ? this.KnittTurn : this.KnittLink; if (!bodyPrefab) return; // 创建新的线身 const bodyNode = instantiate(bodyPrefab); bodyNode.setParent(this.KnittLinkNode); if (isTurn) { // 处理拐点方向 this.updateTurnTransform(bodyNode, fromX, fromY, toX, toY); } else { // 处理普通线段 this.updateLinkTransform(bodyNode, fromPos, toPos); } this.setupLinkColor(bodyNode, patternType); this.linkNodes.push(bodyNode); this.currentLine.linkNodes.push(bodyNode); // 创建新的线头(使用Knitt预制体) const headNode = instantiate(this.Knitt); headNode.setParent(this.KnittLinkNode); headNode.setPosition(toPos.x, toPos.y); this.setupLinkColor(headNode, patternType); // 设置线头方向,使其面向移动方向(从fromPos到toPos) const dx = toPos.x - fromPos.x; const dy = toPos.y - fromPos.y; const angle = Math.atan2(dy, dx) * 180 / Math.PI; // 因为线头的右边是头,所以不需要额外调整角度 headNode.setRotationFromEuler(0, 0, angle); this.linkNodes.push(headNode); this.currentLine.linkNodes.push(headNode); this.currentLinkNode = headNode; console.log(`创建链接段: 从(${fromX},${fromY})到(${toX},${toY}), 拐角: ${isTurn}`); } // 更新拐点的变换 private updateTurnTransform(turnNode: Node, fromX: number, fromY: number, toX: number, toY: number) { // 获取拐点前一个点 if (this.currentLine.cells.length < 2) return; const prevCell = this.currentLine.cells[this.currentLine.cells.length - 2]; const currentCell = { x: fromX, y: fromY }; // 当前点 const nextCell = { x: toX, y: toY }; // 下一个点 // 计算拐点位置(当前格子中心) const turnPos = this.getCellCenterPoint(currentCell.x, currentCell.y); turnNode.setPosition(turnPos.x, turnPos.y); // 设置拐点的缩放为1,1,1 turnNode.setScale(1, 1, 1); // 计算进入方向和离开方向 const inDirX = currentCell.x - prevCell.x; const inDirY = currentCell.y - prevCell.y; const outDirX = nextCell.x - currentCell.x; const outDirY = nextCell.y - currentCell.y; // 根据进入和离开方向计算旋转角度 let angle = 0; // 判断拐角类型并设置正确的角度 if (inDirX === 1 && inDirY === 0) { // 从右进入 if (outDirX === 0 && outDirY === 1) { // 向下离开 angle = 0; // 右下拐角,需要旋转180度 } else if (outDirX === 0 && outDirY === -1) { // 向上离开 angle = -90; // 右上拐角,需要顺时针旋转90度 } } else if (inDirX === -1 && inDirY === 0) { // 从左进入 if (outDirX === 0 && outDirY === 1) { // 向下离开 angle = 90; // 左下拐角,与默认方向一致 } else if (outDirX === 0 && outDirY === -1) { // 向上离开 angle = 180; // 左上拐角,需要逆时针旋转90度 } } else if (inDirX === 0 && inDirY === 1) { // 从下进入 if (outDirX === 1 && outDirY === 0) { // 向右离开 angle = 180; // 下右拐角,需要逆时针旋转90度 } else if (outDirX === -1 && outDirY === 0) { // 向左离开 angle = -90; // 下左拐角,需要旋转180度 } } else if (inDirX === 0 && inDirY === -1) { // 从上进入 if (outDirX === 1 && outDirY === 0) { // 向右离开 angle = 90; // 上右拐角,需要顺时针旋转90度 } else if (outDirX === -1 && outDirY === 0) { // 向左离开 angle = 0; // 上左拐角,需要顺时针旋转90度 } } turnNode.setRotationFromEuler(0, 0, angle); } // ... existing code ... // 创建平滑曲线连接 private createSmoothCurve(fromPos: Vec2, toPos: Vec2, patternType: PatternType) { // 创建多个节点来模拟曲线 const segments = 8; // 曲线分段数 const controlPoints = this.calculateControlPoints(fromPos, toPos); for (let i = 0; i < segments; i++) { const t1 = i / segments; const t2 = (i + 1) / segments; // 计算贝塞尔曲线上的两点 const point1 = this.calculateBezierPoint(t1, fromPos, controlPoints.control1, controlPoints.control2, toPos); const point2 = this.calculateBezierPoint(t2, fromPos, controlPoints.control1, controlPoints.control2, toPos); // 创建线段 const linkNode = instantiate(this.Knitt); linkNode.setParent(this.KnittLinkNode); // 设置线段位置和旋转 this.updateLinkTransform(linkNode, point1, point2); // 设置颜色 this.setupLinkColor(linkNode, patternType); // 保存链接节点引用 this.linkNodes.push(linkNode); if (this.currentLine.linkNodes) { this.currentLine.linkNodes.push(linkNode); } } } // 计算控制点以创建自然的曲线效果 private calculateControlPoints(fromPos: Vec2, toPos: Vec2): { control1: Vec2, control2: Vec2 } { const dx = toPos.x - fromPos.x; const dy = toPos.y - fromPos.y; // 控制点距离基于方向差异 const controlDistance = Math.sqrt(dx * dx + dy * dy) * 0.5; // 创建控制点,使线条呈现S形弯曲效果 const control1 = new Vec2( fromPos.x + dx * 0.25 + (-dy * 0.2), fromPos.y + dy * 0.25 + (dx * 0.2) ); const control2 = new Vec2( fromPos.x + dx * 0.75 + (dy * 0.2), fromPos.y + dy * 0.75 + (-dx * 0.2) ); return { control1, control2 }; } // 计算贝塞尔曲线上的点 private calculateBezierPoint(t: number, p0: Vec2, p1: Vec2, p2: Vec2, p3: Vec2): Vec2 { const u = 1 - t; const tt = t * t; const uu = u * u; const uuu = uu * u; const ttt = tt * t; const p = new Vec2(); p.x = uuu * p0.x + 3 * uu * t * p1.x + 3 * u * tt * p2.x + ttt * p3.x; p.y = uuu * p0.y + 3 * uu * t * p1.y + 3 * u * tt * p2.y + ttt * p3.y; return p; } // ... existing code ... // 更新Link的变换 private updateLinkTransform(linkNode: Node, fromPos: Vec2, toPos: Vec2) { // 计算中心点 const centerX = (fromPos.x + toPos.x) / 2; const centerY = (fromPos.y + toPos.y) / 2; linkNode.setPosition(centerX, centerY); // 计算长度和旋转 const dx = toPos.x - fromPos.x; const dy = toPos.y - fromPos.y; const distance = Math.sqrt(dx * dx + dy * dy); const angle = Math.atan2(dy, dx) * 180 / Math.PI; // 获取Link节点的实际宽度以正确缩放 const linkUITransform = linkNode.getComponent(UITransform); const linkWidth = linkUITransform ? linkUITransform.width : 100; // 设置缩放(长度),使用实际宽度而不是硬编码的100 linkNode.setScale(distance / linkWidth, 1, 1); // 设置旋转 linkNode.setRotationFromEuler(0, 0, angle); } // ... existing code ... // 结束绘制 private endDrawing(x: number, y: number, patternType: PatternType) { // 检查是否连接到另一个端点 if (this.isValidEndPoint(x, y, patternType)) { this.currentLine.endCell = { x, y }; this.completeDrawing(patternType); } else { // 如果不是有效终点,取消绘制 this.cancelDrawing(); } } // 检查是否是有效的终点(另一个端点) private isValidEndPoint(x: number, y: number, patternType: PatternType): boolean { // 查找对应路径组的起点和终点 const pathGroup = this.pathGroups.find(group => group.groupIndex === patternType); if (!pathGroup) return false; const startIndex = pathGroup.path[0]; const endIndex = pathGroup.path[pathGroup.path.length - 1]; const { row: startRow, col: startCol } = this.containerView.getRowColFromIndex(startIndex, this.boardModel.boardCols); const { row: endRow, col: endCol } = this.containerView.getRowColFromIndex(endIndex, this.boardModel.boardCols); // 根据绘制方向确定终点 let targetPoint; if (this.isReversed) { // 反向绘制:终点应该是起点 targetPoint = { x: startCol, y: startRow }; } else { // 正向绘制:终点应该是终点 targetPoint = { x: endCol, y: endRow }; } // 检查当前格子是否是目标终点格子 const isEndPoint = (x === targetPoint.x && y === targetPoint.y); if (isEndPoint) { console.log(`成功连接到终点: 图案类型${patternType}, 终点(${x},${y})`); return true; } //console.log(`无效终点: 当前(${x},${y})不是图案类型${patternType}的目标终点`); return false; } // ... existing code ... // 完成绘制 private completeDrawing(patternType: PatternType) { // 将当前线条添加到历史记录 this.historyLines.push(this.currentLine); // 检查是否完成所有路径 if (this.checkAllPathsCompleted()) { console.log("所有路径完成,游戏胜利!"); this.showWinEffect(); } this.resetDrawingState(); console.log(`图案类型${patternType}的路径绘制完成`); } // 取消绘制 private cancelDrawing() { // 删除当前绘制过程中的链接段 if (this.currentLine && this.currentLine.linkNodes) { this.currentLine.linkNodes.forEach(linkNode => { if (linkNode && linkNode.isValid) { linkNode.destroy(); } }); // 从总链接节点数组中移除 this.linkNodes = this.linkNodes.filter(linkNode => this.currentLine.linkNodes.indexOf(linkNode) === -1 ); } this.resetDrawingState(); console.log("绘制取消"); } // ... existing code ... // 重置绘制状态 private resetDrawingState() { this.isDrawing = false; this.currentLine = null; this.startCell = null; this.startPatternType = null; this.currentCell = null; this.visitedCells = []; this.currentLinkNode = null; this.isReversed = false; } // 设置Link颜色 private setupLinkColor(linkNode: Node, patternType: PatternType) { const color = this.patternColors[patternType]; const sprite = linkNode.getComponent(Sprite); if (sprite) { sprite.color = color; } // 递归设置子节点颜色 this.setChildSpritesColor(linkNode, color); } // 递归设置子节点Sprite颜色 private setChildSpritesColor(node: Node, color: Color) { const sprite = node.getComponent(Sprite); if (sprite) { sprite.color = color; } node.children.forEach(child => this.setChildSpritesColor(child, color)); } // 查找最近的格子 private findNearestCell(point: Vec2): { x: number, y: number } | null { let minDistance = Infinity; let nearestCell = null; for (let y = 0; y < this.boardModel.boardRows; y++) { for (let x = 0; x < this.boardModel.boardCols; x++) { const cellCenter = this.getCellCenterPoint(x, y); const distance = Vec2.distance(point, cellCenter); const threshold = this.cellSize * 0.6; if (distance < minDistance && distance < threshold) { minDistance = distance; nearestCell = { x, y }; } } } return nearestCell; } // 检查是否可以连接到格子 private canConnectToCell(x: number, y: number, patternType: PatternType): boolean { // 检查是否相邻 if (!this.isNeighbor(this.currentCell.x, this.currentCell.y, x, y)) { return false; } // 检查是否已经在路径上(允许回退) const isOnPath = this.visitedCells.some(cell => cell.x === x && cell.y === y); if (isOnPath) { return true; } // 检查格子是否被其他路径占用 if (this.isCellOccupied(x, y)) { return false; } // 新增:检查目标格子是否有其他路径组的Knitt if (this.hasKnittOfOtherGroup(x, y, patternType)) { console.log(`目标格子(${x},${y})有其他路径组的Knitt,不允许连接`); return false; } return true; } // 检查两个格子是否相邻 private isNeighbor(x1: number, y1: number, x2: number, y2: number): boolean { const dx = Math.abs(x1 - x2); const dy = Math.abs(y1 - y2); return (dx === 1 && dy === 0) || (dx === 0 && dy === 1); } // 检查格子是否被占用 private isCellOccupied(x: number, y: number): boolean { return this.historyLines.some(line => line.cells.some(cell => cell.x === x && cell.y === y) ); } // 检查目标格子是否有其他路径组的Knitt private hasKnittOfOtherGroup(x: number, y: number, currentPatternType: PatternType): boolean { // 计算当前格子的索引 const cellIndex = y * this.boardModel.boardCols + x; // 检查这个格子是否属于任何路径组 for (const pathGroup of this.pathGroups) { // 如果格子属于某个路径组,但不是当前路径组 if (pathGroup.path.indexOf(cellIndex) !== -1 && pathGroup.groupIndex !== currentPatternType) { // 检查这个格子是否是起点或终点 const isStartOrEnd = cellIndex === pathGroup.path[0] || cellIndex === pathGroup.path[pathGroup.path.length - 1]; if (isStartOrEnd) { console.log(`格子(${x},${y})是路径组${pathGroup.groupIndex}的起点或终点,不允许连接`); return true; } } } return false; } // 检查路径段是否与其他已完成路径交叉 private getLineCrossedByPath(fromX: number, fromY: number, toX: number, toY: number): Line | null { // 检查从(fromX, fromY)到(toX, toY)的线段是否与任何历史线段交叉 for (const line of this.historyLines) { // 检查相邻格子之间的连线是否与历史路径交叉 for (let i = 0; i < line.cells.length - 1; i++) { const cell1 = line.cells[i]; const cell2 = line.cells[i + 1]; // 检查是否是相邻格子 if (this.isNeighbor(cell1.x, cell1.y, cell2.x, cell2.y)) { // 检查两条线段是否交叉 if (this.isLineSegmentCrossing(fromX, fromY, toX, toY, cell1.x, cell1.y, cell2.x, cell2.y)) { return line; } } } } return null; } // 检查两条线段是否交叉 private isLineSegmentCrossing(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number): boolean { // 使用向量叉积方法检测线段是否相交 function cross(o: { x: number, y: number }, a: { x: number, y: number }, b: { x: number, y: number }) { return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x); } const p1 = { x: x1, y: y1 }; const p2 = { x: x2, y: y2 }; const p3 = { x: x3, y: y3 }; const p4 = { x: x4, y: y4 }; // 快速排斥试验 if (Math.max(p1.x, p2.x) < Math.min(p3.x, p4.x) || Math.max(p3.x, p4.x) < Math.min(p1.x, p2.x) || Math.max(p1.y, p2.y) < Math.min(p3.y, p4.y) || Math.max(p3.y, p4.y) < Math.min(p1.y, p2.y)) { return false; } // 跨立试验 if (cross(p1, p2, p3) * cross(p1, p2, p4) <= 0 && cross(p3, p4, p1) * cross(p3, p4, p2) <= 0) { // 排除端点重合的情况 if ((p1.x === p3.x && p1.y === p3.y) || (p1.x === p4.x && p1.y === p4.y) || (p2.x === p3.x && p2.y === p3.y) || (p2.x === p4.x && p2.y === p4.y)) { return false; } return true; } return false; } // 检查是否是终点(兼容旧代码) private isEndPoint(x: number, y: number, patternType: PatternType): boolean { return this.isValidEndPoint(x, y, patternType); } // 检查所有路径是否完成(简化版) private checkAllPathsCompleted(): boolean { // 1. 检查每个路径组是否都有对应的完成线条 for (const pathGroup of this.pathGroups) { const line = this.historyLines.find(line => line.patternType === pathGroup.groupIndex && line.endCell !== undefined ); if (!line) { console.log(`路径组${pathGroup.groupIndex}未完成:未找到线条`); return false; } // 2. 检查线条是否覆盖了路径组的所有格子 const pathGroupCells = new Set(pathGroup.path); const lineCells = new Set(line.cells.map(cell => cell.y * this.boardModel.boardCols + cell.x )); // 检查路径组的所有格子是否都被线条覆盖 for (const cellIndex of pathGroup.path) { if (!lineCells.has(cellIndex)) { console.log(`路径组${pathGroup.groupIndex}未完成:格子${cellIndex}未被覆盖`); return false; } } } // 3. 检查地图上是否还有空格子(可选,根据你的需求) if (this.hasEmptyCells()) { console.log("还有空格子未连接"); return false; } console.log("所有路径组均已完成"); return true; } // 检查是否有空格子 private hasEmptyCells(): boolean { const allPathCells = new Set(); // 收集所有路径组定义的格子 for (const pathGroup of this.pathGroups) { pathGroup.path.forEach(index => allPathCells.add(index)); } // 收集所有已连接格子的索引 const connectedCells = new Set(); for (const line of this.historyLines) { line.cells.forEach(cell => { const index = cell.y * this.boardModel.boardCols + cell.x; connectedCells.add(index); }); } // 如果有路径组的格子没有被连接,就认为有空格子 for (const cellIndex of allPathCells) { if (!connectedCells.has(cellIndex)) { return true; } } return false; } // 获取格子中心点 getCellCenterPoint(x: number, y: number): Vec2 { const cellNode = this.cellNodes[y][x]; if (!cellNode) return new Vec2(0, 0); // 获取格子的世界坐标 const worldPos = cellNode.worldPosition; // 将世界坐标转换为KnittLinkNode的本地坐标(如果KnittLinkNode存在的话) if (this.KnittLinkNode) { const localPos = new Vec3(); this.KnittLinkNode.inverseTransformPoint(localPos, new Vec3(worldPos.x, worldPos.y, 0)); return new Vec2(localPos.x, localPos.y); } // 如果KnittLinkNode不存在,则使用grid作为备选 if (this.grid) { const localPos = new Vec3(); this.grid.inverseTransformPoint(localPos, new Vec3(worldPos.x, worldPos.y, 0)); return new Vec2(localPos.x, localPos.y); } // 如果都没有,则返回世界坐标 return new Vec2(worldPos.x, worldPos.y); } // 显示胜利效果 private showWinEffect() { console.log("恭喜你赢了!"); aa.uIMgr.showUI(UI_Win); } }