TouchMgr.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. import { _decorator, Component, EventTouch, Material, Node, Sprite, UIOpacity, UITransform, v3, Vec2, Vec3 } from 'cc';
  2. import { AudioManager } from '../AudioManager';
  3. import { Url } from '../Url';
  4. import { BlockController } from './Block/BlockController';
  5. import { BlockConfig, BlocksAll, BlockState, ReConfig, Region } from './Block/BlockData';
  6. import GlobalData from './GlobalData';
  7. import { GridBlockMgr } from './GridBlockMgr';
  8. import { Guide } from './Guide';
  9. import { MainGameLogic } from './MainGameLogic';
  10. import ResSprite from '../ResSprite';
  11. const { ccclass, property } = _decorator;
  12. @ccclass('TouchMgr')
  13. export class TouchMgr extends Component {
  14. /** 单例模式 */
  15. private static _ins: TouchMgr;
  16. constructor() {
  17. super();
  18. TouchMgr._ins = this;
  19. }
  20. public static get ins(): TouchMgr {
  21. if (!TouchMgr._ins) {
  22. TouchMgr._ins = new TouchMgr();
  23. }
  24. return TouchMgr._ins;
  25. }
  26. @property(Node)
  27. selection_container: Node = null;
  28. //三个方块的选择区
  29. @property(Node)
  30. select1: Node = null;
  31. @property(Node)
  32. select2: Node = null;
  33. @property(Node)
  34. select3: Node = null;
  35. //三个方块的选择区的状态
  36. selectCanTouch: boolean[] = [false, false, false]
  37. @property(Node)
  38. tempBlock: Node = null; // 临时方块
  39. @property(Material)
  40. material: Material = null;
  41. @property(Material)
  42. defaultmaterial: Material = null;
  43. posOffset: Vec3 = new Vec3(0, 0, 0);
  44. isCanPlaced: boolean = false; // 是否可以放置
  45. // 是否可以放置的索引数组
  46. placedIndexArr: number[] = [];
  47. tempBlockColor: string = ''; // 临时方块的颜色
  48. private longPressThreshold: number = 200; // 长按阈值,单位毫秒
  49. private longPressTimerID: any = null;
  50. private isLongPress: boolean = false; // 是否长按
  51. vec3Reusable: Vec3 = new Vec3();
  52. private enlargedBlocks: Node[] = []; // 记录上一次放大的块
  53. private touchStartPos: Vec2;
  54. onLoad() {
  55. this.select1.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
  56. this.select1.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
  57. this.select1.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
  58. this.select1.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
  59. this.select2.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
  60. this.select2.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
  61. this.select2.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
  62. this.select2.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
  63. this.select3.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
  64. this.select3.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
  65. this.select3.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
  66. this.select3.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
  67. }
  68. start() {
  69. // log(this.selectCanTouch)
  70. // const p = this.getRegion(new Vec3(0, 300, 0));
  71. // console.log(GridBlockMgr.ins.block_ba_data[p.i][p.j].block_ba_node.position);
  72. // console.log(GridBlockMgr.ins.block_ba_data[p.i][p.j].index_i, GridBlockMgr.ins.block_ba_data[p.i][p.j].index_j);
  73. }
  74. //长按的数据
  75. private longPressEle: {
  76. event: EventTouch,
  77. touch_node: Node,
  78. target_block: Node,
  79. target_block_config: BlockConfig
  80. }
  81. onTouchStart(event: EventTouch) {
  82. AudioManager.ins.playOneShot(Url.AUDIO.SFX2, 1);
  83. this.isLongPress = false;
  84. this.resumePromptBlock();
  85. this.touchStartPos = event.getUILocation();
  86. let touch_node = event.getCurrentTarget() as Node;
  87. let touchSelectIndex = touch_node.getSiblingIndex();
  88. if (!this.selectCanTouch[touchSelectIndex]) {
  89. return;
  90. }
  91. // 获取点击的目标block
  92. let target_block = touch_node.children[0];
  93. // 获取block的BlockController组件中的BlockConfig数据
  94. let target_block_config: BlockConfig = target_block.getComponent(BlockController).curBlockConfig;
  95. this.tempBlockColor = target_block_config.block_color;
  96. this.longPressEle = {
  97. event,
  98. touch_node,
  99. target_block,
  100. target_block_config
  101. }
  102. // this.longPressTimerID = setTimeout(() => {
  103. // this.longPressFunc(this.longPressEle.event, this.longPressEle.touch_node, this.longPressEle.target_block, this.longPressEle.target_block_config);
  104. // }, this.longPressThreshold);
  105. }
  106. longPressFunc(event: EventTouch, touch_node: Node, target_block, target_block_config) {
  107. this.isLongPress = true;
  108. touch_node.children[1].active = false;
  109. // 点击之后下方展示block隐藏
  110. target_block.active = false;
  111. // 生成一个要跟随鼠标移动的临时block,并将位置放在鼠标点击位置
  112. this.tempBlock.active = true;
  113. this.tempBlock.getComponent(BlockController).updateBlockSet(false, target_block_config);
  114. const touch_pos = event.getUILocation();
  115. const temp_touch_pos = this.getRelativePosition(this.selection_container, this.vec2ToVec3(touch_pos));
  116. // 设置临时block的位置到点击位置
  117. this.tempBlock.setPosition(temp_touch_pos.add(new Vec3(0, 250, 0))); // 将tempBlock位置放在点击位置的上方200处
  118. this.tempBlock.getComponent(BlockController).addBlockOffset(2);
  119. // 获取临时tempBlock和鼠标之间的向量差
  120. // this.posOffset = this.vectorDifference(temp_touch_pos, this.tempBlock.position);
  121. }
  122. calculateDistance(vec1: Vec2, vec2: Vec2): number {
  123. return Vec2.distance(vec1, vec2);
  124. }
  125. onTouchMove(event: EventTouch) {
  126. // clearTimeout(this.longPressTimerID);
  127. let touchMovePos = event.getUILocation();
  128. // console.log(this.calculateDistance(touchMovePos, this.touchStartPos));
  129. if (this.calculateDistance(touchMovePos, this.touchStartPos) <= 25) {
  130. //太短就不管
  131. return
  132. }
  133. // console.log("MOVE");
  134. this.longPressFunc(this.longPressEle.event, this.longPressEle.touch_node, this.longPressEle.target_block, this.longPressEle.target_block_config)
  135. this.resumePromptBlock();
  136. let touch_node = event.getCurrentTarget() as Node;
  137. let touchSelectIndex = touch_node.getSiblingIndex();
  138. if (!this.selectCanTouch[touchSelectIndex]) {
  139. return;
  140. }
  141. touch_node.children[1].active = false;
  142. // 获取鼠标当前位置并转换
  143. let touch_pos = event.getUILocation();
  144. // let temp_touch_pos = this.getRelativePosition(this.selection_container, this.vec2ToVec3(touch_pos));
  145. const temp_touch_pos = this.getRelativePosition(this.selection_container, this.vec2ToVec3(touch_pos));
  146. // 设置临时block的位置到点击位置
  147. this.tempBlock.setPosition(temp_touch_pos.add(new Vec3(0, 250, 0))); // 将tempBlock位置放在点击位置的上方200处
  148. this.tempBlock.getComponent(BlockController).addBlockOffset(2);
  149. let pos_to_grid = this.getNodeAToNodeBPoint(GridBlockMgr.ins.gridcontainer.parent, this.tempBlock);
  150. let indexi_j = this.getRegion(new Vec3(pos_to_grid.x, pos_to_grid.y, 0));
  151. GridBlockMgr.ins.updateBoard();
  152. if (indexi_j) {
  153. let date = GridBlockMgr.ins.block_ba_data[indexi_j.i][indexi_j.j];
  154. let tempBlockData = this.tempBlock.getComponent(BlockController).curBlockConfig;
  155. if (!tempBlockData) {
  156. return;
  157. }
  158. let config = this.getCenteredBlocks(date.index_i, date.index_j, tempBlockData.block_arr, tempBlockData.block_color, this.tempBlock);
  159. // console.log(config);
  160. this.isCanPlaced = config.iscanplaced;
  161. this.placedIndexArr = config.indexarr;
  162. this.tempBlockColor = tempBlockData.block_color;
  163. } else {
  164. this.isCanPlaced = false;
  165. }
  166. }
  167. onTouchEnd(event: EventTouch) {
  168. this.resumePromptBlock();
  169. let touch_node = event.getCurrentTarget() as Node;
  170. let touchSelectIndex = touch_node.getSiblingIndex();
  171. if (!this.selectCanTouch[touchSelectIndex]) {
  172. return;
  173. }
  174. // if (this.longPressTimerID) {
  175. // clearTimeout(this.longPressTimerID);
  176. // this.longPressTimerID = null;
  177. // }
  178. if (this.isLongPress) {
  179. touch_node.children[1].active = false;
  180. GridBlockMgr.ins.updateBoard();
  181. if (this.isCanPlaced) {
  182. //可以放置
  183. AudioManager.ins.playOneShot(Url.AUDIO.SFX3, 1);
  184. this.tempBlock.active = false;
  185. for (let i = 0; i < this.placedIndexArr.length; i++) {
  186. //这做一个吸附效果,从上到下,快点放置上去,而不是全部一起直接放置,很生硬
  187. let boardindex_i = this.placedIndexArr[i][0];
  188. let boardindex_j = this.placedIndexArr[i][1];
  189. GridBlockMgr.ins.block_ba_data[boardindex_i][boardindex_j].block_state = BlockState.SHOW;
  190. GridBlockMgr.ins.block_ba_data[boardindex_i][boardindex_j].block_ba_node.getComponent(UIOpacity).opacity = 255;
  191. GridBlockMgr.ins.block_ba_data[boardindex_i][boardindex_j].block_sprite.setSpriteFrame(this.tempBlockColor + '/spriteFrame');
  192. GridBlockMgr.ins.block_ba_data[boardindex_i][boardindex_j].block_color = this.tempBlockColor;
  193. // node.getComponent(ResSprite).setSpriteFrame(blockConfig.block_color + '/spriteFrame');
  194. }
  195. this.selectCanTouch[touch_node.getSiblingIndex()] = false;
  196. let is_empty = this.selectionConEmpty();
  197. if (is_empty && GlobalData.guideRecord > 3) {
  198. // GridBlockMgr.ins.generateSpecificSelectionBlock(false);
  199. GridBlockMgr.ins.generateSelectionBlock(false);
  200. }
  201. GridBlockMgr.ins.checkAndUpdateBlocks();
  202. } else {
  203. //不可以放置
  204. let targetBlock = touch_node.children[0];
  205. targetBlock.active = true;
  206. this.tempBlock.active = false;
  207. }
  208. this.isCanPlaced = false;
  209. if (!GlobalData.isCommer) {
  210. let isOver = GridBlockMgr.ins.detectGameOver();
  211. // console.log(isOver);
  212. if (isOver) {
  213. MainGameLogic.ins.gameOver();
  214. }
  215. }
  216. this.isLongPress = false;
  217. } else {
  218. //旋转逻辑
  219. if (!GlobalData.isRotate) {
  220. return
  221. }
  222. let target_block_config = touch_node.children[0].getComponent(BlockController).curBlockConfig;
  223. if (target_block_config.block_type != 0 && target_block_config.block_type != 4 && target_block_config.block_type != 5) {
  224. touch_node.children[1].active = true;
  225. }
  226. let new_target_block_arr = this.rotateMatrix(target_block_config.block_arr);
  227. const matchingBlockConfig = this.findMatchingBlockConfig(new_target_block_arr);
  228. matchingBlockConfig.block_color = this.tempBlockColor;
  229. touch_node.children[0].getComponent(BlockController).updateBlockSet(true, matchingBlockConfig);
  230. touch_node.children[0].setPosition(Vec3.ZERO);
  231. touch_node.children[0].getComponent(BlockController).addBlockOffset(4);
  232. if (!GlobalData.isCommer) {
  233. let isOver = GridBlockMgr.ins.detectGameOver();
  234. if (isOver) {
  235. MainGameLogic.ins.gameOver();
  236. }
  237. }
  238. }
  239. GridBlockMgr.ins.detectSelectsBlockCanUse(false);
  240. }
  241. // 判断数组一致性
  242. arraysEqual(arr1: number[][], arr2: number[][]): boolean {
  243. if (arr1.length !== arr2.length) return false;
  244. for (let i = 0; i < arr1.length; i++) {
  245. if (arr1[i].length !== arr2[i].length) return false;
  246. for (let j = 0; j < arr1[i].length; j++) {
  247. if (arr1[i][j] !== arr2[i][j]) return false;
  248. }
  249. }
  250. return true;
  251. }
  252. // 根据旋转后的二维数组,找到匹配的数据
  253. public findMatchingBlockConfig(arr: number[][]): BlockConfig | null {
  254. for (const block of BlocksAll) {
  255. if (this.arraysEqual(block.block_arr, arr)) {
  256. return block;
  257. }
  258. }
  259. return null;
  260. }
  261. //获取地区
  262. getRegion(pos: Vec3): Region | null {
  263. const nodeSize = 980; // 节点的大小
  264. const gridSize = 8; // 区域的数量
  265. const cellSize = 122.5; // 每个区域块的大小
  266. // 中心点的偏移量
  267. const halfNodeSize = nodeSize / 2;
  268. // 转换坐标到以中心点为原点的坐标系
  269. const localX = pos.x + halfNodeSize;
  270. const localY = pos.y + halfNodeSize;
  271. // 计算坐标所在的区域块索引
  272. const i = Math.floor((localY) / cellSize);
  273. const j = gridSize - 1 - Math.floor(localX / cellSize);
  274. // 检查索引是否在有效范围内
  275. if (i >= 0 && i < gridSize && j >= 0 && j < gridSize) {
  276. return { i, j };
  277. } else {
  278. return null;
  279. }
  280. }
  281. // 获取中心块的坐标和颜色
  282. // index_i: 当前块的行索引
  283. getCenteredBlocks(index_i: number, index_j: number, curBlockArr: number[][], curBlockColor: string, tempBlock: Node): ReConfig {
  284. if (!curBlockArr) {
  285. this.resumePromptBlock();
  286. return { iscanplaced: false, indexarr: null };
  287. }
  288. // 检查是否处于引导状态,并验证当前位置是否符合引导位置
  289. if (GlobalData.isCommer) {
  290. this.resumePromptBlock();
  291. if ((GlobalData.guideRecord == 1 && (index_i != Guide.ins.guideindex1[0] || index_j != Guide.ins.guideindex1[1])) ||
  292. (GlobalData.guideRecord == 2 && (index_i != Guide.ins.guideindex2[0] || index_j != Guide.ins.guideindex2[1])) ||
  293. (GlobalData.guideRecord == 3 && (index_i != Guide.ins.guideindex3[0] || index_j != Guide.ins.guideindex3[1]))) {
  294. return { iscanplaced: false, indexarr: null };
  295. }
  296. }
  297. const blocks: (number | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null));
  298. const blocks_ba_node: (Node | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null));
  299. const blocks_index: (number[] | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null));
  300. const startI = Math.max(index_i - 2, 0);
  301. const endI = Math.min(index_i + 2, GridBlockMgr.ins.block_ba_data.length - 1);
  302. const startJ = Math.max(index_j - 2, 0);
  303. const endJ = Math.min(index_j + 2, GridBlockMgr.ins.block_ba_data[0].length - 1);
  304. for (let i = startI; i <= endI; i++) {
  305. for (let j = startJ; j <= endJ; j++) {
  306. const block = GridBlockMgr.ins.block_ba_data[i][j].block_state;
  307. const block_ba_node = GridBlockMgr.ins.block_ba_data[i][j].block_ba_node;
  308. const block_index = [GridBlockMgr.ins.block_ba_data[i][j].index_i, GridBlockMgr.ins.block_ba_data[i][j].index_j];
  309. blocks[i - (index_i - 2)][j - (index_j - 2)] = block;
  310. blocks_ba_node[i - (index_i - 2)][j - (index_j - 2)] = block_ba_node;
  311. blocks_index[i - (index_i - 2)][j - (index_j - 2)] = block_index;
  312. }
  313. }
  314. // 检查当前位置是否可以放置块
  315. for (let i = 0; i < 5; i++) {
  316. for (let j = 0; j < 5; j++) {
  317. const block = blocks[i][j];
  318. const curBlock = curBlockArr[i][j];
  319. if ((block === 1 && curBlock === 1) || (block === null && curBlock === 1)) {
  320. this.resumePromptBlock();
  321. return { iscanplaced: false, indexarr: null };
  322. }
  323. }
  324. }
  325. let returnIndexArr = [];
  326. // 显示提示位置
  327. for (let i = 0; i < 5; i++) {
  328. for (let j = 0; j < 5; j++) {
  329. const block = blocks[i][j];
  330. const curBlock = curBlockArr[i][j];
  331. if (block === 0 && curBlock === 1) {
  332. blocks_ba_node[i][j].getComponent(UIOpacity).opacity = 150;
  333. blocks_ba_node[i][j].getComponent(ResSprite).setSpriteFrame(curBlockColor + '/spriteFrame')
  334. returnIndexArr.push(blocks_index[i][j]);
  335. }
  336. }
  337. }
  338. // 处理 tempBlock 的 25 个子节点并建立映射关系
  339. const tempBlockChildren = tempBlock.children;
  340. const tempBlockArr: (Node | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null));
  341. for (let i = 0; i < 5; i++) {
  342. for (let j = 0; j < 5; j++) {
  343. tempBlockArr[i][j] = tempBlockChildren[i * 5 + j];
  344. }
  345. }
  346. // 创建临时棋盘数据对象,并模拟 tempBlock 已经放置
  347. const tempGrid = Array(8).fill(null).map(() => Array(8).fill(0));
  348. for (let i = 0; i < 8; i++) {
  349. for (let j = 0; j < 8; j++) {
  350. tempGrid[i][j] = GridBlockMgr.ins.block_ba_data[i][j].block_state;
  351. }
  352. }
  353. // 模拟放置 curBlockArr 到临时棋盘上
  354. for (let i = 0; i < 5; i++) {
  355. for (let j = 0; j < 5; j++) {
  356. if (curBlockArr[i][j] === 1) {
  357. const gridI = index_i - 2 + i;
  358. const gridJ = index_j - 2 + j;
  359. if (gridI >= 0 && gridI < 8 && gridJ >= 0 && gridJ < 8) {
  360. tempGrid[gridI][gridJ] = 1;
  361. }
  362. }
  363. }
  364. }
  365. // 检查临时棋盘是否有可消除的行或列
  366. let canEliminate = false;
  367. const rowsToCheck = [];
  368. const colsToCheck = [];
  369. for (let i = 0; i < 8; i++) {
  370. if (tempGrid[i].every(value => value === 1)) {
  371. canEliminate = true;
  372. rowsToCheck.push(i);
  373. }
  374. if (tempGrid.every(row => row[i] === 1)) {
  375. canEliminate = true;
  376. colsToCheck.push(i);
  377. }
  378. }
  379. if (canEliminate) {
  380. // 放大真实棋盘上可消除的块
  381. rowsToCheck.forEach(row => {
  382. for (let j = 0; j < 8; j++) {
  383. const blockNode = GridBlockMgr.ins.block_ba_data[row][j].block_ba_node;
  384. if (blockNode && GridBlockMgr.ins.block_ba_data[row][j].block_state === BlockState.SHOW) {
  385. blockNode.getComponent(Sprite).material = this.material;
  386. this.enlargedBlocks.push(blockNode); // 记录放大的块
  387. }
  388. }
  389. });
  390. 5
  391. colsToCheck.forEach(col => {
  392. for (let i = 0; i < 8; i++) {
  393. const blockNode = GridBlockMgr.ins.block_ba_data[i][col].block_ba_node;
  394. if (blockNode && GridBlockMgr.ins.block_ba_data[i][col].block_state === BlockState.SHOW) {
  395. blockNode.getComponent(Sprite).material = this.material;
  396. this.enlargedBlocks.push(blockNode); // 记录放大的块
  397. }
  398. }
  399. });
  400. // 放大 tempBlock 中会产生消除的块
  401. for (let i = 0; i < 5; i++) {
  402. for (let j = 0; j < 5; j++) {
  403. if (curBlockArr[i][j] === 1) {
  404. const gridI = index_i - 2 + i;
  405. const gridJ = index_j - 2 + j;
  406. if (rowsToCheck.includes(gridI) || colsToCheck.includes(gridJ)) {
  407. tempBlockArr[i][j].getComponent(Sprite).material = this.material;
  408. this.enlargedBlocks.push(tempBlockArr[i][j]); // 记录放大的块
  409. }
  410. }
  411. }
  412. }
  413. } else {
  414. this.resumePromptBlock();
  415. }
  416. return { iscanplaced: true, indexarr: returnIndexArr };
  417. }
  418. resumePromptBlock() {
  419. // 恢复上一次放大的块大小
  420. this.enlargedBlocks.forEach(node => {
  421. node.getComponent(Sprite).material = this.defaultmaterial;
  422. });
  423. this.enlargedBlocks = []; // 清空放大块的记录
  424. }
  425. /**
  426. * 获取坐标在目标节点(容器)下的相对位置
  427. * @param container 目标节点(容器)
  428. * @param nodepos 已有node节点坐标可传
  429. */
  430. public getRelativePosition(container: Node, nodepos?: Vec3,): Vec3 {
  431. //const worldPos = (node.getParent() || node).getComponent(UITransform).convertToWorldSpaceAR(nodepos ? nodepos : node.getPosition());
  432. return container.getComponent(UITransform).convertToNodeSpaceAR(nodepos);
  433. }
  434. /**
  435. * 获取A相对B的局部坐标
  436. * @param {*} nodeA
  437. * @param {*} nodeB
  438. */
  439. public getNodeAToNodeBPoint(nodeA: Node, nodeB: Node) {
  440. var nodeAWorldPoint = nodeA.getComponent(UITransform).convertToWorldSpaceAR(Vec3.ZERO);
  441. //console.log("nodeAWorldPoint",nodeAWorldPoint);
  442. var AToBPos = nodeB.getComponent(UITransform).convertToNodeSpaceAR(nodeAWorldPoint);
  443. return v3(AToBPos.x, AToBPos.y, 1) //{ x: AToBPos.x, y: AToBPos.y };
  444. }
  445. /**
  446. * 计算两个向量的差
  447. * @param vec1 向量1
  448. * @param vec2 向量2
  449. * @returns
  450. */
  451. vectorDifference(vec1: Vec3, vec2: Vec3): Vec3 {
  452. // 使用 cc.Vec3 的 subtract 方法计算向量差
  453. const result = new Vec3();
  454. Vec3.subtract(result, vec1, vec2);
  455. return result;
  456. }
  457. /**
  458. *
  459. * @param arr 判断选择区是否已经清空
  460. * @returns
  461. */
  462. selectionConEmpty(): boolean {
  463. for (let i = 0; i < this.selectCanTouch.length; i++) {
  464. if (this.selectCanTouch[i] !== false) {
  465. return false;
  466. }
  467. }
  468. return true;
  469. }
  470. /**
  471. * 二维数组旋转
  472. * @param matrix 二维矩阵
  473. * @param clockwise 是否顺时针
  474. * @returns
  475. */
  476. rotateMatrix(matrix: any[][], clockwise: boolean = true): any[][] {
  477. const N = matrix.length;
  478. const rotatedMatrix: any[][] = [];
  479. for (let i = 0; i < N; i++) {
  480. rotatedMatrix.push([]);
  481. }
  482. for (let i = 0; i < N; i++) {
  483. for (let j = 0; j < N; j++) {
  484. if (clockwise) {
  485. rotatedMatrix[j][N - 1 - i] = matrix[i][j];
  486. } else {
  487. rotatedMatrix[N - 1 - j][i] = matrix[i][j];
  488. }
  489. }
  490. }
  491. return rotatedMatrix;
  492. }
  493. /**
  494. * Vec2转Vec3
  495. * @param vec2 一个Vec2
  496. * @returns 一个Vec3
  497. */
  498. vec2ToVec3(vec2: Vec2): Vec3 {
  499. this.vec3Reusable.set(vec2.x, vec2.y, 0);
  500. return this.vec3Reusable;
  501. }
  502. }