| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020 |
- 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<PatternType, { x: number, y: number }[]> = 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);
- }
- }
|