import { _decorator, Component, EventTouch, Material, Node, Sprite, UIOpacity, UITransform, v3, Vec2, Vec3 } from 'cc'; import { AudioManager } from '../AudioManager'; import { Url } from '../Url'; import { BlockController } from './Block/BlockController'; import { BlockConfig, BlocksAll, BlockState, ReConfig, Region } from './Block/BlockData'; import GlobalData from './GlobalData'; import { GridBlockMgr } from './GridBlockMgr'; import { Guide } from './Guide'; import { MainGameLogic } from './MainGameLogic'; import ResSprite from '../ResSprite'; const { ccclass, property } = _decorator; @ccclass('TouchMgr') export class TouchMgr extends Component { /** 单例模式 */ private static _ins: TouchMgr; constructor() { super(); TouchMgr._ins = this; } public static get ins(): TouchMgr { if (!TouchMgr._ins) { TouchMgr._ins = new TouchMgr(); } return TouchMgr._ins; } @property(Node) selection_container: Node = null; //三个方块的选择区 @property(Node) select1: Node = null; @property(Node) select2: Node = null; @property(Node) select3: Node = null; //三个方块的选择区的状态 selectCanTouch: boolean[] = [false, false, false] @property(Node) tempBlock: Node = null; // 临时方块 @property(Material) material: Material = null; @property(Material) defaultmaterial: Material = null; posOffset: Vec3 = new Vec3(0, 0, 0); isCanPlaced: boolean = false; // 是否可以放置 // 是否可以放置的索引数组 placedIndexArr: number[] = []; tempBlockColor: string = ''; // 临时方块的颜色 private longPressThreshold: number = 200; // 长按阈值,单位毫秒 private longPressTimerID: any = null; private isLongPress: boolean = false; // 是否长按 vec3Reusable: Vec3 = new Vec3(); private enlargedBlocks: Node[] = []; // 记录上一次放大的块 private touchStartPos: Vec2; onLoad() { this.select1.on(Node.EventType.TOUCH_START, this.onTouchStart, this); this.select1.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this); this.select1.on(Node.EventType.TOUCH_END, this.onTouchEnd, this); this.select1.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this); this.select2.on(Node.EventType.TOUCH_START, this.onTouchStart, this); this.select2.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this); this.select2.on(Node.EventType.TOUCH_END, this.onTouchEnd, this); this.select2.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this); this.select3.on(Node.EventType.TOUCH_START, this.onTouchStart, this); this.select3.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this); this.select3.on(Node.EventType.TOUCH_END, this.onTouchEnd, this); this.select3.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this); } start() { // log(this.selectCanTouch) // const p = this.getRegion(new Vec3(0, 300, 0)); // console.log(GridBlockMgr.ins.block_ba_data[p.i][p.j].block_ba_node.position); // console.log(GridBlockMgr.ins.block_ba_data[p.i][p.j].index_i, GridBlockMgr.ins.block_ba_data[p.i][p.j].index_j); } //长按的数据 private longPressEle: { event: EventTouch, touch_node: Node, target_block: Node, target_block_config: BlockConfig } onTouchStart(event: EventTouch) { AudioManager.ins.playOneShot(Url.AUDIO.SFX2, 1); this.isLongPress = false; this.resumePromptBlock(); this.touchStartPos = event.getUILocation(); let touch_node = event.getCurrentTarget() as Node; let touchSelectIndex = touch_node.getSiblingIndex(); if (!this.selectCanTouch[touchSelectIndex]) { return; } // 获取点击的目标block let target_block = touch_node.children[0]; // 获取block的BlockController组件中的BlockConfig数据 let target_block_config: BlockConfig = target_block.getComponent(BlockController).curBlockConfig; this.tempBlockColor = target_block_config.block_color; this.longPressEle = { event, touch_node, target_block, target_block_config } // this.longPressTimerID = setTimeout(() => { // this.longPressFunc(this.longPressEle.event, this.longPressEle.touch_node, this.longPressEle.target_block, this.longPressEle.target_block_config); // }, this.longPressThreshold); } longPressFunc(event: EventTouch, touch_node: Node, target_block, target_block_config) { this.isLongPress = true; touch_node.children[1].active = false; // 点击之后下方展示block隐藏 target_block.active = false; // 生成一个要跟随鼠标移动的临时block,并将位置放在鼠标点击位置 this.tempBlock.active = true; this.tempBlock.getComponent(BlockController).updateBlockSet(false, target_block_config); const touch_pos = event.getUILocation(); const temp_touch_pos = this.getRelativePosition(this.selection_container, this.vec2ToVec3(touch_pos)); // 设置临时block的位置到点击位置 this.tempBlock.setPosition(temp_touch_pos.add(new Vec3(0, 250, 0))); // 将tempBlock位置放在点击位置的上方200处 this.tempBlock.getComponent(BlockController).addBlockOffset(2); // 获取临时tempBlock和鼠标之间的向量差 // this.posOffset = this.vectorDifference(temp_touch_pos, this.tempBlock.position); } calculateDistance(vec1: Vec2, vec2: Vec2): number { return Vec2.distance(vec1, vec2); } onTouchMove(event: EventTouch) { // clearTimeout(this.longPressTimerID); let touchMovePos = event.getUILocation(); // console.log(this.calculateDistance(touchMovePos, this.touchStartPos)); if (this.calculateDistance(touchMovePos, this.touchStartPos) <= 25) { //太短就不管 return } // console.log("MOVE"); this.longPressFunc(this.longPressEle.event, this.longPressEle.touch_node, this.longPressEle.target_block, this.longPressEle.target_block_config) this.resumePromptBlock(); let touch_node = event.getCurrentTarget() as Node; let touchSelectIndex = touch_node.getSiblingIndex(); if (!this.selectCanTouch[touchSelectIndex]) { return; } touch_node.children[1].active = false; // 获取鼠标当前位置并转换 let touch_pos = event.getUILocation(); // let temp_touch_pos = this.getRelativePosition(this.selection_container, this.vec2ToVec3(touch_pos)); const temp_touch_pos = this.getRelativePosition(this.selection_container, this.vec2ToVec3(touch_pos)); // 设置临时block的位置到点击位置 this.tempBlock.setPosition(temp_touch_pos.add(new Vec3(0, 250, 0))); // 将tempBlock位置放在点击位置的上方200处 this.tempBlock.getComponent(BlockController).addBlockOffset(2); let pos_to_grid = this.getNodeAToNodeBPoint(GridBlockMgr.ins.gridcontainer.parent, this.tempBlock); let indexi_j = this.getRegion(new Vec3(pos_to_grid.x, pos_to_grid.y, 0)); GridBlockMgr.ins.updateBoard(); if (indexi_j) { let date = GridBlockMgr.ins.block_ba_data[indexi_j.i][indexi_j.j]; let tempBlockData = this.tempBlock.getComponent(BlockController).curBlockConfig; if (!tempBlockData) { return; } let config = this.getCenteredBlocks(date.index_i, date.index_j, tempBlockData.block_arr, tempBlockData.block_color, this.tempBlock); // console.log(config); this.isCanPlaced = config.iscanplaced; this.placedIndexArr = config.indexarr; this.tempBlockColor = tempBlockData.block_color; } else { this.isCanPlaced = false; } } onTouchEnd(event: EventTouch) { this.resumePromptBlock(); let touch_node = event.getCurrentTarget() as Node; let touchSelectIndex = touch_node.getSiblingIndex(); if (!this.selectCanTouch[touchSelectIndex]) { return; } // if (this.longPressTimerID) { // clearTimeout(this.longPressTimerID); // this.longPressTimerID = null; // } if (this.isLongPress) { touch_node.children[1].active = false; GridBlockMgr.ins.updateBoard(); if (this.isCanPlaced) { //可以放置 AudioManager.ins.playOneShot(Url.AUDIO.SFX3, 1); this.tempBlock.active = false; for (let i = 0; i < this.placedIndexArr.length; i++) { //这做一个吸附效果,从上到下,快点放置上去,而不是全部一起直接放置,很生硬 let boardindex_i = this.placedIndexArr[i][0]; let boardindex_j = this.placedIndexArr[i][1]; GridBlockMgr.ins.block_ba_data[boardindex_i][boardindex_j].block_state = BlockState.SHOW; GridBlockMgr.ins.block_ba_data[boardindex_i][boardindex_j].block_ba_node.getComponent(UIOpacity).opacity = 255; GridBlockMgr.ins.block_ba_data[boardindex_i][boardindex_j].block_sprite.setSpriteFrame(this.tempBlockColor + '/spriteFrame'); GridBlockMgr.ins.block_ba_data[boardindex_i][boardindex_j].block_color = this.tempBlockColor; // node.getComponent(ResSprite).setSpriteFrame(blockConfig.block_color + '/spriteFrame'); } this.selectCanTouch[touch_node.getSiblingIndex()] = false; let is_empty = this.selectionConEmpty(); if (is_empty && GlobalData.guideRecord > 3) { // GridBlockMgr.ins.generateSpecificSelectionBlock(false); GridBlockMgr.ins.generateSelectionBlock(false); } GridBlockMgr.ins.checkAndUpdateBlocks(); } else { //不可以放置 let targetBlock = touch_node.children[0]; targetBlock.active = true; this.tempBlock.active = false; } this.isCanPlaced = false; if (!GlobalData.isCommer) { let isOver = GridBlockMgr.ins.detectGameOver(); // console.log(isOver); if (isOver) { MainGameLogic.ins.gameOver(); } } this.isLongPress = false; } else { //旋转逻辑 if (!GlobalData.isRotate) { return } let target_block_config = touch_node.children[0].getComponent(BlockController).curBlockConfig; if (target_block_config.block_type != 0 && target_block_config.block_type != 4 && target_block_config.block_type != 5) { touch_node.children[1].active = true; } let new_target_block_arr = this.rotateMatrix(target_block_config.block_arr); const matchingBlockConfig = this.findMatchingBlockConfig(new_target_block_arr); matchingBlockConfig.block_color = this.tempBlockColor; touch_node.children[0].getComponent(BlockController).updateBlockSet(true, matchingBlockConfig); touch_node.children[0].setPosition(Vec3.ZERO); touch_node.children[0].getComponent(BlockController).addBlockOffset(4); if (!GlobalData.isCommer) { let isOver = GridBlockMgr.ins.detectGameOver(); if (isOver) { MainGameLogic.ins.gameOver(); } } } GridBlockMgr.ins.detectSelectsBlockCanUse(false); } // 判断数组一致性 arraysEqual(arr1: number[][], arr2: number[][]): boolean { if (arr1.length !== arr2.length) return false; for (let i = 0; i < arr1.length; i++) { if (arr1[i].length !== arr2[i].length) return false; for (let j = 0; j < arr1[i].length; j++) { if (arr1[i][j] !== arr2[i][j]) return false; } } return true; } // 根据旋转后的二维数组,找到匹配的数据 public findMatchingBlockConfig(arr: number[][]): BlockConfig | null { for (const block of BlocksAll) { if (this.arraysEqual(block.block_arr, arr)) { return block; } } return null; } //获取地区 getRegion(pos: Vec3): Region | null { const nodeSize = 980; // 节点的大小 const gridSize = 8; // 区域的数量 const cellSize = 122.5; // 每个区域块的大小 // 中心点的偏移量 const halfNodeSize = nodeSize / 2; // 转换坐标到以中心点为原点的坐标系 const localX = pos.x + halfNodeSize; const localY = pos.y + halfNodeSize; // 计算坐标所在的区域块索引 const i = Math.floor((localY) / cellSize); const j = gridSize - 1 - Math.floor(localX / cellSize); // 检查索引是否在有效范围内 if (i >= 0 && i < gridSize && j >= 0 && j < gridSize) { return { i, j }; } else { return null; } } // 获取中心块的坐标和颜色 // index_i: 当前块的行索引 getCenteredBlocks(index_i: number, index_j: number, curBlockArr: number[][], curBlockColor: string, tempBlock: Node): ReConfig { if (!curBlockArr) { this.resumePromptBlock(); return { iscanplaced: false, indexarr: null }; } // 检查是否处于引导状态,并验证当前位置是否符合引导位置 if (GlobalData.isCommer) { this.resumePromptBlock(); if ((GlobalData.guideRecord == 1 && (index_i != Guide.ins.guideindex1[0] || index_j != Guide.ins.guideindex1[1])) || (GlobalData.guideRecord == 2 && (index_i != Guide.ins.guideindex2[0] || index_j != Guide.ins.guideindex2[1])) || (GlobalData.guideRecord == 3 && (index_i != Guide.ins.guideindex3[0] || index_j != Guide.ins.guideindex3[1]))) { return { iscanplaced: false, indexarr: null }; } } const blocks: (number | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null)); const blocks_ba_node: (Node | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null)); const blocks_index: (number[] | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null)); const startI = Math.max(index_i - 2, 0); const endI = Math.min(index_i + 2, GridBlockMgr.ins.block_ba_data.length - 1); const startJ = Math.max(index_j - 2, 0); const endJ = Math.min(index_j + 2, GridBlockMgr.ins.block_ba_data[0].length - 1); for (let i = startI; i <= endI; i++) { for (let j = startJ; j <= endJ; j++) { const block = GridBlockMgr.ins.block_ba_data[i][j].block_state; const block_ba_node = GridBlockMgr.ins.block_ba_data[i][j].block_ba_node; const block_index = [GridBlockMgr.ins.block_ba_data[i][j].index_i, GridBlockMgr.ins.block_ba_data[i][j].index_j]; blocks[i - (index_i - 2)][j - (index_j - 2)] = block; blocks_ba_node[i - (index_i - 2)][j - (index_j - 2)] = block_ba_node; blocks_index[i - (index_i - 2)][j - (index_j - 2)] = block_index; } } // 检查当前位置是否可以放置块 for (let i = 0; i < 5; i++) { for (let j = 0; j < 5; j++) { const block = blocks[i][j]; const curBlock = curBlockArr[i][j]; if ((block === 1 && curBlock === 1) || (block === null && curBlock === 1)) { this.resumePromptBlock(); return { iscanplaced: false, indexarr: null }; } } } let returnIndexArr = []; // 显示提示位置 for (let i = 0; i < 5; i++) { for (let j = 0; j < 5; j++) { const block = blocks[i][j]; const curBlock = curBlockArr[i][j]; if (block === 0 && curBlock === 1) { blocks_ba_node[i][j].getComponent(UIOpacity).opacity = 150; blocks_ba_node[i][j].getComponent(ResSprite).setSpriteFrame(curBlockColor + '/spriteFrame') returnIndexArr.push(blocks_index[i][j]); } } } // 处理 tempBlock 的 25 个子节点并建立映射关系 const tempBlockChildren = tempBlock.children; const tempBlockArr: (Node | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null)); for (let i = 0; i < 5; i++) { for (let j = 0; j < 5; j++) { tempBlockArr[i][j] = tempBlockChildren[i * 5 + j]; } } // 创建临时棋盘数据对象,并模拟 tempBlock 已经放置 const tempGrid = Array(8).fill(null).map(() => Array(8).fill(0)); for (let i = 0; i < 8; i++) { for (let j = 0; j < 8; j++) { tempGrid[i][j] = GridBlockMgr.ins.block_ba_data[i][j].block_state; } } // 模拟放置 curBlockArr 到临时棋盘上 for (let i = 0; i < 5; i++) { for (let j = 0; j < 5; j++) { if (curBlockArr[i][j] === 1) { const gridI = index_i - 2 + i; const gridJ = index_j - 2 + j; if (gridI >= 0 && gridI < 8 && gridJ >= 0 && gridJ < 8) { tempGrid[gridI][gridJ] = 1; } } } } // 检查临时棋盘是否有可消除的行或列 let canEliminate = false; const rowsToCheck = []; const colsToCheck = []; for (let i = 0; i < 8; i++) { if (tempGrid[i].every(value => value === 1)) { canEliminate = true; rowsToCheck.push(i); } if (tempGrid.every(row => row[i] === 1)) { canEliminate = true; colsToCheck.push(i); } } if (canEliminate) { // 放大真实棋盘上可消除的块 rowsToCheck.forEach(row => { for (let j = 0; j < 8; j++) { const blockNode = GridBlockMgr.ins.block_ba_data[row][j].block_ba_node; if (blockNode && GridBlockMgr.ins.block_ba_data[row][j].block_state === BlockState.SHOW) { blockNode.getComponent(Sprite).material = this.material; this.enlargedBlocks.push(blockNode); // 记录放大的块 } } }); 5 colsToCheck.forEach(col => { for (let i = 0; i < 8; i++) { const blockNode = GridBlockMgr.ins.block_ba_data[i][col].block_ba_node; if (blockNode && GridBlockMgr.ins.block_ba_data[i][col].block_state === BlockState.SHOW) { blockNode.getComponent(Sprite).material = this.material; this.enlargedBlocks.push(blockNode); // 记录放大的块 } } }); // 放大 tempBlock 中会产生消除的块 for (let i = 0; i < 5; i++) { for (let j = 0; j < 5; j++) { if (curBlockArr[i][j] === 1) { const gridI = index_i - 2 + i; const gridJ = index_j - 2 + j; if (rowsToCheck.includes(gridI) || colsToCheck.includes(gridJ)) { tempBlockArr[i][j].getComponent(Sprite).material = this.material; this.enlargedBlocks.push(tempBlockArr[i][j]); // 记录放大的块 } } } } } else { this.resumePromptBlock(); } return { iscanplaced: true, indexarr: returnIndexArr }; } resumePromptBlock() { // 恢复上一次放大的块大小 this.enlargedBlocks.forEach(node => { node.getComponent(Sprite).material = this.defaultmaterial; }); this.enlargedBlocks = []; // 清空放大块的记录 } /** * 获取坐标在目标节点(容器)下的相对位置 * @param container 目标节点(容器) * @param nodepos 已有node节点坐标可传 */ public getRelativePosition(container: Node, nodepos?: Vec3,): Vec3 { //const worldPos = (node.getParent() || node).getComponent(UITransform).convertToWorldSpaceAR(nodepos ? nodepos : node.getPosition()); return container.getComponent(UITransform).convertToNodeSpaceAR(nodepos); } /** * 获取A相对B的局部坐标 * @param {*} nodeA * @param {*} nodeB */ public getNodeAToNodeBPoint(nodeA: Node, nodeB: Node) { var nodeAWorldPoint = nodeA.getComponent(UITransform).convertToWorldSpaceAR(Vec3.ZERO); //console.log("nodeAWorldPoint",nodeAWorldPoint); var AToBPos = nodeB.getComponent(UITransform).convertToNodeSpaceAR(nodeAWorldPoint); return v3(AToBPos.x, AToBPos.y, 1) //{ x: AToBPos.x, y: AToBPos.y }; } /** * 计算两个向量的差 * @param vec1 向量1 * @param vec2 向量2 * @returns */ vectorDifference(vec1: Vec3, vec2: Vec3): Vec3 { // 使用 cc.Vec3 的 subtract 方法计算向量差 const result = new Vec3(); Vec3.subtract(result, vec1, vec2); return result; } /** * * @param arr 判断选择区是否已经清空 * @returns */ selectionConEmpty(): boolean { for (let i = 0; i < this.selectCanTouch.length; i++) { if (this.selectCanTouch[i] !== false) { return false; } } return true; } /** * 二维数组旋转 * @param matrix 二维矩阵 * @param clockwise 是否顺时针 * @returns */ rotateMatrix(matrix: any[][], clockwise: boolean = true): any[][] { const N = matrix.length; const rotatedMatrix: any[][] = []; for (let i = 0; i < N; i++) { rotatedMatrix.push([]); } for (let i = 0; i < N; i++) { for (let j = 0; j < N; j++) { if (clockwise) { rotatedMatrix[j][N - 1 - i] = matrix[i][j]; } else { rotatedMatrix[N - 1 - j][i] = matrix[i][j]; } } } return rotatedMatrix; } /** * Vec2转Vec3 * @param vec2 一个Vec2 * @returns 一个Vec3 */ vec2ToVec3(vec2: Vec2): Vec3 { this.vec3Reusable.set(vec2.x, vec2.y, 0); return this.vec3Reusable; } }