UI_Main.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. import { _decorator, Component, director, game, instantiate, Label, Node, NodePool, PhysicsSystem2D, randomRangeInt, Scene, Scheduler, Sprite, SpriteFrame, v3, Vec3 } from 'cc';
  2. import { GameUILayers, gui } from 'db://assets/core/ui/ui';
  3. import ui_base from 'db://assets/core/ui/ui_base';
  4. import { ModuleDef } from 'db://assets/Script/ModuleDef';
  5. import { Layout_Main } from './Layout_Main';
  6. import { UI_Settings } from '../UI_Settings/UI_Settings';
  7. import { skin_res } from '../../game/link/skin/skin_res';
  8. import { BlockLink } from '../../game/link/block/BlockLink';
  9. import { ch } from 'db://assets/ch/ch';
  10. import { UI_Win } from '../UI_Win/UI_Win';
  11. import { LvData } from '../../game/LvData/LvData';
  12. import { UI_Lose } from '../UI_Lose/UI_Lose';
  13. import { Stone } from '../../game/tui/stone/Stone';
  14. import { Role } from '../../game/tui/role/Role';
  15. import { GameState } from '../../game/GameState';
  16. import { audioManager } from '../../Audio/AudioManager';
  17. import { Hall } from '../../hall/Hall';
  18. import { data_type } from '../../game/PlayerData';
  19. const { ccclass, property } = _decorator;
  20. interface event_protocol {
  21. pause(): void;//游戏暂停
  22. resume(): void;//游戏恢复
  23. next_level(): void;//下一关
  24. }
  25. @ccclass('UI_Main')
  26. export class UI_Main extends ui_base {
  27. public lt: Layout_Main;
  28. public static lt: Layout_Main;
  29. lv = Hall.getInstance().player.data.get(data_type.max_floor)
  30. public evt = ch.get_new_event<event_protocol>();
  31. running = true;
  32. constructor() {
  33. super(ModuleDef.GAME, 'ui/main/main', GameUILayers.GAME, Layout_Main);
  34. }
  35. private _stone: number = 0;//掉落的石頭
  36. private totalStones: number = 120; // 总石头数量
  37. static blockArry: Node[][] = [];
  38. //石头对象池
  39. private _stonePool: NodePool;
  40. private _activeStones: Node[] = [];
  41. //方块对象池
  42. private _blockPool: NodePool;
  43. private _activeBlocks: Node[] = [];
  44. private _paused: boolean = false;
  45. get is_paused(): boolean { return this._paused; }
  46. protected async onCreated() {
  47. audioManager.play('sound/bgm');
  48. this.lt = this.getLayout<Layout_Main>();
  49. UI_Main.lt = this.lt;
  50. this.onButtonEvent(UI_Main.lt.setBtn, () => {
  51. gui.show(UI_Settings);
  52. this.pause();
  53. }, this);
  54. this._stonePool = new NodePool('Stone');
  55. this.preloadStonePool(50); // 预加载50个石头
  56. this.init()
  57. this.evt.on(this.evt.key.pause, this.pause, this);
  58. this.evt.on(this.evt.key.resume, this.resume, this);
  59. this.evt.on(this.evt.key.next_level, this.new_level, this);
  60. }
  61. public pause(): void {
  62. this._paused = true;
  63. // DirectorUtil.pause();
  64. UI_Main.lt.title.set_stop();
  65. }
  66. //
  67. public resume(): void {
  68. this._paused = false;
  69. // DirectorUtil.resume();
  70. UI_Main.lt.title.set_resume();
  71. }
  72. init() {
  73. for (let i = 0; i < 20; i++) {
  74. this.lt.scheduleOnce(() => {
  75. this.creatorStone(10);//石头
  76. }, i * 0.1)
  77. }
  78. this.creatorBlock();//llk
  79. UI_Main.lt.role.init(endPos.time)
  80. UI_Main.lt.pushItem.init(endPos.time)
  81. this.setingLV();
  82. }
  83. new_level() {
  84. this.setingLV();
  85. this.close();
  86. Hall.getInstance().to_main();
  87. }
  88. setingLV() {
  89. const layout = this.getLayout<Layout_Main>();
  90. debugger
  91. let currentLevel = this.lv; // 从存档读取当前关卡
  92. if (currentLevel <= 0) {
  93. currentLevel = 1;
  94. }
  95. UI_Main.lt.Level.string = currentLevel.toString();
  96. const passedLevels = currentLevel - 1;
  97. const group = Math.floor(passedLevels / 4);
  98. const unlockPos = passedLevels % 4;
  99. // 动态控制循环次数,避免越界
  100. const maxCircles = Math.min(layout.LevelNum.length, 4);
  101. for (let i = 0; i < maxCircles; i++) {
  102. const num = group * 4 + 1 + i;
  103. layout.LevelNum[i].string = num.toString();
  104. // 新组起始关(第5关)时强制解锁第一个节点
  105. const isNewGroupStart = unlockPos === 0 && passedLevels > 0;
  106. const shouldUnlock = isNewGroupStart ? i === 0 : i <= unlockPos;
  107. layout.LevelNode[i].active = shouldUnlock;
  108. }
  109. }
  110. addStone(count: number) {
  111. this._stone += count
  112. this.flash();
  113. this.checkVictory();
  114. }
  115. flash() {
  116. UI_Main.lt.title.filled(this._stone)
  117. }
  118. checkVictory() {
  119. const remainingTime = UI_Main.lt.title.getRemainingTime();
  120. if (this._stone == this.totalStones && remainingTime > 0) {
  121. console.log("游戏胜利!");
  122. // 更新
  123. Hall.getInstance().player.data.add(data_type.max_floor, 1);
  124. Hall.getInstance().player.setDirty();
  125. Hall.getInstance().player.save_rank_floor();
  126. Hall.getInstance().player.save_Province_floor();
  127. Hall.getInstance().player.save();
  128. gui.show(UI_Win);
  129. UI_Main.lt.title.unschedule(UI_Main.lt.title.updateTime);
  130. UI_Main.lt.unscheduleAllCallbacks(); // 停止所有update
  131. }
  132. else if (this._stone < this.totalStones && remainingTime < 0) {
  133. console.log("游戏失败!");
  134. gui.show(UI_Lose);
  135. }
  136. }
  137. public async creatorBlock() {
  138. const startX = -315;
  139. const startY = 40;
  140. const spacing = 70;
  141. const tempBlock = instantiate(UI_Main.lt.block);
  142. const blockProto = tempBlock.getChildByName("icon").getComponent(skin_res);
  143. let allSkins: SpriteFrame[] = [];
  144. // 获取skin4的唯一图片
  145. const skin4Image = blockProto.skin4[0];
  146. if (LvData.instance.fkpf == 1) {
  147. allSkins = [
  148. ...blockProto.skin1,
  149. ...blockProto.skin2,
  150. ...blockProto.skin3
  151. ].slice(0, 21); // 使用前21种皮肤
  152. } else if (LvData.instance.fkpf == 2) {
  153. allSkins = [
  154. ...blockProto.skin5,
  155. ...blockProto.skin6,
  156. ...blockProto.skin7
  157. ].slice(0, 21); // 使用前21种皮肤
  158. } else if (LvData.instance.fkpf == 3) {
  159. allSkins = [
  160. ...blockProto.skin8,
  161. ...blockProto.skin9,
  162. ...blockProto.skin10
  163. ].slice(0, 21);
  164. } else if (LvData.instance.fkpf == 4) {
  165. allSkins = [
  166. ...blockProto.skin11,
  167. ...blockProto.skin12,
  168. ...blockProto.skin13
  169. ].slice(0, 21); // 使用前21种皮肤
  170. }
  171. tempBlock.destroy();
  172. // 生成随机皮肤对(48对=96个)
  173. let requiredSkins: SpriteFrame[] = [];
  174. for (let i = 0; i < 48; i++) {
  175. const skin = allSkins[Math.floor(Math.random() * allSkins.length)];
  176. requiredSkins.push(skin, skin); // 成对添加
  177. }
  178. // 添加2对(4个)skin4图片
  179. requiredSkins.push(skin4Image, skin4Image); // 第一对
  180. requiredSkins.push(skin4Image, skin4Image); // 第二对
  181. // 洗牌算法
  182. const shuffleArray = (array: SpriteFrame[]) => {
  183. for (let i = array.length - 1; i > 0; i--) {
  184. const j = Math.floor(Math.random() * (i + 1));
  185. [array[i], array[j]] = [array[j], array[i]];
  186. }
  187. return array;
  188. };
  189. // 打乱顺序
  190. const shuffledSkins = shuffleArray([...requiredSkins]);
  191. // 创建10x10网格
  192. const grid: SpriteFrame[][] = Array(10).fill(null).map(() => Array(10).fill(null));
  193. // 填充网格并避免相邻相同
  194. for (let y = 0; y < 10; y++) {
  195. for (let x = 0; x < 10; x++) {
  196. const index = y * 10 + x;
  197. let skin = shuffledSkins[index];
  198. // 检查左邻居和上邻居
  199. const neighbors: SpriteFrame[] = [];
  200. if (x > 0) neighbors.push(grid[y][x - 1]); // 左邻居
  201. if (y > 0) neighbors.push(grid[y - 1][x]); // 上邻居
  202. // 如果与邻居相同,尝试交换后续皮肤
  203. if (neighbors.some(n => n === skin)) {
  204. for (let swapIndex = index + 1; swapIndex < shuffledSkins.length; swapIndex++) {
  205. const candidate = shuffledSkins[swapIndex];
  206. if (neighbors.indexOf(candidate) === -1) {
  207. // 交换皮肤
  208. [shuffledSkins[index], shuffledSkins[swapIndex]] =
  209. [shuffledSkins[swapIndex], shuffledSkins[index]];
  210. skin = shuffledSkins[index];
  211. break;
  212. }
  213. }
  214. }
  215. grid[y][x] = skin;
  216. }
  217. }
  218. // 创建对象池
  219. if (!this._blockPool) {
  220. this._blockPool = new NodePool('Block');
  221. }
  222. // 清空现有方块
  223. this.clearAllBlocks();
  224. for (let y = 0; y < 10; y++) {
  225. BlockLink.blockArry[y] = [];
  226. for (let x = 0; x < 10; x++) {
  227. let blockInstance: Node;
  228. // 从对象池获取或创建
  229. if (this._blockPool.size() > 0) {
  230. blockInstance = this._blockPool.get();
  231. } else {
  232. blockInstance = instantiate(UI_Main.lt.block);
  233. }
  234. const icon = blockInstance.getChildByName("icon")?.getComponent(Sprite);
  235. const blockicon = blockInstance.getComponent(Sprite)
  236. const blockProto = blockInstance.getChildByName("icon").getComponent(skin_res);
  237. if (LvData.instance.fkpf == 1) {
  238. blockicon.spriteFrame = blockProto.fk1[0]
  239. } else if (LvData.instance.fkpf == 2) {
  240. blockicon.spriteFrame = blockProto.fk2[0]
  241. } else if (LvData.instance.fkpf == 3) {
  242. blockicon.spriteFrame = blockProto.fk3[0]
  243. } else if (LvData.instance.fkpf == 4) {
  244. blockicon.spriteFrame = blockProto.fk4[0]
  245. }
  246. if (icon) {
  247. icon.spriteFrame = grid[y][x]; // 使用网格中的皮肤
  248. }
  249. const posX = startX + x * spacing;
  250. const posY = startY - y * spacing;
  251. blockInstance.setPosition(posX, posY);
  252. blockInstance.active = true;
  253. UI_Main.lt.blockNode.addChild(blockInstance);
  254. BlockLink.blockArry[y][x] = blockInstance;
  255. this._activeBlocks.push(blockInstance);
  256. }
  257. }
  258. BlockLink.totalBlocks = 100;
  259. BlockLink.remainingBlocks = BlockLink.totalBlocks;
  260. }
  261. // 回收方块
  262. public recycleBlock(blockNode: Node) {
  263. blockNode.active = false;
  264. UI_Main.lt.blockNode.removeChild(blockNode);
  265. this._blockPool.put(blockNode);
  266. // 从活动列表中移除
  267. const index = this._activeBlocks.indexOf(blockNode);
  268. if (index !== -1) {
  269. this._activeBlocks.splice(index, 1);
  270. }
  271. }
  272. // 清空所有方块
  273. public clearAllBlocks() {
  274. while (this._activeBlocks.length > 0) {
  275. this.recycleBlock(this._activeBlocks[0]);
  276. }
  277. BlockLink.blockArry = [];
  278. }
  279. // 预加载对象池
  280. preloadStonePool(count: number) {
  281. for (let i = 0; i < count; i++) {
  282. const stone = instantiate(UI_Main.lt.stone);
  283. stone.getComponent(Stone)?.init(this);
  284. // stone.getComponent(Stone)?.init(this,GameState.inst.spf[0]);
  285. this.returnStoneToPool(stone);
  286. }
  287. }
  288. private getStoneFromPool(): Node | null {
  289. if (this._stonePool.size() > 0) {
  290. return this._stonePool.get();
  291. }
  292. return null;
  293. }
  294. private returnStoneToPool(stoneNode: Node) {
  295. stoneNode.active = false;
  296. this._stonePool.put(stoneNode);
  297. }
  298. //创建石头
  299. public async creatorStone(count: number) {
  300. //debugger
  301. const centerPos = new Vec3(0, 0, 0);
  302. for (let i = 0; i < count; i++) {
  303. let stoneInstance: Node = null;
  304. if (this._stonePool.size() > 0) {
  305. stoneInstance = this._stonePool.get()
  306. } else {
  307. stoneInstance = instantiate(UI_Main.lt.stone);
  308. stoneInstance.getComponent(Stone)?.init(this); // 传递UI_Main引用
  309. }
  310. const blockProto = stoneInstance.getComponent(Stone);
  311. const blockiconSprite = stoneInstance.getComponent(Sprite)
  312. if (LvData.instance.stpf == 1) {
  313. blockiconSprite.spriteFrame = blockProto.stoneSkin1[0]
  314. } else if (LvData.instance.stpf == 2) {
  315. blockiconSprite.spriteFrame = blockProto.stoneSkin2[0]
  316. } else if (LvData.instance.stpf == 3) {
  317. blockiconSprite.spriteFrame = blockProto.stoneSkin3[0]
  318. } else if (LvData.instance.stpf == 4) {
  319. blockiconSprite.spriteFrame = blockProto.stoneSkin4[0]
  320. }
  321. let scale = Math.random() * 0.3 + 0.8
  322. stoneInstance.scale = v3(scale, scale, 1)
  323. //在stoneNode的位置附近偏移
  324. const offsetX = randomRangeInt(-5, 5); // 水平方向随机偏移
  325. const offsetY = randomRangeInt(-2, 2); // 垂直方向随机偏移
  326. const spawnPos = new Vec3(
  327. centerPos.x + offsetX,
  328. centerPos.y + offsetY,
  329. centerPos.z
  330. );
  331. stoneInstance.active = true;
  332. UI_Main.lt.stoneNode.addChild(stoneInstance);
  333. this._activeStones.push(stoneInstance);
  334. stoneInstance.setPosition(spawnPos);
  335. }
  336. }
  337. // 回收石头到对象池
  338. public recycleStone(stoneNode: Node) {
  339. stoneNode.active = false;
  340. UI_Main.lt.stoneNode.removeChild(stoneNode);
  341. this._stonePool.put(stoneNode);
  342. // 从活动列表中移除
  343. const index = this._activeStones.indexOf(stoneNode);
  344. if (index !== -1) {
  345. this._activeStones.splice(index, 1);
  346. }
  347. this.addStone(1);
  348. }
  349. // 清空所有石头
  350. public clearAllStones() {
  351. while (this._activeStones.length > 0) {
  352. this.recycleStone(this._activeStones[0]);
  353. }
  354. }
  355. protected onUpdate(dt: number) {
  356. UI_Main.lt.role.doMove(dt);
  357. UI_Main.lt.pushItem.resize(dt)
  358. }
  359. public static get role(): Role {
  360. return UI_Main.lt.role;
  361. }
  362. }
  363. export const endPos = {
  364. // 终点
  365. role: v3(-200, 220),
  366. pushItem: 500,
  367. ballEndY: -1200,
  368. time: 150
  369. }