EliminateViewComp.ts 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585
  1. /*
  2. * @Author: mojunshou 1637302775@qq.com
  3. * @Date: 2025-03-11 18:05:45
  4. * @LastEditors: mojunshou 1637302775@qq.com
  5. * @LastEditTime: 2025-03-17 14:06:35
  6. * @Description:
  7. */
  8. import { _decorator, Color, EventTouch, instantiate, JsonAsset, Node, Prefab, Sprite, UITransform, Vec3, Widget, tween } from "cc";
  9. import { oops } from "db://oops-framework/core/Oops";
  10. import { ecs } from "db://oops-framework/libs/ecs/ECS";
  11. import { LabelChange } from "db://oops-framework/libs/gui/label/LabelChange";
  12. import { CCComp } from "db://oops-framework/module/common/CCComp";
  13. import { GameConfig, GameStatus, GridStatus } from "../../common/config/GameConfig";
  14. import { UIID } from "../../common/config/GameUIConfig";
  15. import { randomRangeInt } from "cc";
  16. import { Vec2 } from "cc";
  17. import { log } from "console";
  18. const { ccclass, property } = _decorator;
  19. // 网格数据接口
  20. interface GridData {
  21. name: string,
  22. status: GridStatus,
  23. gridNode: Node | null,
  24. row: number,
  25. column: number,
  26. gridColorKey: string | null,
  27. }
  28. //网格接口
  29. interface Grids {
  30. row: number,
  31. column: number,
  32. }
  33. //底部方块数据定义
  34. interface BrickData {
  35. index: number,
  36. brickKey: string,
  37. rotateFlag: boolean,
  38. gridConfig: Grids[],
  39. deg: number,
  40. brickNode: Node | null,
  41. gridColorKey: string,
  42. brickInitPos: Vec3,
  43. }
  44. //可修改的数据
  45. interface EditingData {
  46. brickData: BrickData | null,
  47. gridList: GridData[],
  48. }
  49. interface GridConfigData {
  50. row: number,
  51. column: number,
  52. }
  53. /** 视图层对象 */
  54. @ccclass('EliminateViewComp')
  55. @ecs.register('EliminateView', false)
  56. export class EliminateViewComp extends CCComp {
  57. @property({ type: LabelChange, displayName: "自动提现金额" })
  58. private amountLb: LabelChange = null!;
  59. @property({ type: LabelChange, displayName: "额外奖励" })
  60. private awardLb: LabelChange = null!;
  61. //游戏状态
  62. game_status: number = GameStatus.None;
  63. max_row: number = 0;
  64. max_col: number = 0;
  65. itemSize: number = 0;
  66. itemSpacing: number = 0; //方块间距
  67. brickNum: number = 0;
  68. //没使用颜色
  69. notUseColor = new Color(255, 255, 255, 255)
  70. //可用的颜色
  71. usableColor = new Color(0, 255, 0, 100)
  72. //不可用的颜色
  73. unavailableColor = new Color(255, 0, 0, 100)
  74. //旋转容错
  75. rotateFaultTolerant = 10;
  76. //游戏变量定义
  77. _score: number = 0; //分数
  78. rotateTag: boolean = false; //旋转标记
  79. gameConfig: { GridType?: any, bricks?: any } = {} //配置数据
  80. editingFlag = false //编辑状态
  81. editingData: EditingData = {
  82. brickData: null,
  83. gridList: [],
  84. }
  85. //旋转标记
  86. rotateFlag = false
  87. rotateBrickData: BrickData | null = null;
  88. //网格列表
  89. gridList: GridData[][] = [];
  90. //砖块列表
  91. bricksList: BrickData[] = [];
  92. //网格颜色列表
  93. gridColorList: GridData[] = [];
  94. gridPrefab: Prefab | null = null;
  95. rotatePrefab: Prefab | null = null;
  96. prefabUrlMap: { [key: string]: Prefab } = {}; //预制体url映射
  97. private _isAutoFunc: (() => void) | null = null
  98. private readonly GRID_GRAY_COLOR = new Color(128, 128, 128, 255)
  99. private readonly GRID_GRAY_DELAY = 0.01
  100. isAutoMode: boolean = false;
  101. autoModeInterval: number = 1 // 自动模式的间隔时间(秒)
  102. autoModeTimer: number = 0 // 自动模式计时器
  103. //网格列表
  104. gridsNode: Node | null = null; //中间网格区域
  105. itemNode: Node | null = null; //底部三个item块区域
  106. moveNode: Node | null = null; //移动层Node
  107. rotateNode: Node | null = null; //旋转Node
  108. hammerNode: Node | null = null; //锤子节点
  109. //锤子功能
  110. hammerMode = false; // 是否在锤子模式
  111. hammerRange = 2; // 锤子影响范围(以点击位置为中心的方形区域边长)
  112. hammerPreviewNodes: Node[] = []; // 预览区域的节点列表
  113. //分数管理
  114. get score() {
  115. return this._score
  116. }
  117. set score(v: number) {
  118. this._score = v
  119. }
  120. /** 视图层逻辑代码分离演示 */
  121. async start() {
  122. // const entity = this.ent as ecs.Entity; // ecs.Entity 可转为当前模块的具体实体对象
  123. this.setButton();
  124. this.initBindNode();
  125. await this.loadBrickConfig(); // 等待 loadBrickConfig 执行完毕
  126. this.loadPrefabsAsset().then(() => {
  127. this.scheduleOnce(() => {
  128. this.initGameData();
  129. this.initGrids();
  130. }, 0.1)
  131. }); // 然后再执行 loadPrefabsAsset
  132. }
  133. /** 视图对象通过 ecs.Entity.remove(GameViewComp) 删除组件是触发组件处理自定义释放逻辑 */
  134. reset() {
  135. this.node.destroy();
  136. }
  137. private initBindNode() {
  138. this.gridsNode = this.node.getChildByPath("Scene/center/GridNode");
  139. this.itemNode = this.node.getChildByPath("Scene/itemNode");
  140. this.moveNode = this.node.getChildByPath("Scene/MoveNode");
  141. this.rotateNode = this.node.getChildByPath("Scene/rotateNode");
  142. this.hammerNode = this.node.getChildByPath("Scene/hammerNode")
  143. }
  144. private btn_setting(event: EventTouch) {
  145. console.log("点击了设置按钮");
  146. oops.gui.open(UIID.Setting);
  147. }
  148. //初始化游戏数据
  149. private initGameData() {
  150. this.max_row = GameConfig.MaxRow;
  151. this.max_col = GameConfig.MaxCol;
  152. this.itemSize = GameConfig.ItemSize;
  153. this.itemSpacing = GameConfig.Spacing;
  154. this.brickNum = GameConfig.BlockNum;
  155. this.rotateFlag = false;
  156. this.rotateBrickData = null;
  157. this.setGameStatus(GameStatus.Start);
  158. }
  159. // private loadBrickConfig() {
  160. // let json_name: string = "gui/eliminate/config/GridConfig";
  161. // oops.res.load(json_name, JsonAsset, (err: Error | null, res: any) => {
  162. // if (res) {
  163. // this.gameConfig = res.json;
  164. // console.log("???????????????????", this.gameConfig);
  165. // } else {
  166. // console.log("JON数据加载失,请检查文件")
  167. // }
  168. // })
  169. // }
  170. // //加载预制体资源
  171. // private loadPrefabsAsset() {
  172. // let loadList = [];
  173. // const loadMap = {
  174. // gridPrefab: "gui/eliminate/prefabs/Grid",
  175. // rotatePrefab: "gui/eliminate/prefabs/Rotation"
  176. // }
  177. // Object.keys(loadMap).forEach(key => {
  178. // let path = loadMap[key as keyof typeof loadMap];
  179. // const keyTyped = key as 'gridPrefab' | 'rotatePrefab';
  180. // loadList.push(
  181. // new Promise((resolve, reject) => {
  182. // oops.res.load(path, Prefab, (err: Error | null, res: any) => {
  183. // if (err) {
  184. // reject(err)
  185. // return;
  186. // }
  187. // if (res) {
  188. // this[keyTyped] = res;
  189. // resolve(0)
  190. // }
  191. // })
  192. // })
  193. // )
  194. // })
  195. // //加载类型预制体
  196. // console.log("this.gameConfig", this.gameConfig)
  197. // const prefabUrls: string[] = []
  198. // const typeList = this.gameConfig['GridType'];
  199. // Object.keys(typeList).forEach(key => {
  200. // let path = typeList[key]['prefabUrl'];
  201. // prefabUrls.push(path);
  202. // })
  203. // prefabUrls.forEach(path => {
  204. // loadList.push(
  205. // new Promise((resolve, reject) => {
  206. // oops.res.load(path, Prefab, (err: Error | null, res: any) => {
  207. // if (err) {
  208. // reject(err)
  209. // return;
  210. // }
  211. // if (res) {
  212. // this.prefabUrlMap[path] = res
  213. // resolve(0)
  214. // }
  215. // })
  216. // })
  217. // )
  218. // })
  219. // console.log(">>>>>>>>>>>>>>>>loadList", loadList)
  220. // }
  221. //初始化网格
  222. private async loadBrickConfig() {
  223. let json_name: string = "gui/eliminate/config/GridConfig";
  224. return new Promise<void>((resolve, reject) => {
  225. oops.res.load(json_name, JsonAsset, (err: Error | null, res: any) => {
  226. if (res) {
  227. this.gameConfig = res.json;
  228. resolve();
  229. } else {
  230. console.log("JSON数据加载失,请检查文件");
  231. reject(err);
  232. }
  233. });
  234. });
  235. }
  236. //加载预制体资源
  237. private async loadPrefabsAsset() {
  238. const loadMap = {
  239. gridPrefab: "gui/eliminate/prefabs/Grid",
  240. rotatePrefab: "gui/eliminate/prefabs/Rotation"
  241. };
  242. Object.keys(loadMap).forEach(key => {
  243. let path = loadMap[key as keyof typeof loadMap];
  244. const keyTyped = key as 'gridPrefab' | 'rotatePrefab';
  245. new Promise((resolve, reject) => {
  246. oops.res.load(path, Prefab, (err: Error | null, res: any) => {
  247. if (err) {
  248. reject(err);
  249. return;
  250. }
  251. if (res) {
  252. this[keyTyped] = res;
  253. console.log("this.gridPrefab", this.gridPrefab);
  254. resolve(0);
  255. }
  256. });
  257. })
  258. });
  259. //加载类型预制体
  260. console.log("this.gameConfig", this.gameConfig);
  261. const prefabUrls: string[] = [];
  262. const typeList = this.gameConfig['GridType'];
  263. Object.keys(typeList).forEach(key => {
  264. console.log(">>>>>>>>>>", key);
  265. let path = typeList[key]['prefabUrl'];
  266. prefabUrls.push(path);
  267. });
  268. prefabUrls.forEach(path => {
  269. console.log("11111111", path)
  270. new Promise((resolve, reject) => {
  271. oops.res.load(path, Prefab, (err: Error | null, res: any) => {
  272. if (err) {
  273. reject(err);
  274. return;
  275. }
  276. if (res) {
  277. this.prefabUrlMap[path] = res;
  278. resolve(0);
  279. }
  280. });
  281. })
  282. });
  283. console.log("this.prefabUrlMap", this.prefabUrlMap)
  284. }
  285. private initGrids() {
  286. // 清理现有网格
  287. this.clearExistingGrids();
  288. // 设置网格容器大小
  289. this.setupGridsContainer();
  290. // 生成网格矩阵
  291. this.createGridMatrix();
  292. }
  293. //设置游戏状态
  294. /**
  295. * @description: 设置游戏状态
  296. * @param {GameStatus} status
  297. * @return {*}
  298. */
  299. private setGameStatus(status: GameStatus) {
  300. this.game_status = status;
  301. switch (status) {
  302. case GameStatus.None:
  303. break;
  304. case GameStatus.Start:
  305. break;
  306. case GameStatus.Over:
  307. break;
  308. default:
  309. break;
  310. }
  311. }
  312. /**
  313. * @description: 清理现有的网格数据
  314. * @return {*}
  315. */
  316. private clearExistingGrids(): void {
  317. this.gridList = [];
  318. if (this.gridsNode) {
  319. this.gridsNode.children.forEach(node => node.destroy());
  320. }
  321. }
  322. /**
  323. * @description: 设置网格大小
  324. * @return {*}
  325. */
  326. private setupGridsContainer(): void {
  327. const containerSize = {
  328. width: this.itemSize * this.max_col + this.itemSpacing * 2,
  329. height: this.itemSize * this.max_row + this.itemSpacing * 2
  330. };
  331. if (this.gridsNode) {
  332. const uiTransform = this.gridsNode.getComponent(UITransform);
  333. if (uiTransform) {
  334. uiTransform.setContentSize(
  335. containerSize.width,
  336. containerSize.height
  337. );
  338. }
  339. }
  340. }
  341. /**
  342. * @description: 设置网格容器大小
  343. * @return {*}
  344. */
  345. private createGridMatrix(): void {
  346. for (let rowIndex = 0; rowIndex < this.max_row; rowIndex++) {
  347. const currentRow: GridData[] = [];
  348. this.gridList.push(currentRow);
  349. for (let columnIndex = 0; columnIndex < this.max_col; columnIndex++) {
  350. const gridData = this.createGridData(rowIndex, columnIndex);
  351. currentRow.push(gridData);
  352. this.createGridNode(gridData);
  353. }
  354. }
  355. // 初始化格子状态
  356. for (let rowIndex = 0; rowIndex < this.max_row; rowIndex++) {
  357. for (let columnIndex = 0; columnIndex < this.max_col; columnIndex++) {
  358. this.gridList[rowIndex][columnIndex].status = GridStatus.NotUse
  359. this.generateGrid(this.gridList[rowIndex][columnIndex])
  360. }
  361. }
  362. // 初始化方块
  363. if (this.itemNode) {
  364. if (this.itemNode) {
  365. this.itemNode.children.forEach(node => { node.destroy() });
  366. }
  367. }
  368. this.bricksList.length = 0
  369. for (let i = 1; i <= this.brickNum; i++) {
  370. this.addBrick(i)
  371. }
  372. // 清除旋转数据
  373. if (this.rotateNode) {
  374. this.rotateNode.children.forEach(node => { node.destroy() })
  375. }
  376. }
  377. /**
  378. * @description: 创建格子数据
  379. * @param {number} row
  380. * @param {number} column
  381. * @return {*}
  382. */
  383. private createGridData(row: number, column: number): GridData {
  384. return {
  385. name: `Grid-${row}-${column}`,
  386. status: GridStatus.NotUse,
  387. gridNode: null,
  388. row: row,
  389. column: column,
  390. gridColorKey: null,
  391. };
  392. }
  393. /**
  394. * @description: 创建网格Node
  395. * @param {GridData} gridData
  396. * @return {*}
  397. */
  398. private createGridNode(gridData: GridData): void {
  399. const gridNode = new Node(gridData.name);
  400. if (this.gridsNode) {
  401. this.gridsNode.addChild(gridNode);
  402. }
  403. gridData.gridNode = gridNode;
  404. // 设置网格大小
  405. gridNode.addComponent(UITransform).setContentSize(this.itemSize, this.itemSize);
  406. // 设置网格位置约束
  407. this.setupGridWidget(gridNode, gridData.row, gridData.column);
  408. }
  409. /**
  410. * @description: 设置网格约束
  411. * @param {Node} gridNode
  412. * @param {number} row
  413. * @param {number} column
  414. * @return {*}
  415. */
  416. private setupGridWidget(gridNode: Node, row: number, column: number): void {
  417. const gridWidget = gridNode.addComponent(Widget);
  418. gridWidget.isAlignLeft = true;
  419. gridWidget.left = this.itemSize * column + this.itemSpacing;
  420. gridWidget.isAlignBottom = true;
  421. gridWidget.bottom = this.itemSize * row + this.itemSpacing;
  422. }
  423. /**
  424. * 生成或更新网格
  425. * @param gridData 网格数据
  426. */
  427. private generateGrid(gridData: GridData) {
  428. if (!gridData || !gridData.gridNode) {
  429. console.warn('无效的网格数据');
  430. return;
  431. }
  432. // 清理现有子节点
  433. this.clearGridChildren(gridData.gridNode);
  434. // 获取对应的预制体
  435. const prefab = this.getGridPrefab(gridData);
  436. if (!prefab) {
  437. console.warn('无法获取网格预制体');
  438. return;
  439. }
  440. // 创建并配置新节点
  441. const node = this.createNewGridNode(prefab, gridData);
  442. // 设置节点属性
  443. this.setupGridNode(node, gridData);
  444. }
  445. /**
  446. * 清理网格的子节点
  447. */
  448. private clearGridChildren(gridNode: Node): void {
  449. const children = gridNode.children.slice(); // 创建副本避免遍历时修改问题
  450. children.forEach(node => node.destroy());
  451. }
  452. /**
  453. * 获取对应状态的预制体
  454. */
  455. private getGridPrefab(gridData: GridData): Prefab {
  456. if (gridData.status === GridStatus.NotUse) {
  457. if (this.gridPrefab) {
  458. return this.gridPrefab;
  459. } else {
  460. throw new Error('Grid prefab is not loaded');
  461. }
  462. }
  463. if (gridData.status === GridStatus.Used && gridData.gridColorKey) {
  464. const prefabUrl = this.gameConfig['GridType']?.[gridData.gridColorKey]?.['prefabUrl'];
  465. return this.prefabUrlMap[prefabUrl];
  466. }
  467. throw new Error('Invalid grid status or missing gridColorKey');
  468. }
  469. /**
  470. * 创建网格节点
  471. */
  472. private createNewGridNode(prefab: Prefab, gridData: GridData): Node {
  473. const node = instantiate(prefab);
  474. if (gridData && gridData.gridNode) {
  475. gridData.gridNode.addChild(node);
  476. return node;
  477. }
  478. return node
  479. }
  480. /**
  481. * 设置网格节点的属性
  482. */
  483. private setupGridNode(node: Node, gridData: GridData): void {
  484. // 设置未使用状态的颜色
  485. if (gridData.status === GridStatus.NotUse) {
  486. const sprite = node.getComponent(Sprite);
  487. if (sprite) {
  488. sprite.color = this.notUseColor;
  489. }
  490. }
  491. // 设置节点大小
  492. const transform = node.getComponent(UITransform);
  493. if (transform) {
  494. transform.setContentSize(
  495. this.itemSize - this.itemSpacing * 2,
  496. this.itemSize - this.itemSpacing * 2
  497. );
  498. }
  499. // 设置位置
  500. node.setPosition(Vec3.ZERO);
  501. }
  502. //底部增加方块
  503. addBrick(index: number) {
  504. const brickKey = Object.keys(this.gameConfig['bricks'])[randomRangeInt(0, Object.keys(this.gameConfig['bricks']).length)]
  505. const gridColorKey = Object.keys(this.gameConfig['GridType'])[randomRangeInt(0, Object.keys(this.gameConfig['GridType']).length)]
  506. const brickConfig = this.gameConfig['bricks'][brickKey]
  507. const brickData: BrickData = {
  508. index,
  509. brickKey,
  510. rotateFlag: brickConfig['rotateFlag'],
  511. gridConfig: brickConfig['gridConfig'],
  512. deg: 0,
  513. brickNode: null,
  514. gridColorKey,
  515. brickInitPos: new Vec3(),
  516. }
  517. this.bricksList.push(brickData)
  518. // 生成方块
  519. const brickNode = this.generateBrick(brickKey, gridColorKey);
  520. if (this.itemNode) {
  521. this.itemNode.addChild(brickNode)
  522. }
  523. brickData.brickNode = brickNode
  524. // 方块间隔
  525. let offset = 220
  526. if (this.brickNum % 2 === 1) {
  527. const middleNum = Math.floor(this.brickNum / 2) + 1
  528. if (index < middleNum) {
  529. offset = - offset
  530. }
  531. else if (index === middleNum) {
  532. offset = 0
  533. }
  534. }
  535. if (brickData && brickData.brickNode) {
  536. let width = 0;
  537. if (this.itemNode) {
  538. width = this.itemNode.getComponent(UITransform)!.width;
  539. }
  540. brickData.brickNode.setPosition(offset, 0)
  541. brickData.brickNode.scale_x = 0.6;
  542. brickData.brickNode.scale_y = 0.6;
  543. brickData.brickInitPos = brickData.brickNode.getWorldPosition()
  544. this.brickAddEvent(brickData);
  545. }
  546. }
  547. touchStartLocation = new Vec2()
  548. touchStartFlag = false
  549. //方块添加监听事件
  550. brickAddEvent(brickData: BrickData) {
  551. const brickNode = brickData.brickNode
  552. if (!brickNode) {
  553. console.log("打印进这里来了")
  554. return
  555. }
  556. //这个事件不生效
  557. brickNode.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
  558. // if (this.adShowingFlag) return
  559. // 未操作完时不能操作下一个方块
  560. if (this.editingFlag) return
  561. this.touchStartFlag = true
  562. this.editingFlag = true
  563. this.editingData.brickData = null
  564. this.editingData.gridList.length = 0
  565. // 隐藏游戏提示
  566. // this.gameTipNode.active = false
  567. // 记录开始触摸的位置,用于结束触摸时,判断位置是否是单击
  568. this.touchStartLocation.set(event.getUILocation())
  569. // 添加到移动节点里进行移动
  570. const pos = brickNode.getWorldPosition()
  571. brickNode.setParent(this.moveNode)
  572. brickNode.setWorldPosition(pos)
  573. // 添加放大动画
  574. tween(brickNode)
  575. .to(0.2, { scale: new Vec3(1, 1, 1) })
  576. .start()
  577. const index = this.bricksList.findIndex(data => data === brickData)
  578. if (index > -1) {
  579. this.editingData.brickData = this.bricksList.splice(index, 1)[0]
  580. } else {
  581. console.error("bricksList not find brickData:", brickData)
  582. }
  583. // 清除旋转数据
  584. if (this.rotateFlag && this.rotateBrickData !== this.editingData.brickData) {
  585. this.rotateFlag = false
  586. this.rotateBrickData = null
  587. if (this.rotateNode) {
  588. this.rotateNode.children.forEach(node => { node.destroy() })
  589. }
  590. }
  591. })
  592. // 触摸移动时
  593. brickNode.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
  594. // 防止如放回方块回弹动画时,已经触摸在另一个方块上面,从而导致异常错误
  595. if (this.editingData.brickData !== brickData) return
  596. // 清除旋转数据
  597. if (event.getUILocation().subtract(this.touchStartLocation).length() >= this.rotateFaultTolerant) {
  598. this.rotateFlag = false
  599. this.rotateBrickData = null
  600. if (this.rotateNode) {
  601. this.rotateNode.children.forEach(node => { node.destroy() })
  602. }
  603. }
  604. // 格子颜色恢复
  605. this.gridColorRecovery()
  606. // 移动
  607. brickNode.setWorldPosition(brickNode.getWorldPosition().add(event.getUIDelta().toVec3()))
  608. // 每次移动重置数据
  609. this.editingData.gridList.length = 0
  610. // 实时获取方块位置判断在哪个格子上
  611. const tempGridList: GridData[] = []
  612. brickNode.children.forEach((brickGridNode) => {
  613. const brickGridPos = brickGridNode.getWorldPosition()
  614. let gridData: GridData | null = null
  615. for (let rowIndex = 0; rowIndex < this.max_row && gridData === null; rowIndex++) {
  616. for (let columnIndex = 0; columnIndex < this.max_col && gridData === null; columnIndex++) {
  617. const nowGridData = this.gridList[rowIndex][columnIndex];
  618. if (nowGridData && nowGridData.gridNode) {
  619. const gridPos = nowGridData.gridNode.getWorldPosition()
  620. if (Vec3.distance(gridPos, brickGridPos) <= (this.itemSize / 2 - this.itemSpacing)) {
  621. gridData = nowGridData
  622. }
  623. }
  624. }
  625. }
  626. if (gridData === null) return
  627. tempGridList.push(gridData)
  628. })
  629. // 检查整体情况
  630. let checkFlag = false
  631. if (
  632. tempGridList.length === brickData.gridConfig.length &&
  633. tempGridList.filter(d => d.status === GridStatus.NotUse).length === brickData.gridConfig.length
  634. ) {
  635. checkFlag = true
  636. tempGridList.forEach((gridData) => {
  637. this.editingData.gridList.push(gridData)
  638. })
  639. }
  640. // 格子给用户提示
  641. tempGridList.forEach((gridData) => {
  642. if (gridData.status !== GridStatus.NotUse) return
  643. if (gridData.gridNode) {
  644. const children = gridData.gridNode.children[0];
  645. if (children) {
  646. children.getComponent(Sprite)!.color = checkFlag ? this.usableColor : this.unavailableColor
  647. }
  648. }
  649. // 用于恢复格子
  650. this.gridColorList.push(gridData)
  651. })
  652. })
  653. // 触摸松开后
  654. brickNode.on(Node.EventType.TOUCH_END, (event: EventTouch) => {
  655. // 当连击很快时,会出现1次start,2次end情况,为了避免所以通过标志位表示是一个连贯操作
  656. if (!this.touchStartFlag) return
  657. this.touchStartFlag = false
  658. // console.log("TOUCH_END", this.editingFlag, this.editingData.brickData.index)
  659. // 防止如放回方块回弹动画时,已经触摸在另一个方块上面,从而导致异常错误
  660. if (this.editingData.brickData !== brickData) return
  661. // 第二次单击,旋转
  662. if (this.rotateFlag) {
  663. const brickData = this.editingData.brickData
  664. this.brickGridRotate(brickData)
  665. // 方块放回待选区
  666. this.bricksList.push(brickData)
  667. if (this.itemNode && brickData.brickNode) {
  668. this.itemNode.addChild(brickData.brickNode)
  669. // brickData.brickNode.setWorldPosition(brickData.brickInitPos)
  670. tween(brickData.brickNode)
  671. .to(0.2, {
  672. worldPosition: brickData.brickInitPos,
  673. scale: new Vec3(0.8, 0.8, 0.8)
  674. })
  675. .start()
  676. }
  677. this.editingFlag = false
  678. }
  679. // 方块到格子
  680. else if (this.editingData.brickData && this.editingData.gridList.length > 0) {
  681. console.log("移动到格子了")
  682. // 修改格子
  683. this.editingData.gridList.forEach((gridData) => {
  684. gridData.status = GridStatus.Used
  685. if (this.editingData.brickData) {
  686. gridData.gridColorKey = this.editingData.brickData.gridColorKey;
  687. }
  688. this.generateGrid(gridData)
  689. })
  690. // 销毁方块--这增加动画
  691. if (this.editingData.brickData && this.editingData.brickData.brickNode) {
  692. this.editingData.brickData.brickNode.destroy();
  693. }
  694. // 新增方块
  695. this.addBrick(this.editingData.brickData.index)
  696. this.scheduleOnce(() => {
  697. // 格子消除
  698. this.gridEliminate().then(() => {
  699. // 检查方块是否还能消除格子
  700. this.prompt(false).then((promptFlag) => {
  701. if (!promptFlag) {
  702. this.gameOver()
  703. }
  704. this.editingFlag = false
  705. })
  706. })
  707. })
  708. }
  709. // 方块回到待选区
  710. else {
  711. console.log("返回带待选区域")
  712. const brickData = this.editingData.brickData
  713. this.bricksList.push(brickData)
  714. // this.audioManager.playMoveFail()
  715. // 回弹动画
  716. if (brickData.brickNode) {
  717. tween(brickData.brickNode)
  718. .to(0.15, {
  719. worldPosition: this.editingData.brickData.brickInitPos,
  720. scale: new Vec3(0.8, 0.8, 0.8)
  721. })
  722. .call(() => {
  723. if (this.itemNode && brickData.brickNode) {
  724. this.itemNode.addChild(brickData.brickNode);
  725. brickData.brickNode.setWorldPosition(brickData.brickInitPos);
  726. }
  727. this.editingFlag = false;
  728. })
  729. .start();
  730. }
  731. }
  732. // 旋转标志位
  733. if (
  734. !this.rotateFlag &&
  735. this.editingData.brickData.rotateFlag &&
  736. event.getUILocation().subtract(this.touchStartLocation).length() < this.rotateFaultTolerant
  737. ) {
  738. console.log("进这里啦?????????????????????")
  739. this.rotateFlag = true
  740. this.rotateBrickData = this.editingData.brickData;
  741. if (this.rotateBrickData.brickNode) {
  742. const prosition = this.rotateBrickData.brickNode.getWorldPosition()
  743. const rotateNode = instantiate(this.rotatePrefab)
  744. if (this.rotateNode && rotateNode) {
  745. const instantiatedRotateNode = instantiate(rotateNode);
  746. this.rotateNode.addChild(instantiatedRotateNode);
  747. instantiatedRotateNode.setWorldPosition(prosition)
  748. }
  749. }
  750. }
  751. // 格子颜色恢复
  752. this.gridColorRecovery();
  753. })
  754. }
  755. // 提示
  756. prompt(tipFlag = true) {
  757. console.log("点击了提示按钮")
  758. return new Promise((resolve, reject) => {
  759. const gridPromptList: GridData[] = []
  760. let moveFlag = false
  761. // 找方块可消除位置
  762. for (let rowIndex = 0; rowIndex < this.max_row && !moveFlag; rowIndex++) {
  763. for (let columnIndex = 0; columnIndex < this.max_col && !moveFlag; columnIndex++) {
  764. const gridData = this.gridList[rowIndex][columnIndex]
  765. if (gridData.status !== GridStatus.NotUse) continue
  766. // 方块不旋转检测是否能放
  767. for (let brickI = 0; brickI < this.bricksList.length && !moveFlag; brickI++) {
  768. const brickData = this.bricksList[brickI]
  769. if (this.moveIf(rowIndex, columnIndex, brickData.gridConfig)) {
  770. // 复制整体网格,以方块设置网格状态
  771. const gridList = this.copyGridList()
  772. brickData.gridConfig.forEach((gridConfigData) => {
  773. gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex].status = GridStatus.Used
  774. })
  775. // 检查复制的整体网格是否有可消除
  776. if (this.gridEliminateCheck(gridList).gridEliminateList.length > 0) {
  777. moveFlag = true
  778. brickData.gridConfig.forEach((gridConfigData) => {
  779. gridPromptList.push(this.gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex])
  780. })
  781. }
  782. }
  783. }
  784. // 方块旋转检测是否能放
  785. for (let brickI = 0; brickI < this.bricksList.length && !moveFlag; brickI++) {
  786. const brickData = this.bricksList[brickI]
  787. if (!brickData.rotateFlag) continue
  788. let gridConfig = brickData.gridConfig
  789. let deg = brickData.deg
  790. // 获得旋转的方块网格配置
  791. for (let count = 1; count <= 3 && !moveFlag; count++) {
  792. const next = this.nextGridRotate(gridConfig, deg)
  793. if (this.moveIf(rowIndex, columnIndex, next.gridConfig)) {
  794. // 复制整体网格,以方块设置网格状态
  795. const gridList = this.copyGridList()
  796. next.gridConfig.forEach((gridConfigData) => {
  797. gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex].status = GridStatus.Used
  798. })
  799. // 检查复制的整体网格是否有可消除
  800. if (this.gridEliminateCheck(gridList).gridEliminateList.length > 0) {
  801. moveFlag = true
  802. next.gridConfig.forEach((gridConfigData) => {
  803. gridPromptList.push(this.gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex])
  804. })
  805. }
  806. }
  807. gridConfig = next.gridConfig
  808. deg = next.deg
  809. }
  810. }
  811. }
  812. }
  813. // 找方块可放置位置
  814. for (let rowIndex = 0; rowIndex < this.max_row && !moveFlag; rowIndex++) {
  815. for (let columnIndex = 0; columnIndex < this.max_col && !moveFlag; columnIndex++) {
  816. const gridData = this.gridList[rowIndex][columnIndex]
  817. if (gridData.status !== GridStatus.NotUse) continue
  818. // 方块不旋转检测是否能放
  819. for (let brickI = 0; brickI < this.bricksList.length && !moveFlag; brickI++) {
  820. const brickData = this.bricksList[brickI]
  821. if (this.moveIf(rowIndex, columnIndex, brickData.gridConfig)) {
  822. moveFlag = true
  823. brickData.gridConfig.forEach((gridConfigData) => {
  824. gridPromptList.push(this.gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex])
  825. })
  826. }
  827. }
  828. // 方块旋转检测是否能放
  829. for (let brickI = 0; brickI < this.bricksList.length && !moveFlag; brickI++) {
  830. const brickData = this.bricksList[brickI]
  831. if (!brickData.rotateFlag) continue
  832. let gridConfig = brickData.gridConfig
  833. let deg = brickData.deg
  834. for (let count = 1; count <= 3 && !moveFlag; count++) {
  835. const next = this.nextGridRotate(gridConfig, deg)
  836. if (this.moveIf(rowIndex, columnIndex, next.gridConfig)) {
  837. moveFlag = true
  838. next.gridConfig.forEach((gridConfigData) => {
  839. gridPromptList.push(this.gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex])
  840. })
  841. }
  842. gridConfig = next.gridConfig
  843. deg = next.deg
  844. }
  845. }
  846. }
  847. }
  848. if (gridPromptList.length < 1) {
  849. resolve(false)
  850. return
  851. }
  852. if (!tipFlag) {
  853. resolve(true)
  854. return
  855. }
  856. // 提示用户(网格变绿)
  857. gridPromptList.forEach((gridData) => {
  858. if (gridData.gridNode && gridData.gridNode.children[0]) {
  859. const sprite = gridData.gridNode.children[0].getComponent(Sprite);
  860. if (sprite) {
  861. sprite.color = this.usableColor;
  862. }
  863. }
  864. // 用于恢复格子
  865. this.gridColorList.push(gridData)
  866. })
  867. resolve(true)
  868. })
  869. }
  870. // 复制整体网格
  871. copyGridList() {
  872. const gridList: GridData[][] = []
  873. for (let rowIndex = 0; rowIndex < this.max_row; rowIndex++) {
  874. gridList.push([])
  875. for (let columnIndex = 0; columnIndex < this.max_col; columnIndex++) {
  876. const gridData = this.gridList[rowIndex][columnIndex]
  877. gridList[rowIndex].push({
  878. name: gridData.name,
  879. status: gridData.status,
  880. gridNode: null,
  881. row: gridData.row,
  882. column: gridData.column,
  883. gridColorKey: gridData.gridColorKey,
  884. })
  885. }
  886. }
  887. return gridList
  888. }
  889. // 检查是否能放置方块
  890. moveIf(row: number, column: number, gridConfig: GridConfigData[]) {
  891. let moveFlag = true
  892. for (let i = 0; i < gridConfig.length; i++) {
  893. const gridConfigData = gridConfig[i]
  894. const gridI = row + gridConfigData.row
  895. const gridJ = column + gridConfigData.column
  896. // 边界判断
  897. if (
  898. gridI < 0 ||
  899. gridI > this.max_row - 1 ||
  900. gridJ < 0 ||
  901. gridJ > this.max_col - 1
  902. ) {
  903. moveFlag = false
  904. break
  905. }
  906. // 已用
  907. else if (this.gridList[gridI][gridJ].status === GridStatus.Used) {
  908. moveFlag = false
  909. break
  910. }
  911. }
  912. return moveFlag
  913. }
  914. brickGridRotate(brickData: BrickData) {
  915. const next = this.nextGridRotate(brickData.gridConfig, brickData.deg)
  916. brickData.deg = next.deg
  917. brickData.gridConfig = next.gridConfig;
  918. if (brickData.brickNode) {
  919. tween(brickData.brickNode).to(0.1, { angle: next.deg }).start();
  920. }
  921. // this.audioManager.playRotate()
  922. }
  923. //下一个旋转
  924. nextGridRotate(gridConfig: GridConfigData[], deg: number) {
  925. const newGridConfig: GridConfigData[] = []
  926. // 顺时针旋转
  927. let newDeg = deg - 90
  928. gridConfig.forEach((gridConfigData) => {
  929. // 例如(1,2) => (-2,1),可以画图分析
  930. newGridConfig.push({
  931. row: -gridConfigData.column,
  932. column: gridConfigData.row
  933. })
  934. })
  935. return { gridConfig: newGridConfig, deg: newDeg }
  936. }
  937. //格子颜色恢复
  938. gridColorRecovery() {
  939. while (this.gridColorList.length > 0) {
  940. const gridData = this.gridColorList.pop();
  941. if (gridData) {
  942. if (gridData.status === GridStatus.NotUse) {
  943. if (gridData.gridNode) {
  944. gridData.gridNode.children[0].getComponent(Sprite)!.color = this.notUseColor;
  945. }
  946. }
  947. }
  948. }
  949. }
  950. // 生成独立的方块节点
  951. generateBrick(brickKey: string, gridColorKey: string) {
  952. const brickConfig = this.gameConfig['bricks'][brickKey]
  953. let rowMin = 0
  954. let rowMax = 0
  955. let columnMin = 0
  956. let columnMax = 0
  957. brickConfig['gridConfig'].forEach((gridConfigData: GridConfigData) => {
  958. if (gridConfigData.row < rowMin) {
  959. rowMin = gridConfigData.row
  960. }
  961. else if (gridConfigData.row > rowMax) {
  962. rowMax = gridConfigData.row
  963. }
  964. if (gridConfigData.column < columnMin) {
  965. columnMin = gridConfigData.column
  966. }
  967. else if (gridConfigData.column > columnMax) {
  968. columnMax = gridConfigData.column
  969. }
  970. })
  971. const rowNum = (rowMax - rowMin + 1)
  972. const columnNum = (columnMax - columnMin + 1)
  973. // 生成独立的方块节点
  974. const brickNode = new Node()
  975. brickNode.name = brickKey
  976. const transformCom: UITransform = brickNode.addComponent(UITransform)
  977. transformCom.setContentSize(
  978. this.itemSize * columnNum,
  979. this.itemSize * rowNum
  980. )
  981. transformCom.setAnchorPoint(0.5, 0.5)
  982. const gridPrefab = this.prefabUrlMap[this.gameConfig['GridType'][gridColorKey]['prefabUrl']]
  983. //生成对应的配置方块
  984. brickConfig['gridConfig'].forEach((gridConfigData: GridConfigData) => {
  985. const gridNode = new Node()
  986. gridNode.name = 'grid'
  987. brickNode.addChild(gridNode)
  988. gridNode.addComponent(UITransform).setContentSize(this.itemSize, this.itemSize)
  989. const gridWidget: Widget = gridNode.addComponent(Widget)
  990. gridWidget.isAlignLeft = true
  991. gridWidget.left = this.itemSize * gridConfigData.column - this.itemSize * columnMin
  992. gridWidget.isAlignBottom = true
  993. gridWidget.bottom = this.itemSize * gridConfigData.row - this.itemSize * rowMin
  994. const node = instantiate(gridPrefab)
  995. gridNode.addChild(node)
  996. const uiTransform = node.getComponent(UITransform);
  997. if (uiTransform) {
  998. uiTransform.setContentSize(
  999. this.itemSize - this.itemSpacing * 2,
  1000. this.itemSize - this.itemSpacing * 2,
  1001. )
  1002. }
  1003. node.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
  1004. event.propagationStopped = false
  1005. })
  1006. node.setPosition(Vec3.ZERO)
  1007. })
  1008. return brickNode
  1009. }
  1010. // 消除,这里增加飞的动画
  1011. gridEliminate() {
  1012. return new Promise((resolve, reject) => {
  1013. const d = this.gridEliminateCheck(this.gridList)
  1014. const gridEliminateList = d.gridEliminateList
  1015. const eliminateRowNum = d.eliminateRowNum
  1016. const eliminateColumnNum = d.eliminateColumnNum
  1017. // 无行可消除
  1018. if (gridEliminateList.length < 1) {
  1019. resolve(false)
  1020. return
  1021. }
  1022. // 消除格子
  1023. gridEliminateList.forEach((gridData) => {
  1024. if (gridData.gridNode) {
  1025. tween(gridData.gridNode.children[0])
  1026. .to(0.2, { scale: new Vec3(0.5, 0.5) })
  1027. .call(() => {
  1028. gridData.status = GridStatus.NotUse
  1029. this.generateGrid(gridData)
  1030. })
  1031. .start()
  1032. }
  1033. })
  1034. // 音效
  1035. // this.audioManager.playEliminate()
  1036. this.scheduleOnce(() => {
  1037. // 成绩
  1038. let score = 0
  1039. for (let i = 1; i <= eliminateRowNum; i++) {
  1040. score += this.max_col * i
  1041. }
  1042. for (let i = 1; i <= eliminateColumnNum; i++) {
  1043. score += this.max_row * i
  1044. }
  1045. this.score += score
  1046. resolve(true)
  1047. }, 0.2)
  1048. })
  1049. }
  1050. // 检查消除
  1051. gridEliminateCheck(gridList: GridData[][]) {
  1052. const gridEliminateList: GridData[] = []
  1053. let eliminateRowNum = 0
  1054. let eliminateColumnNum = 0
  1055. // 行检查
  1056. for (let rowIndex = 0; rowIndex < this.max_row; rowIndex++) {
  1057. const rowData = gridList[rowIndex]
  1058. if (rowData.every(gridData => gridData.status === GridStatus.Used)) {
  1059. rowData.forEach(gridData => {
  1060. if (gridEliminateList.findIndex(data => data === gridData) < 0) {
  1061. gridEliminateList.push(gridData)
  1062. }
  1063. })
  1064. eliminateRowNum += 1
  1065. }
  1066. }
  1067. // 列检查
  1068. for (let columnIndex = 0; columnIndex < this.max_col; columnIndex++) {
  1069. if (gridList.every(rowData => rowData[columnIndex].status === GridStatus.Used)) {
  1070. gridList.forEach(rowData => {
  1071. const gridData = rowData[columnIndex]
  1072. if (gridEliminateList.findIndex(data => data === gridData) < 0) {
  1073. gridEliminateList.push(gridData)
  1074. }
  1075. })
  1076. eliminateColumnNum += 1
  1077. }
  1078. }
  1079. return {
  1080. gridEliminateList,
  1081. eliminateRowNum,
  1082. eliminateColumnNum,
  1083. }
  1084. }
  1085. async gameOver() {
  1086. // 先禁用所有操作
  1087. this.isAutoMode = false
  1088. this.editingFlag = true // 防止操作
  1089. // 执行格子变灰动画
  1090. await this.playGameOverGridAnimation()
  1091. // 播放游戏结束音效
  1092. // this.audioManager.playGameOver()
  1093. // 设置游戏状态
  1094. this.setGameStatus(GameStatus.Over)
  1095. // 恢复操作标志
  1096. this.editingFlag = false
  1097. }
  1098. private async playGameOverGridAnimation(): Promise<void> {
  1099. // 按行从左到右收集所有已使用的格子
  1100. const usedGrids: GridData[] = []
  1101. for (let row = 0; row < this.max_row; row++) {
  1102. for (let col = 0; col < this.max_col; col++) {
  1103. const grid = this.gridList[row][col]
  1104. if (grid.status === GridStatus.Used) {
  1105. usedGrids.push(grid)
  1106. }
  1107. }
  1108. }
  1109. // 执行变灰动画
  1110. return new Promise<void>((resolve) => {
  1111. let completedCount = 0
  1112. usedGrids.forEach((grid, index) => {
  1113. this.scheduleOnce(() => {
  1114. // 获取格子的 Sprite 组件
  1115. if (grid.gridNode) {
  1116. const gridSprite = grid.gridNode.children[0].getComponent(Sprite)
  1117. // 创建变灰动画
  1118. if (!gridSprite) {
  1119. return
  1120. }
  1121. tween(gridSprite)
  1122. .to(0.2, { color: this.GRID_GRAY_COLOR })
  1123. .call(() => {
  1124. completedCount++
  1125. if (completedCount === usedGrids.length) {
  1126. resolve()
  1127. }
  1128. })
  1129. .start()
  1130. }
  1131. }, index * this.GRID_GRAY_DELAY)
  1132. })
  1133. // 如果没有已使用的格子,直接完成
  1134. if (usedGrids.length === 0) {
  1135. resolve()
  1136. }
  1137. })
  1138. }
  1139. // //刷新按钮
  1140. btn_refresh() {
  1141. if (
  1142. this.game_status !== GameStatus.Start
  1143. ) return
  1144. // 清除方块重新获取
  1145. if (this.itemNode) {
  1146. this.itemNode.children.forEach(node => { node.destroy() });
  1147. }
  1148. this.bricksList.length = 0
  1149. for (let i = 1; i <= this.brickNum; i++) {
  1150. this.addBrick(i)
  1151. }
  1152. // 清除旋转数据
  1153. if (this.rotateNode) {
  1154. this.rotateNode.children.forEach(node => { node.destroy() })
  1155. }
  1156. this.rotateFlag = false
  1157. this.rotateBrickData = null
  1158. // 恢复格子颜色
  1159. this.gridColorRecovery()
  1160. }
  1161. //提示按钮
  1162. btn_hint() {
  1163. if (
  1164. this.game_status !== GameStatus.Start
  1165. ) return
  1166. // 先检查是否死局
  1167. this.prompt(false).then((flag) => {
  1168. if (!flag) {
  1169. this.gameOver()
  1170. return
  1171. }
  1172. this.gridColorRecovery()
  1173. this.prompt()
  1174. })
  1175. }
  1176. //继续按钮
  1177. onContinueBtnClick() {
  1178. this.setGameStatus(GameStatus.Start);
  1179. }
  1180. // ... 修改 autoPlaceBrick 方法
  1181. async autoPlaceBrick() {
  1182. if (!this.isAutoMode) return;
  1183. if (this.bricksList.length === 0) return
  1184. // 遍历所有方块,找到一个可以放置的方块
  1185. let bestMove = null;
  1186. let selectedBrickIndex = -1;
  1187. for (let i = 0; i < this.bricksList.length; i++) {
  1188. const move = this.findBestPosition(this.bricksList[i]);
  1189. if (move) {
  1190. // 优先选择可以消除的位置
  1191. if (move.score > 0) {
  1192. bestMove = move;
  1193. selectedBrickIndex = i;
  1194. break;
  1195. } else if (bestMove === null) {
  1196. // 如果还没有找到任何可放置的位置,保存这个位置
  1197. bestMove = move;
  1198. selectedBrickIndex = i;
  1199. }
  1200. }
  1201. }
  1202. // 如果所有方块都没有可放置的位置,游戏结束
  1203. if (!bestMove || selectedBrickIndex === -1) {
  1204. console.log('所有方块都没有可放置的位置,游戏结束');
  1205. this.isAutoMode = false;
  1206. if (this._isAutoFunc) {
  1207. this.unschedule(this._isAutoFunc);
  1208. this._isAutoFunc = null;
  1209. }
  1210. this.gameOver();
  1211. return;
  1212. }
  1213. // 获取选中的方块
  1214. const brickData = this.bricksList[selectedBrickIndex];
  1215. // 从列表中移除方块
  1216. this.bricksList.splice(selectedBrickIndex, 1);
  1217. // 保存方块的初始位置(从待选区开始)
  1218. const startPos = brickData.brickInitPos.clone();
  1219. // 设置方块到移动层并确保位置精确
  1220. if (brickData.brickNode) {
  1221. brickData.brickNode.setParent(this.moveNode);
  1222. brickData.brickNode.setWorldPosition(startPos);
  1223. }
  1224. // this.guideNode.active = true;
  1225. // this.guideNode.setWorldPosition(
  1226. // startPos.x,
  1227. // startPos.y + brickData.brickNode.getComponent(UITransform).height / 2,
  1228. // startPos.z
  1229. // );
  1230. // 如果需要旋转,先旋转到正确的角度
  1231. if (brickData.rotateFlag && bestMove.rotation && bestMove.rotation.deg !== brickData.deg) {
  1232. await new Promise<void>((resolve) => {
  1233. if (brickData.brickNode) {
  1234. tween(brickData.brickNode)
  1235. .to(0.2, { angle: bestMove.rotation ? bestMove.rotation.deg : 0 })
  1236. .call(() => {
  1237. if (bestMove.rotation) {
  1238. brickData.gridConfig = bestMove.rotation.gridConfig;
  1239. brickData.deg = bestMove.rotation.deg;
  1240. }
  1241. resolve();
  1242. })
  1243. .start();
  1244. }
  1245. });
  1246. }
  1247. // 获取目标位置,有些目标位置获取有问题需要修改
  1248. const targetGrid = this.gridList[bestMove.position.row][bestMove.position.column];
  1249. if (!targetGrid.gridNode) {
  1250. console.error('targetGrid.gridNode is null');
  1251. return;
  1252. }
  1253. const targetWorldPos = targetGrid.gridNode.getWorldPosition().clone();
  1254. // 创建平滑的移动动画
  1255. await new Promise<void>((resolve) => {
  1256. if (brickData.brickNode) {
  1257. tween(brickData.brickNode)
  1258. .to(0.3, {
  1259. worldPosition: targetWorldPos,
  1260. }, {
  1261. easing: 'cubicOut'
  1262. })
  1263. .call(() => {
  1264. if (brickData.brickNode) {
  1265. brickData.brickNode.setWorldPosition(targetWorldPos);
  1266. // 放置方块
  1267. if (bestMove.rotation) {
  1268. bestMove.rotation.gridConfig.forEach((gridConfigData) => {
  1269. const gridData = this.gridList[bestMove.position.row + gridConfigData.row][bestMove.position.column + gridConfigData.column];
  1270. gridData.status = GridStatus.Used;
  1271. gridData.gridColorKey = brickData.gridColorKey;
  1272. this.generateGrid(gridData);
  1273. });
  1274. }
  1275. // 销毁方块
  1276. brickData.brickNode.destroy();
  1277. // 添加新方块
  1278. this.addBrick(brickData.index);
  1279. }
  1280. resolve();
  1281. })
  1282. .start();
  1283. }
  1284. // 引导节点跟着移动
  1285. // if (this.guildNode) {
  1286. // tween(this.guideNode)
  1287. // .to(0.3, {
  1288. // worldPosition: targetWorldPos
  1289. // })
  1290. // .call(() => {
  1291. // this.guideNode.active = false;
  1292. // })
  1293. // .start();
  1294. // }
  1295. });
  1296. // 检查并执行消除
  1297. await this.gridEliminate();
  1298. // 检查是否还有任何方块可以放置
  1299. let hasValidMove = false;
  1300. for (const brick of this.bricksList) {
  1301. if (this.findBestPosition(brick)) {
  1302. hasValidMove = true;
  1303. break;
  1304. }
  1305. }
  1306. if (!hasValidMove) {
  1307. console.log('没有方块可以放置,游戏结束');
  1308. this.isAutoMode = false;
  1309. if (this._isAutoFunc) {
  1310. this.unschedule(this._isAutoFunc);
  1311. this._isAutoFunc = null;
  1312. }
  1313. this.gameOver();
  1314. }
  1315. }
  1316. // ... 修改 findBestPosition 方法,这个方法有问题,需要修改
  1317. findBestPosition(brickData: BrickData) {
  1318. let bestScore = -1
  1319. let bestPosition = null
  1320. let bestRotation = null
  1321. let fallbackPosition = null
  1322. let fallbackRotation = null
  1323. // 遍历所有可能的位置
  1324. for (let rowIndex = 0; rowIndex < this.max_row; rowIndex++) {
  1325. for (let columnIndex = 0; columnIndex < this.max_col; columnIndex++) {
  1326. // 检查当前位置的所有可能旋转
  1327. let currentGridConfig = brickData.gridConfig
  1328. let currentDeg = brickData.deg
  1329. // 最多旋转4次(0°, 90°, 180°, 270°)
  1330. for (let rotation = 0; rotation < 4; rotation++) {
  1331. if (this.moveIf(rowIndex, columnIndex, currentGridConfig)) {
  1332. // 如果还没有找到后备位置,保存第一个可放置的位置
  1333. if (fallbackPosition === null) {
  1334. fallbackPosition = { row: rowIndex, column: columnIndex }
  1335. fallbackRotation = { gridConfig: currentGridConfig, deg: currentDeg }
  1336. }
  1337. // 模拟放置并计算得分
  1338. const gridList = this.copyGridList()
  1339. currentGridConfig.forEach((gridConfigData) => {
  1340. gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex].status = GridStatus.Used
  1341. })
  1342. // 计算这个位置的得分
  1343. const eliminateCheck = this.gridEliminateCheck(gridList)
  1344. let positionScore = eliminateCheck.gridEliminateList.length
  1345. // 如果这个位置比之前找到的更好
  1346. if (positionScore > bestScore) {
  1347. bestScore = positionScore
  1348. bestPosition = { row: rowIndex, column: columnIndex }
  1349. bestRotation = { gridConfig: currentGridConfig, deg: currentDeg }
  1350. }
  1351. }
  1352. // 如果方块可以旋转,计算下一个旋转状态
  1353. if (brickData.rotateFlag && rotation < 3) {
  1354. const next = this.nextGridRotate(currentGridConfig, currentDeg)
  1355. currentGridConfig = next.gridConfig
  1356. currentDeg = next.deg
  1357. } else {
  1358. break
  1359. }
  1360. }
  1361. }
  1362. }
  1363. // 如果找到了最佳得分位置,返回它
  1364. if (bestPosition !== null) {
  1365. return {
  1366. position: bestPosition,
  1367. rotation: bestRotation,
  1368. score: bestScore
  1369. }
  1370. }
  1371. // 如果没有找到可消除的位置但有可放置的位置,返回第一个可放置的位置
  1372. if (fallbackPosition !== null) {
  1373. return {
  1374. position: fallbackPosition,
  1375. rotation: fallbackRotation,
  1376. score: 0
  1377. }
  1378. }
  1379. // 如果真的没有任何可放置的位置,返回null
  1380. return null
  1381. }
  1382. //重新开始-这样正常要接广告
  1383. private btn_reopen() {
  1384. console.log("重新开始");
  1385. if (this.game_status === GameStatus.None) return
  1386. this.initGameData()
  1387. }
  1388. // ... 添加自动模式的开关方法
  1389. onAutoButtonClick() {
  1390. if (
  1391. this.game_status !== GameStatus.Start ||
  1392. this.editingFlag
  1393. ) return
  1394. // 切换自动模式状态
  1395. this.isAutoMode = !this.isAutoMode
  1396. if (this.isAutoMode) {
  1397. this._isAutoFunc = () => {
  1398. this.autoPlaceBrick()
  1399. }
  1400. this.schedule(this._isAutoFunc, this.autoModeInterval)
  1401. console.log('开启自动模式')
  1402. } else {
  1403. if (this._isAutoFunc) {
  1404. this.unschedule(this._isAutoFunc)
  1405. this._isAutoFunc = null
  1406. }
  1407. console.log('关闭自动模式')
  1408. }
  1409. }
  1410. private btn_auto() {
  1411. // if (
  1412. // this.game_status !== GameStatus.Start ||
  1413. // this.editingFlag
  1414. // ) return
  1415. // 切换自动模式状态
  1416. this.isAutoMode = !this.isAutoMode
  1417. if (this.isAutoMode) {
  1418. this._isAutoFunc = () => {
  1419. this.autoPlaceBrick()
  1420. }
  1421. this.schedule(this._isAutoFunc, this.autoModeInterval)
  1422. console.log('开启自动模式')
  1423. } else {
  1424. if (this._isAutoFunc) {
  1425. this.unschedule(this._isAutoFunc)
  1426. this._isAutoFunc = null
  1427. }
  1428. console.log('关闭自动模式')
  1429. }
  1430. }
  1431. }