GridBlockMgr.ts 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. import { _decorator, Component, instantiate, Node, Prefab, Sprite, tween, UIOpacity, v3, Vec3, view } from 'cc';
  2. import { BlockController } from './Block/BlockController';
  3. import { BlockBaData, BlockConfig, BlocksAll, BlockState, BlockType, BlockWeights, ReConfig } from './Block/BlockData';
  4. import GlobalData from './GlobalData';
  5. import { LocalStorageMgr } from './LocalStorageMgr';
  6. import { MainGameLogic } from './MainGameLogic';
  7. import { TouchMgr } from './TouchMgr';
  8. import { AudioManager } from '../AudioManager';
  9. import { Url } from '../Url';
  10. import ResSprite from '../ResSprite';
  11. const { ccclass, property } = _decorator;
  12. @ccclass('GridBlockMgr')
  13. export class GridBlockMgr extends Component {
  14. /** 单例模式 */
  15. private static _ins: GridBlockMgr;
  16. constructor() {
  17. super();
  18. GridBlockMgr._ins = this;
  19. }
  20. public static get ins(): GridBlockMgr {
  21. if (!GridBlockMgr._ins) {
  22. GridBlockMgr._ins = new GridBlockMgr();
  23. }
  24. return GridBlockMgr._ins;
  25. }
  26. @property(Prefab)
  27. block: Prefab = null;
  28. @property(Node)
  29. gridcontainer: Node = null;
  30. @property(Node)
  31. selects: Node[] = [];
  32. @property(Node)
  33. SelectionContainer: Node = null;
  34. private boardSize: number = 8;
  35. private cellSize: number = 122.5;
  36. block_ba_data: BlockBaData[][];
  37. temp_block_ba_data: BlockBaData[][];
  38. temp_block_ba_data1: BlockBaData[][];
  39. selection_block_type: BlockType[];
  40. selection_block_config: BlockConfig[]
  41. protected onLoad(): void {
  42. this.temp_block_ba_data = this.initializeBlockData();
  43. this.temp_block_ba_data1 = this.initializeBlockData();
  44. }
  45. async start() {
  46. }
  47. protected update(dt: number): void {
  48. }
  49. // 初始化 temp_block_ba_data 的方法
  50. initializeBlockData(): BlockBaData[][] {
  51. const size = 8; // 根据实际情况设置
  52. const data: BlockBaData[][] = [];
  53. for (let i = 0; i < size; i++) {
  54. const row: BlockBaData[] = [];
  55. for (let j = 0; j < size; j++) {
  56. row.push({
  57. block_state: BlockState.HIDE,
  58. block_ba_node: null, // 根据实际情况初始化
  59. index_i: i,
  60. index_j: j,
  61. block_sprite: null, // 根据实际情况初始化
  62. block_color: Url.BLOCK.ORANGE
  63. });
  64. }
  65. data.push(row);
  66. }
  67. return data;
  68. }
  69. // 检测消除
  70. checkAndUpdateBlocks(): Vec3 | null {
  71. const size = 8;
  72. const rowsToClear = new Set<number>();
  73. const colsToClear = new Set<number>();
  74. let anyCleared = false;
  75. let eliminNum = 0; // 消除的总块数
  76. let totalRow = 0; // 累积的行索引总和
  77. let totalCol = 0; // 累积的列索引总和
  78. let clearedRowCount = 0; // 消除的行数
  79. let clearedColCount = 0; // 消除的列数
  80. // 检查每一行
  81. for (let i = 0; i < size; i++) {
  82. let rowAllOnes = true;
  83. for (let j = 0; j < size; j++) {
  84. if (this.block_ba_data[i][j].block_state !== BlockState.SHOW) {
  85. rowAllOnes = false;
  86. break;
  87. }
  88. }
  89. if (rowAllOnes) {
  90. rowsToClear.add(i);
  91. anyCleared = true;
  92. clearedRowCount++;
  93. }
  94. }
  95. // 检查每一列
  96. for (let j = 0; j < size; j++) {
  97. let colAllOnes = true;
  98. for (let i = 0; i < size; i++) {
  99. if (this.block_ba_data[i][j].block_state !== BlockState.SHOW) {
  100. colAllOnes = false;
  101. break;
  102. }
  103. }
  104. if (colAllOnes) {
  105. colsToClear.add(j);
  106. anyCleared = true;
  107. clearedColCount++;
  108. }
  109. }
  110. let centerX = null;
  111. let centerY = null;
  112. let centerPosition: Vec3 = null;
  113. // 统一处理需要消除的行和列
  114. rowsToClear.forEach((i) => {
  115. for (let j = 0; j < size; j++) {
  116. const node = this.block_ba_data[i][j].block_ba_node;
  117. node.getComponent(UIOpacity).opacity = 0;
  118. this.block_ba_data[i][j].block_state = BlockState.HIDE;
  119. MainGameLogic.ins.createOneBlockEfEffect(node, this.block_ba_data[i][j].block_color);
  120. eliminNum++;
  121. totalRow += i;
  122. totalCol += j;
  123. }
  124. });
  125. colsToClear.forEach((j) => {
  126. for (let i = 0; i < size; i++) {
  127. const node = this.block_ba_data[i][j].block_ba_node;
  128. node.getComponent(UIOpacity).opacity = 0;
  129. this.block_ba_data[i][j].block_state = BlockState.HIDE;
  130. MainGameLogic.ins.createOneBlockEfEffect(node, this.block_ba_data[i][j].block_color);
  131. eliminNum++;
  132. totalRow += i;
  133. totalCol += j;
  134. }
  135. });
  136. if (anyCleared) {
  137. AudioManager.ins.playOneShot(Url.AUDIO.SFX10, 1);
  138. // 计算最中间块的索引位置
  139. centerX = Math.floor(totalRow / eliminNum);
  140. centerY = Math.floor(totalCol / eliminNum);
  141. // 获取最中间块的实际位置
  142. const centerNode: Node = this.block_ba_data[centerX][centerY].block_ba_node;
  143. centerPosition = centerNode.getPosition(); // 获取世界坐标
  144. // 将消除的行数和列数相加,传递给 detectGuideAndElimiTimes
  145. // console.log(clearedRowCount, clearedColCount);
  146. const totalClearedLines = clearedRowCount + clearedColCount;
  147. MainGameLogic.ins.detectGuideAndElimiTimes(totalClearedLines);
  148. // MainGameLogic.ins.getElimitateCash();
  149. // console.log('消除格子数', eliminNum);
  150. // console.log('最中间块坐标', centerPosition);
  151. let baseScore = mtec.number.random(GlobalData.addScoreFloor, GlobalData.addScoreUp);
  152. MainGameLogic.ins.createConsecutiveEffect(centerPosition, totalClearedLines);
  153. this.scheduleOnce(() => {
  154. MainGameLogic.ins.createEleAddScoreEff(centerPosition, baseScore * eliminNum);
  155. }, 1)
  156. }
  157. this.scheduleOnce(() => {
  158. this.saveCurrentBoardState();
  159. }, 1)
  160. // 返回最中间块的世界坐标
  161. return centerPosition;
  162. }
  163. /** 保存当前地图数据的方法 */
  164. saveCurrentBoardState(): void {
  165. const size = 8;
  166. const currentBoardData = [];
  167. for (let i = 0; i < size; i++) {
  168. const row = [];
  169. for (let j = 0; j < size; j++) {
  170. row.push(this.block_ba_data[i][j].block_state === BlockState.SHOW ? 1 : 0);
  171. }
  172. currentBoardData.push(row);
  173. }
  174. LocalStorageMgr.setItem(LocalStorageMgr.lastGameBoardData_key, currentBoardData)
  175. }
  176. // 检测棋盘相连区域,并将最多块数的区域消除-----道具使用
  177. chechBlockArea() {
  178. // 根据this.block_ba_data棋盘数据,检测格子相互之间相连的区域
  179. // 计算每个区域相邻的格子有多少,得出格子最最多的区域,将这个区域的格子全部隐藏(消除)
  180. // 定义方向数组,用于移动上下左右四个方向
  181. const directions = [
  182. { x: 0, y: 1 }, // 上
  183. { x: 1, y: 0 }, // 右
  184. { x: 0, y: -1 }, // 下
  185. { x: -1, y: 0 } // 左
  186. ];
  187. // 用于记录已经访问过的格子
  188. const visited = new Array(this.boardSize).fill(false).map(() => new Array(this.boardSize).fill(false));
  189. let maxArea = 0;
  190. let maxAreaBlocks: BlockBaData[] = [];
  191. // 深度优先搜索,检测相连区域
  192. const dfs = (i: number, j: number, currentBlocks: BlockBaData[]) => {
  193. visited[i][j] = true;
  194. currentBlocks.push(this.block_ba_data[i][j]);
  195. for (const direction of directions) {
  196. const newRow = i + direction.x;
  197. const newCol = j + direction.y;
  198. if (
  199. newRow >= 0 && newRow < this.boardSize &&
  200. newCol >= 0 && newCol < this.boardSize &&
  201. !visited[newRow][newCol] &&
  202. this.block_ba_data[newRow][newCol].block_state === BlockState.SHOW
  203. ) {
  204. dfs(newRow, newCol, currentBlocks);
  205. }
  206. }
  207. };
  208. // 遍历棋盘,找到所有区域
  209. for (let i = 0; i < this.boardSize; i++) {
  210. for (let j = 0; j < this.boardSize; j++) {
  211. if (
  212. !visited[i][j] &&
  213. this.block_ba_data[i][j].block_state === BlockState.SHOW
  214. ) {
  215. const currentBlocks: BlockBaData[] = [];
  216. dfs(i, j, currentBlocks);
  217. if (currentBlocks.length > maxArea) {
  218. maxArea = currentBlocks.length;
  219. maxAreaBlocks = currentBlocks;
  220. }
  221. }
  222. }
  223. }
  224. // 消除最大区域的格子
  225. for (const block of maxAreaBlocks) {
  226. block.block_ba_node.getComponent(UIOpacity).opacity = 0;
  227. block.block_state = BlockState.HIDE;
  228. }
  229. // console.log('最大区域的格子数:', maxArea);
  230. // console.log('消除的格子:', maxAreaBlocks);
  231. }
  232. // 检查并消除2x2区域,并返回消除区域的中心坐标
  233. chechAndEliminateRandomArea(): boolean {
  234. const regions: { blocks: BlockBaData[], row: number, col: number }[] = [];
  235. // 遍历棋盘,以2x2区域为单位进行划分
  236. for (let i = 0; i < this.boardSize; i += 4) {
  237. for (let j = 0; j < this.boardSize; j += 4) {
  238. const currentRegion: BlockBaData[] = [];
  239. // 遍历当前2x2区域的格子
  240. for (let x = i; x < i + 4; x++) {
  241. for (let y = j; y < j + 4; y++) {
  242. if (this.block_ba_data[x][y].block_state === BlockState.SHOW) {
  243. currentRegion.push(this.block_ba_data[x][y]);
  244. }
  245. }
  246. }
  247. // 如果该区域内有显示状态的格子,记录下来
  248. if (currentRegion.length > 0) {
  249. regions.push({ blocks: currentRegion, row: i, col: j });
  250. }
  251. }
  252. }
  253. // 如果没有找到任何显示状态的区域,直接返回 null
  254. if (regions.length === 0) {
  255. // console.log('没有区域内有格子处于显示状态');
  256. MainGameLogic.ins.propInUse = false;
  257. MainGameLogic.ins.createOneTipTpast('game_tip_noblock')
  258. return false;
  259. }
  260. // 随机选择一个区域
  261. const randomIndex = Math.floor(Math.random() * regions.length);
  262. const selectedRegion = regions[randomIndex];
  263. // 计算四个格子中心位置的平均值
  264. let centerX = 0, centerY = 0, centerZ = 0;
  265. for (const block of selectedRegion.blocks) {
  266. const position = block.block_ba_node.getPosition();
  267. centerX += position.x;
  268. centerY += position.y;
  269. centerZ += position.z;
  270. }
  271. const centerPosition = new Vec3(centerX / selectedRegion.blocks.length, centerY / selectedRegion.blocks.length, centerZ / selectedRegion.blocks.length);
  272. let fun = () => {
  273. // 消除选中区域的格子
  274. for (const block of selectedRegion.blocks) {
  275. block.block_ba_node.getComponent(UIOpacity).opacity = 0;
  276. block.block_state = BlockState.HIDE;
  277. MainGameLogic.ins.createOneBlockEfEffect(block.block_ba_node, block.block_color);
  278. }
  279. MainGameLogic.ins.propInUse = false;
  280. // console.log(`消除了区域 [${selectedRegion.row}, ${selectedRegion.col}] 内的格子`);
  281. }
  282. this.saveCurrentBoardState();
  283. // MainGameLogic.ins.usePropHummerEffect(centerPosition, fun)
  284. return true;
  285. }
  286. // 根据数据设置棋盘格子
  287. async generateBoard(_boardData) {
  288. // 初始化block_ba_data二维数组
  289. this.block_ba_data = [];
  290. for (let i = 0; i < this.boardSize; i++) {
  291. this.block_ba_data[i] = [];
  292. for (let j = 0; j < this.boardSize; j++) {
  293. let state = _boardData[i][j];
  294. let name = (i + 1) + '_' + (j + 1);
  295. let _block_ba = this.gridcontainer.getChildByName(name);
  296. _block_ba.getComponent(UIOpacity).opacity = state == 1 ? 255 : 0;
  297. _block_ba.getComponent(ResSprite).setSpriteFrame(Url.BLOCK.ORANGE + '/spriteFrame');
  298. // 创建BlockBaData对象并赋值
  299. const blockBaData: BlockBaData = {
  300. index_i: i,
  301. index_j: j,
  302. block_ba_node: _block_ba,
  303. block_state: state == 1 ? BlockState.SHOW : BlockState.HIDE,
  304. block_sprite: _block_ba.getComponent(ResSprite),
  305. block_color: Url.BLOCK.ORANGE
  306. };
  307. // 将blockBaData对象保存到block_ba_data二维数组中
  308. this.block_ba_data[i][j] = blockBaData;
  309. }
  310. }
  311. }
  312. // 根据数据设置棋盘格子_带效果
  313. async generateBoard_effect(_boardData) {
  314. // 播放音效
  315. AudioManager.ins.playOneShot(Url.AUDIO.SFX12, 1);
  316. // 初始化block_ba_data二维数组,快速完成初始化,没有延迟
  317. this.block_ba_data = [];
  318. for (let i = 0; i < this.boardSize; i++) {
  319. this.block_ba_data[i] = [];
  320. for (let j = 0; j < this.boardSize; j++) {
  321. let name = (i + 1) + '_' + (j + 1);
  322. let _block_ba = this.gridcontainer.getChildByName(name);
  323. // 创建BlockBaData对象并初始化状态为隐藏
  324. const blockBaData: BlockBaData = {
  325. index_i: i,
  326. index_j: j,
  327. block_ba_node: _block_ba,
  328. block_state: BlockState.HIDE,
  329. block_sprite: _block_ba.getComponent(ResSprite),
  330. block_color: Url.BLOCK.ORANGE
  331. };
  332. // 将blockBaData对象保存到block_ba_data二维数组中
  333. this.block_ba_data[i][j] = blockBaData;
  334. }
  335. }
  336. // 设置透明度的逐行显示效果,添加延迟
  337. for (let i = this.boardSize - 1; i >= 0; i--) {
  338. for (let j = 0; j < this.boardSize; j++) {
  339. let _block_ba = this.block_ba_data[i][j].block_ba_node;
  340. tween(_block_ba.getComponent(UIOpacity))
  341. .to(0.1, { opacity: 255 }) // 显示块
  342. .start();
  343. }
  344. // 等待一段时间以便逐行显示
  345. await this.delay(100); // 可以调整延迟时间
  346. }
  347. // 根据数据设置状态,并从最上面一行开始逐行隐藏state为0的块
  348. for (let i = 0; i < this.boardSize; i++) {
  349. for (let j = 0; j < this.boardSize; j++) {
  350. let state = _boardData[i][j];
  351. this.block_ba_data[i][j].block_state = state == 1 ? BlockState.SHOW : BlockState.HIDE;
  352. // 如果状态是隐藏,调整透明度
  353. if (state == 0) {
  354. let _block_ba = this.block_ba_data[i][j].block_ba_node;
  355. tween(_block_ba.getComponent(UIOpacity))
  356. .to(0.1, { opacity: 0 }) // 隐藏块
  357. .start();
  358. }
  359. }
  360. // 等待一段时间以便逐行隐藏
  361. await this.delay(100); // 可以调整延迟时间
  362. }
  363. // 保存当前的棋盘状态
  364. this.saveCurrentBoardState();
  365. }
  366. // 延迟函数
  367. delay(ms: number): Promise<void> {
  368. return new Promise(resolve => setTimeout(resolve, ms));
  369. }
  370. // 更新棋盘
  371. updateBoard() {
  372. for (let i = 0; i < this.block_ba_data.length; i++) {
  373. for (let j = 0; j < this.block_ba_data[i].length; j++) {
  374. let state = this.block_ba_data[i][j].block_state
  375. this.block_ba_data[i][j].block_ba_node.getComponent(UIOpacity).opacity = state == 1 ? 255 : 0;
  376. }
  377. }
  378. }
  379. // 根据权重随机返回一个图块数据
  380. getRandomBlockConfig(): BlockConfig | undefined {
  381. let totalWeight = 0;
  382. const cumulativeWeights: { type: BlockType, weight: number }[] = [];
  383. // 累积权重
  384. for (const type in BlockWeights) {
  385. if (BlockWeights.hasOwnProperty(type)) {
  386. const blockType = Number(type) as BlockType;
  387. const weight = BlockWeights[blockType];
  388. totalWeight += weight;
  389. cumulativeWeights.push({ type: blockType, weight: totalWeight });
  390. }
  391. }
  392. const random = Math.random() * totalWeight;
  393. let selectedType: BlockType | undefined = undefined;
  394. let low = 0;
  395. let high = cumulativeWeights.length - 1;
  396. // 二分查找
  397. while (low <= high) {
  398. const mid = Math.floor((low + high) / 2);
  399. if (random < cumulativeWeights[mid].weight) {
  400. selectedType = cumulativeWeights[mid].type;
  401. high = mid - 1;
  402. } else {
  403. low = mid + 1;
  404. }
  405. }
  406. if (selectedType !== undefined) {
  407. const filteredBlocks = BlocksAll.filter(block => block.block_type === selectedType);
  408. // console.log('Filtered Blocks:', filteredBlocks);
  409. if (filteredBlocks.length > 0) {
  410. const randomIndex = Math.floor(Math.random() * filteredBlocks.length);
  411. let colorIandU = mtec.number.random(0, 6, 0);
  412. switch (colorIandU) {
  413. case 0:
  414. filteredBlocks[randomIndex].block_color = Url.BLOCK.BLUE;
  415. break;
  416. case 1:
  417. filteredBlocks[randomIndex].block_color = Url.BLOCK.DEEPBLUE;
  418. break;
  419. case 2:
  420. filteredBlocks[randomIndex].block_color = Url.BLOCK.GREEN;
  421. break;
  422. case 3:
  423. filteredBlocks[randomIndex].block_color = Url.BLOCK.ORANGE;
  424. break;
  425. case 4:
  426. filteredBlocks[randomIndex].block_color = Url.BLOCK.PURPLE;
  427. break;
  428. case 5:
  429. filteredBlocks[randomIndex].block_color = Url.BLOCK.RED;
  430. break;
  431. case 6:
  432. filteredBlocks[randomIndex].block_color = Url.BLOCK.YELLOW;
  433. break;
  434. default:
  435. filteredBlocks[randomIndex].block_color = Url.BLOCK.BLUE;
  436. break;
  437. }
  438. return filteredBlocks[randomIndex];
  439. }
  440. }
  441. return undefined;
  442. }
  443. /**
  444. * 选择区刷新生成三个图块
  445. * @param isPropFrash 是否使用道具刷新-用作效果判断
  446. * @param blockConfig 是否指定块,传块配置
  447. */
  448. generateSelectionBlock(isPropFrash: boolean, blockConfig?: BlockConfig[]) {
  449. let configs = [];
  450. if (blockConfig) {
  451. for (let i = 0; i < blockConfig.length; i++) {
  452. configs.push(blockConfig[i]);
  453. TouchMgr.ins.selectCanTouch[i] = true;
  454. }
  455. } else {
  456. for (let i = 0; i < 3; i++) {
  457. configs.push(GridBlockMgr.ins.getRandomBlockConfig())
  458. TouchMgr.ins.selectCanTouch[i] = true;
  459. }
  460. }
  461. if (isPropFrash) {
  462. this.selects.forEach((block, index) => {
  463. tween(block)
  464. .to(0.2, { scale: v3(0, 0, 1) })
  465. .call(() => {
  466. const blockConfig = configs[index];
  467. block.setPosition(new Vec3(0, 0, 0))
  468. block.getComponent(BlockController).updateBlockSet(true, blockConfig);
  469. block.getComponent(BlockController).addBlockOffset(4);
  470. block.active = true
  471. })
  472. .to(0.2, { scale: v3(0.5, 0.5, 1) })
  473. .start()
  474. })
  475. MainGameLogic.ins.propInUse = false;
  476. } else {
  477. this.SelectionContainer.setPosition(v3(view.getVisibleSize().width, 0, 0))
  478. this.SelectionContainer.setScale(0, 0, 1);
  479. tween(this.SelectionContainer)
  480. .parallel(
  481. tween().to(0.5, { position: v3(0, 0, 0) }),
  482. tween().to(0.5, { scale: v3(1, 1, 1) })
  483. )
  484. .start()
  485. this.selects.forEach((block, index) => {
  486. const blockConfig = configs[index];
  487. block.setPosition(new Vec3(0, 0, 0))
  488. block.getComponent(BlockController).updateBlockSet(true, blockConfig);
  489. block.getComponent(BlockController).addBlockOffset(4);
  490. block.active = true
  491. })
  492. }
  493. this.detectSelectsBlockCanUse(true);
  494. }
  495. // 选择区生成引导图块
  496. generareGuideSelectionBlock(blockConfig: BlockConfig[]) {
  497. let configs = [];
  498. for (let i = 0; i < blockConfig.length; i++) {
  499. configs.push(blockConfig[i]);
  500. }
  501. this.selects.forEach((block, index) => {
  502. const blockConfi = configs[index];
  503. block.setPosition(new Vec3(0, 0, 0))
  504. block.getComponent(BlockController).updateBlockSet(true, blockConfi);
  505. block.getComponent(BlockController).addBlockOffset(4);
  506. block.active = index == 0 || index == 2 ? false : true;
  507. })
  508. TouchMgr.ins.selectCanTouch = [false, true, false];
  509. }
  510. // 重置 temp_block_ba_data
  511. resetTempBlockData() {
  512. for (let i = 0; i < this.block_ba_data.length; i++) {
  513. for (let j = 0; j < this.block_ba_data[i].length; j++) {
  514. this.temp_block_ba_data[i][j] = {
  515. index_i: this.block_ba_data[i][j].index_i,
  516. index_j: this.block_ba_data[i][j].index_j,
  517. block_state: this.block_ba_data[i][j].block_state,
  518. block_ba_node: this.block_ba_data[i][j].block_ba_node,
  519. block_sprite: this.block_ba_data[i][j].block_ba_node.getComponent(ResSprite),
  520. block_color: this.block_ba_data[i][j].block_color
  521. };
  522. }
  523. }
  524. }
  525. // 获取选择区的图块数据,用于检测游戏结束
  526. getSelectionBlockConfig() {
  527. this.selection_block_config = []; // 清空之前的配置
  528. for (let i = 0; i < TouchMgr.ins.selectCanTouch.length; i++) {
  529. if (TouchMgr.ins.selectCanTouch[i]) {
  530. const element = this.selects[i].getComponent(BlockController).curBlockConfig;
  531. this.selection_block_config.push(element);
  532. }
  533. }
  534. }
  535. // 检测游戏结束, 结束返回 true,否则返回 false
  536. detectGameOver(): boolean {
  537. this.detectSelectsBlockCanUse(false);
  538. this.getSelectionBlockConfig();
  539. const results_block_config: BlockConfig[] = [];
  540. if (GlobalData.isRotate) {
  541. // 根据 this.selection_block_config 里的 block_type,从 blocks 中找到所有对应的 BlockConfig,存到 results 里
  542. for (let i = 0; i < this.selection_block_config.length; i++) {
  543. const blockType = this.selection_block_config[i];
  544. // results_block_config.push(blockType);
  545. for (let j = 0; j < BlocksAll.length; j++) {
  546. if (BlocksAll[j].block_type === blockType.block_type) {
  547. results_block_config.push(BlocksAll[j]);
  548. }
  549. }
  550. }
  551. } else {
  552. for (let i = 0; i < this.selection_block_config.length; i++) {
  553. results_block_config.push(this.selection_block_config[i])
  554. }
  555. }
  556. // console.log(results_block_config);
  557. // 循环遍历results里的BlockConfig能不能放置
  558. for (let i = 0; i < this.block_ba_data.length; i++) {
  559. for (let j = 0; j < this.block_ba_data[i].length; j++) {
  560. for (let k = 0; k < results_block_config.length; k++) {
  561. // 重置 temp_block_ba_data
  562. this.resetTempBlockData();
  563. const blockArr = results_block_config[k].block_arr;
  564. const date = this.temp_block_ba_data[i][j];
  565. // 检测放置的方法
  566. let config = this.getCenteredBlocks(date.index_i, date.index_j, blockArr);
  567. let isCanPlaced = config.iscanplaced;
  568. // 如果可以放置
  569. if (isCanPlaced) {
  570. return false;
  571. }
  572. }
  573. }
  574. }
  575. // 移除道具检查逻辑
  576. return true;
  577. }
  578. // 遍历检测每个块能否消除,并选择最优的三个块生成(生成特定选择块的方法)
  579. generateSpecificSelectionBlock(isPropFrash) {
  580. const results: { block: BlockConfig; totalEliminate: number }[] = [];
  581. for (let i = 0; i < this.block_ba_data.length; i++) {
  582. for (let j = 0; j < this.block_ba_data.length; j++) {
  583. for (let k = 0; k < BlocksAll.length; k++) {
  584. // 重置 temp_block_ba_data
  585. this.resetTempBlockData();
  586. const blockArr = BlocksAll[k].block_arr;
  587. const date = this.temp_block_ba_data[i][j];
  588. // 检测放置的方法
  589. let config = this.getCenteredBlocks(date.index_i, date.index_j, blockArr);
  590. let isCanPlaced = config.iscanplaced;
  591. let placedIndexArr = config.indexarr;
  592. // 如果可以放置
  593. if (isCanPlaced) {
  594. // 根据返回结果修改temp_block_ba_data对应位置的数据
  595. for (let idx = 0; idx < placedIndexArr.length; idx++) {
  596. let boardindex_i = placedIndexArr[idx][0];
  597. let boardindex_j = placedIndexArr[idx][1];
  598. this.temp_block_ba_data[boardindex_i][boardindex_j].block_state = BlockState.SHOW;
  599. }
  600. // 返回消除的行列数
  601. let totalEliminate = this.checkAndUpdateTempBlocks();
  602. // 只有在 totalEliminate > 0 时才保存到 results
  603. if (totalEliminate > 0) {
  604. // 检查是否已经存在相同 block_type 的数据
  605. const existingIndex = results.findIndex(result => result.block.block_type === BlocksAll[k].block_type);
  606. if (existingIndex !== -1) {
  607. // 如果存在且新的 totalEliminate 更大,则替换
  608. if (results[existingIndex].totalEliminate < totalEliminate) {
  609. results[existingIndex] = { block: BlocksAll[k], totalEliminate };
  610. }
  611. } else {
  612. // 如果不存在,则添加到 results
  613. results.push({ block: BlocksAll[k], totalEliminate });
  614. }
  615. }
  616. }
  617. }
  618. }
  619. }
  620. // console.log(this.temp_block_ba_data);
  621. // console.log(this.block_ba_data);
  622. // 按 totalEliminate 排序,totalEliminate 最大的数据排在最前面
  623. results.sort((a, b) => {
  624. if (b.totalEliminate !== a.totalEliminate) {
  625. return b.totalEliminate - a.totalEliminate;
  626. } else {
  627. // 如果 totalEliminate 相同,则按 block_arr 中 1 的数量排序,数量越少越排在前面
  628. const countOnesA = this.countOnes(a.block.block_arr);
  629. const countOnesB = this.countOnes(b.block.block_arr);
  630. return countOnesA - countOnesB;
  631. }
  632. });
  633. // console.log(results);
  634. let configs: BlockConfig[] = [];
  635. if (results.length >= 3) {
  636. for (let i = 0; i < 3; i++) {
  637. let colorIandU = mtec.number.random(0, 6, 0);
  638. switch (colorIandU) {
  639. case 0:
  640. results[i].block.block_color = Url.BLOCK.BLUE;
  641. break;
  642. case 1:
  643. results[i].block.block_color = Url.BLOCK.DEEPBLUE;
  644. break;
  645. case 2:
  646. results[i].block.block_color = Url.BLOCK.GREEN;
  647. break;
  648. case 3:
  649. results[i].block.block_color = Url.BLOCK.ORANGE;
  650. break;
  651. case 4:
  652. results[i].block.block_color = Url.BLOCK.PURPLE;
  653. break;
  654. case 5:
  655. results[i].block.block_color = Url.BLOCK.RED;
  656. break;
  657. case 6:
  658. results[i].block.block_color = Url.BLOCK.YELLOW;
  659. break;
  660. default:
  661. results[i].block.block_color = Url.BLOCK.BLUE;
  662. break;
  663. }
  664. configs.push(results[i].block)
  665. TouchMgr.ins.selectCanTouch[i] = true;
  666. }
  667. if (isPropFrash) {
  668. this.selects.forEach((block, index) => {
  669. tween(block)
  670. .to(0.2, { scale: v3(0, 0, 1) })
  671. .call(() => {
  672. const blockConfig = configs[index];
  673. block.setPosition(new Vec3(0, 0, 0))
  674. block.getComponent(BlockController).updateBlockSet(true, blockConfig);
  675. block.getComponent(BlockController).addBlockOffset(4);
  676. block.active = true;
  677. })
  678. .to(0.2, { scale: v3(0.5, 0.5, 1) })
  679. .start()
  680. })
  681. MainGameLogic.ins.propInUse = false;
  682. } else {
  683. this.SelectionContainer.setPosition(v3(view.getVisibleSize().width, 0, 0))
  684. this.SelectionContainer.setScale(0, 0, 1);
  685. tween(this.SelectionContainer)
  686. .parallel(
  687. tween().to(0.5, { position: v3(0, 0, 0) }),
  688. tween().to(0.5, { scale: v3(1, 1, 1) })
  689. )
  690. .start()
  691. this.selects.forEach((block, index) => {
  692. const blockConfig = configs[index];
  693. block.setPosition(new Vec3(0, 0, 0))
  694. block.getComponent(BlockController).updateBlockSet(true, blockConfig);
  695. block.getComponent(BlockController).addBlockOffset(4);
  696. block.active = true
  697. })
  698. }
  699. } else if (results.length == 2) {
  700. for (let i = 0; i < 2; i++) {
  701. let colorIandU = mtec.number.random(0, 6, 0);
  702. switch (colorIandU) {
  703. case 0:
  704. results[i].block.block_color = Url.BLOCK.BLUE;
  705. break;
  706. case 1:
  707. results[i].block.block_color = Url.BLOCK.DEEPBLUE;
  708. break;
  709. case 2:
  710. results[i].block.block_color = Url.BLOCK.GREEN;
  711. break;
  712. case 3:
  713. results[i].block.block_color = Url.BLOCK.ORANGE;
  714. break;
  715. case 4:
  716. results[i].block.block_color = Url.BLOCK.PURPLE;
  717. break;
  718. case 5:
  719. results[i].block.block_color = Url.BLOCK.RED;
  720. break;
  721. case 6:
  722. results[i].block.block_color = Url.BLOCK.YELLOW;
  723. break;
  724. default:
  725. results[i].block.block_color = Url.BLOCK.BLUE;
  726. break;
  727. }
  728. configs.push(results[i].block)
  729. TouchMgr.ins.selectCanTouch[i] = true;
  730. }
  731. configs.push(GridBlockMgr.ins.getRandomBlockConfig())
  732. TouchMgr.ins.selectCanTouch[2] = true;
  733. if (isPropFrash) {
  734. this.selects.forEach((block, index) => {
  735. tween(block)
  736. .to(0.2, { scale: v3(0, 0, 1) })
  737. .call(() => {
  738. const blockConfig = configs[index];
  739. block.setPosition(new Vec3(0, 0, 0))
  740. block.getComponent(BlockController).updateBlockSet(true, blockConfig);
  741. block.getComponent(BlockController).addBlockOffset(4);
  742. block.active = true
  743. })
  744. .to(0.2, { scale: v3(0.5, 0.5, 1) })
  745. .start()
  746. })
  747. MainGameLogic.ins.propInUse = false;
  748. } else {
  749. this.SelectionContainer.setPosition(v3(view.getVisibleSize().width, 0, 0));
  750. this.SelectionContainer.setScale(0, 0, 1);
  751. tween(this.SelectionContainer)
  752. .parallel(
  753. tween().to(0.5, { position: v3(0, 0, 0) }),
  754. tween().to(0.5, { scale: v3(1, 1, 1) })
  755. )
  756. .start()
  757. this.selects.forEach((block, index) => {
  758. const blockConfig = configs[index];
  759. block.setPosition(new Vec3(0, 0, 0))
  760. block.getComponent(BlockController).updateBlockSet(true, blockConfig);
  761. block.getComponent(BlockController).addBlockOffset(4);
  762. block.active = true
  763. })
  764. }
  765. } else if (results.length == 1) {
  766. let colorIandU = mtec.number.random(0, 6, 0);
  767. switch (colorIandU) {
  768. case 0:
  769. results[0].block.block_color = Url.BLOCK.BLUE;
  770. break;
  771. case 1:
  772. results[0].block.block_color = Url.BLOCK.DEEPBLUE;
  773. break;
  774. case 2:
  775. results[0].block.block_color = Url.BLOCK.GREEN;
  776. break;
  777. case 3:
  778. results[0].block.block_color = Url.BLOCK.ORANGE;
  779. break;
  780. case 4:
  781. results[0].block.block_color = Url.BLOCK.PURPLE;
  782. break;
  783. case 5:
  784. results[0].block.block_color = Url.BLOCK.RED;
  785. break;
  786. case 6:
  787. results[0].block.block_color = Url.BLOCK.YELLOW;
  788. break;
  789. default:
  790. results[0].block.block_color = Url.BLOCK.BLUE;
  791. break;
  792. }
  793. configs.push(results[0].block)
  794. TouchMgr.ins.selectCanTouch[0] = true;
  795. for (let i = 1; i < 3; i++) {
  796. configs.push(GridBlockMgr.ins.getRandomBlockConfig())
  797. TouchMgr.ins.selectCanTouch[i] = true;
  798. }
  799. if (isPropFrash) {
  800. this.selects.forEach((block, index) => {
  801. tween(block)
  802. .to(0.2, { scale: v3(0, 0, 1) })
  803. .call(() => {
  804. const blockConfig = configs[index];
  805. block.setPosition(new Vec3(0, 0, 0))
  806. block.getComponent(BlockController).updateBlockSet(true, blockConfig);
  807. block.getComponent(BlockController).addBlockOffset(4);
  808. block.active = true
  809. })
  810. .to(0.2, { scale: v3(0.5, 0.5, 1) })
  811. .start()
  812. })
  813. MainGameLogic.ins.propInUse = false;
  814. } else {
  815. this.SelectionContainer.setPosition(v3(view.getVisibleSize().width, 0, 0))
  816. this.SelectionContainer.setScale(0, 0, 1);
  817. tween(this.SelectionContainer)
  818. .parallel(
  819. tween().to(0.5, { position: v3(0, 0, 0) }),
  820. tween().to(0.5, { scale: v3(1, 1, 1) })
  821. )
  822. .start()
  823. this.selects.forEach((block, index) => {
  824. const blockConfig = configs[index];
  825. block.setPosition(new Vec3(0, 0, 0))
  826. block.getComponent(BlockController).updateBlockSet(true, blockConfig);
  827. block.getComponent(BlockController).addBlockOffset(4);
  828. block.active = true
  829. })
  830. }
  831. } else {
  832. this.generateSelectionBlock(isPropFrash)
  833. }
  834. this.detectSelectsBlockCanUse(true)
  835. }
  836. /**
  837. * 检测选择的三个块是否可以使用
  838. * @param switchF
  839. */
  840. detectSelectsBlockCanUse(switchF: boolean) {
  841. for (let i = 0; i < this.selects.length; i++) {
  842. const element = this.selects[i].getComponent(BlockController);
  843. if (switchF) {
  844. this.selects[i].parent.getChildByName('frash').active = false;
  845. }
  846. element.checkGray();
  847. }
  848. }
  849. // 重置 temp_block_ba_data1
  850. resetTempBlockData1() {
  851. for (let i = 0; i < this.block_ba_data.length; i++) {
  852. for (let j = 0; j < this.block_ba_data[i].length; j++) {
  853. this.temp_block_ba_data1[i][j] = {
  854. index_i: this.block_ba_data[i][j].index_i,
  855. index_j: this.block_ba_data[i][j].index_j,
  856. block_state: this.block_ba_data[i][j].block_state,
  857. block_ba_node: this.block_ba_data[i][j].block_ba_node,
  858. block_sprite: this.block_ba_data[i][j].block_ba_node.getComponent(ResSprite),
  859. block_color: this.block_ba_data[i][j].block_color
  860. };
  861. }
  862. }
  863. }
  864. // 检测传入的块能否放置棋盘,不能则置灰
  865. detectIsCanPlaced(block_arr: number[][], block_node: Node) {
  866. // 循环遍历results里的BlockConfig能不能放置
  867. for (let i = 0; i < this.block_ba_data.length; i++) {
  868. for (let j = 0; j < this.block_ba_data[i].length; j++) {
  869. // 重置 temp_block_ba_data1
  870. GridBlockMgr.ins.resetTempBlockData1();
  871. const blockArr = block_arr
  872. const date = this.temp_block_ba_data1[i][j];
  873. // 检测放置的方法
  874. let config = this.getCenteredBlocks(date.index_i, date.index_j, blockArr);
  875. let isCanPlaced = config.iscanplaced;
  876. // 如果可以放置
  877. if (isCanPlaced) {
  878. for (let i = 0; i < block_node.children.length; i++) {
  879. const element = block_node.children[i].getComponent(Sprite);
  880. element.grayscale = false;
  881. }
  882. return
  883. } else {
  884. for (let i = 0; i < block_node.children.length; i++) {
  885. const element = block_node.children[i].getComponent(Sprite);
  886. element.grayscale = true;
  887. }
  888. }
  889. }
  890. }
  891. }
  892. // 计算 block_arr 中 1 的数量的方法
  893. countOnes(blockArr: number[][]): number {
  894. let count = 0;
  895. for (let i = 0; i < blockArr.length; i++) {
  896. for (let j = 0; j < blockArr[i].length; j++) {
  897. if (blockArr[i][j] === 1) {
  898. count++;
  899. }
  900. }
  901. }
  902. return count;
  903. }
  904. // 检测temp格子 消除方法
  905. checkAndUpdateTempBlocks(): number {
  906. const size = this.temp_block_ba_data.length;
  907. let totalEliminated = 0;
  908. // 检查每一行
  909. for (let i = 0; i < size; i++) {
  910. let rowAllOnes = true;
  911. for (let j = 0; j < size; j++) {
  912. if (this.temp_block_ba_data[i][j].block_state !== BlockState.SHOW) {
  913. rowAllOnes = false;
  914. break;
  915. }
  916. }
  917. if (rowAllOnes) {
  918. totalEliminated++;
  919. for (let j = 0; j < size; j++) {
  920. this.temp_block_ba_data[i][j].block_state = BlockState.HIDE;
  921. }
  922. }
  923. }
  924. // 检查每一列
  925. for (let j = 0; j < size; j++) {
  926. let colAllOnes = true;
  927. for (let i = 0; i < size; i++) {
  928. if (this.temp_block_ba_data[i][j].block_state !== BlockState.SHOW) {
  929. colAllOnes = false;
  930. break;
  931. }
  932. }
  933. if (colAllOnes) {
  934. totalEliminated++;
  935. for (let i = 0; i < size; i++) {
  936. this.temp_block_ba_data[i][j].block_state = BlockState.HIDE;
  937. }
  938. }
  939. }
  940. return totalEliminated;
  941. }
  942. // 判断当前位置是否可以放置块
  943. getCenteredBlocks(index_i: number, index_j: number, curBlockArr: number[][]): ReConfig {
  944. if (!curBlockArr) {
  945. return
  946. }
  947. const blocks: (number | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null));
  948. const blocks_ba_node: (Node | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null));
  949. const blocks_index: (number[] | null)[][] = Array(5).fill(null).map(() => Array(5).fill(null));
  950. const startI = Math.max(index_i - 2, 0);
  951. const endI = Math.min(index_i + 2, this.temp_block_ba_data.length - 1);
  952. const startJ = Math.max(index_j - 2, 0);
  953. const endJ = Math.min(index_j + 2, this.temp_block_ba_data[0].length - 1);
  954. for (let i = startI; i <= endI; i++) {
  955. for (let j = startJ; j <= endJ; j++) {
  956. const block = this.temp_block_ba_data[i][j].block_state;
  957. const block_ba_node = this.temp_block_ba_data[i][j].block_ba_node;
  958. const block_index = [this.temp_block_ba_data[i][j].index_i, this.temp_block_ba_data[i][j].index_j];
  959. blocks[i - (index_i - 2)][j - (index_j - 2)] = block;
  960. blocks_ba_node[i - (index_i - 2)][j - (index_j - 2)] = block_ba_node;
  961. blocks_index[i - (index_i - 2)][j - (index_j - 2)] = block_index
  962. }
  963. }
  964. for (let i = 0; i < 5; i++) {
  965. for (let j = 0; j < 5; j++) {
  966. const block = blocks[i][j];
  967. const curBlock = curBlockArr[i][j];
  968. if ((block === 1 && curBlock === 1) || (block === null && curBlock === 1)) {
  969. return { iscanplaced: false, indexarr: null };
  970. }
  971. }
  972. }
  973. let returnIndexArr = [];
  974. // 显示提示位置
  975. for (let i = 0; i < 5; i++) {
  976. for (let j = 0; j < 5; j++) {
  977. const block = blocks[i][j];
  978. const curBlock = curBlockArr[i][j];
  979. if ((block === 0 && curBlock === 1)) {
  980. // blocks_ba_node[i][j].getComponent(UIOpacity).opacity = 150;
  981. returnIndexArr.push(blocks_index[i][j])
  982. }
  983. }
  984. }
  985. return { iscanplaced: true, indexarr: returnIndexArr };
  986. }
  987. // 生成一个展示的临时图块
  988. generateTempBlock(blockConfig: BlockConfig) {
  989. let tempblock = instantiate(this.block);
  990. tempblock.getComponent(BlockController).updateBlockSet(false, blockConfig);
  991. return tempblock;
  992. }
  993. }