GameBlockSelection.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. import { _decorator, Component, Node, Button, Label, find, UITransform, Sprite, Color, tween, Tween } from 'cc';
  2. import { LevelSessionManager } from '../../Core/LevelSessionManager';
  3. import { BallController } from '../BallController';
  4. import { BlockManager } from '../BlockManager';
  5. import { GameStartMove } from '../../Animations/GameStartMove';
  6. import { GameManager } from '../../LevelSystem/GameManager';
  7. import { GamePause } from '../GamePause';
  8. import { BlockTag } from './BlockTag';
  9. const { ccclass, property } = _decorator;
  10. @ccclass('GameBlockSelection')
  11. export class GameBlockSelection extends Component {
  12. @property({
  13. type: Node,
  14. tooltip: '拖拽BlockSelectionUI/diban/ann001按钮节点到这里'
  15. })
  16. public addBallButton: Node = null;
  17. @property({
  18. type: Node,
  19. tooltip: '拖拽BlockSelectionUI/diban/ann002按钮节点到这里'
  20. })
  21. public addCoinButton: Node = null;
  22. @property({
  23. type: Node,
  24. tooltip: '拖拽BlockSelectionUI/diban/ann003按钮节点到这里'
  25. })
  26. public refreshButton: Node = null;
  27. @property({
  28. type: Node,
  29. tooltip: '拖拽Canvas-001/TopArea/CoinNode/CoinLabel节点到这里'
  30. })
  31. public coinLabelNode: Node = null;
  32. @property({
  33. type: Node,
  34. tooltip: '拖拽Canvas/InsufficientCoinsUI节点到这里'
  35. })
  36. public insufficientCoinsUI: Node = null;
  37. @property({
  38. type: Node,
  39. tooltip: '拖拽Canvas/GameLevelUI/BallController节点到这里'
  40. })
  41. public ballControllerNode: Node = null;
  42. @property({
  43. type: Node,
  44. tooltip: '拖拽Canvas/GameLevelUI/BlockController节点到这里'
  45. })
  46. public blockManagerNode: Node = null;
  47. @property({
  48. type: Node,
  49. tooltip: '拖拽Canvas/Camera节点到这里'
  50. })
  51. public cameraNode: Node = null;
  52. @property({
  53. type: Node,
  54. tooltip: '拖拽Canvas/GameLevelUI/GameArea/GridContainer节点到这里'
  55. })
  56. public gridContainer: Node = null;
  57. @property({
  58. type: Node,
  59. tooltip: '拖拽confirm按钮节点到这里'
  60. })
  61. public confirmButton: Node = null;
  62. @property({
  63. type: Node,
  64. tooltip: '拖拽BlockSelectionUI根节点到这里'
  65. })
  66. public blockSelectionUINode: Node = null;
  67. @property({
  68. type: Node,
  69. tooltip: '拖拽BlockSelectionUI/diban节点到这里'
  70. })
  71. public dibanNode: Node = null;
  72. // 按钮费用配置
  73. private readonly ADD_BALL_COST = 80;
  74. private readonly ADD_COIN_AMOUNT = 80;
  75. private readonly REFRESH_COST = 5;
  76. private session: LevelSessionManager = null;
  77. private ballController: BallController = null;
  78. private blockManager: BlockManager = null;
  79. private gameStartMove: GameStartMove = null;
  80. private gameManager: GameManager = null;
  81. // 回调函数,用于通知GameManager
  82. public onConfirmCallback: () => void = null;
  83. start() {
  84. console.log('GameBlockSelection.start() 开始初始化');
  85. // 获取管理器实例
  86. this.session = LevelSessionManager.inst;
  87. // 获取BallController
  88. if (this.ballControllerNode) {
  89. this.ballController = this.ballControllerNode.getComponent(BallController);
  90. } else {
  91. console.warn('BallController节点未绑定,请在Inspector中拖拽Canvas/GameLevelUI/BallController节点');
  92. }
  93. // 获取BlockManager
  94. if (this.blockManagerNode) {
  95. this.blockManager = this.blockManagerNode.getComponent(BlockManager);
  96. } else {
  97. console.warn('BlockManager节点未绑定,请在Inspector中拖拽Canvas/GameLevelUI/BlockController节点');
  98. }
  99. // 获取GameStartMove组件
  100. if (this.cameraNode) {
  101. this.gameStartMove = this.cameraNode.getComponent(GameStartMove);
  102. console.log('GameStartMove组件获取结果:', !!this.gameStartMove);
  103. // 如果GameStartMove存在,设置BlockSelectionUI和diban引用,并更新原始位置
  104. if (this.gameStartMove && this.blockSelectionUINode && this.dibanNode) {
  105. this.gameStartMove.blockSelectionUI = this.blockSelectionUINode;
  106. this.gameStartMove.dibanNode = this.dibanNode;
  107. this.gameStartMove.updateDibanOriginalPosition();
  108. console.log('GameStartMove引用设置完成:', {
  109. blockSelectionUISet: !!this.gameStartMove.blockSelectionUI,
  110. dibanNodeSet: !!this.gameStartMove.dibanNode,
  111. blockSelectionUINodeName: this.blockSelectionUINode.name,
  112. dibanNodeName: this.dibanNode.name
  113. });
  114. } else {
  115. console.warn('GameStartMove引用设置失败:', {
  116. gameStartMove: !!this.gameStartMove,
  117. blockSelectionUINode: !!this.blockSelectionUINode,
  118. dibanNode: !!this.dibanNode
  119. });
  120. }
  121. } else {
  122. console.warn('Camera节点未绑定,请在Inspector中拖拽Canvas/Camera节点');
  123. }
  124. // 如果没有指定coinLabelNode,尝试找到它
  125. if (!this.coinLabelNode) {
  126. this.coinLabelNode = find('Canvas-001/TopArea/CoinNode/CoinLabel');
  127. }
  128. // 获取GameManager实例
  129. const gameManagerNode = find('Canvas/GameManager');
  130. if (gameManagerNode) {
  131. this.gameManager = gameManagerNode.getComponent(GameManager);
  132. }
  133. // 绑定按钮事件
  134. this.bindButtonEvents();
  135. console.log('GameBlockSelection.start() 初始化完成');
  136. }
  137. // 绑定按钮事件
  138. private bindButtonEvents() {
  139. // 绑定新增小球按钮
  140. if (this.addBallButton) {
  141. const btn = this.addBallButton.getComponent(Button);
  142. if (btn) {
  143. this.addBallButton.on(Button.EventType.CLICK, this.onAddBallClicked, this);
  144. } else {
  145. this.addBallButton.on(Node.EventType.TOUCH_END, this.onAddBallClicked, this);
  146. }
  147. }
  148. // 绑定增加金币按钮
  149. if (this.addCoinButton) {
  150. const btn = this.addCoinButton.getComponent(Button);
  151. if (btn) {
  152. this.addCoinButton.on(Button.EventType.CLICK, this.onAddCoinClicked, this);
  153. } else {
  154. this.addCoinButton.on(Node.EventType.TOUCH_END, this.onAddCoinClicked, this);
  155. }
  156. }
  157. // 绑定刷新方块按钮
  158. if (this.refreshButton) {
  159. const btn = this.refreshButton.getComponent(Button);
  160. if (btn) {
  161. this.refreshButton.on(Button.EventType.CLICK, this.onRefreshClicked, this);
  162. } else {
  163. this.refreshButton.on(Node.EventType.TOUCH_END, this.onRefreshClicked, this);
  164. }
  165. }
  166. // 绑定确认按钮
  167. if (this.confirmButton) {
  168. const btn = this.confirmButton.getComponent(Button);
  169. if (btn) {
  170. this.confirmButton.on(Button.EventType.CLICK, this.onConfirmButtonClicked, this);
  171. } else {
  172. this.confirmButton.on(Node.EventType.TOUCH_END, this.onConfirmButtonClicked, this);
  173. }
  174. }
  175. }
  176. // 新增小球按钮点击
  177. private onAddBallClicked() {
  178. if (!this.canSpendCoins(this.ADD_BALL_COST)) {
  179. this.showInsufficientCoinsUI();
  180. return;
  181. }
  182. // 扣除金币
  183. if (this.session.spendCoins(this.ADD_BALL_COST)) {
  184. this.updateCoinDisplay();
  185. // 创建新的小球
  186. if (this.ballControllerNode) {
  187. const ballController = this.ballControllerNode.getComponent(BallController);
  188. if (ballController) {
  189. // 使用createAdditionalBall方法创建额外的小球,而不是替换现有的小球
  190. ballController.createAdditionalBall();
  191. console.log(`新增小球成功,扣除${this.ADD_BALL_COST}金币`);
  192. } else {
  193. console.error('找不到BallController组件');
  194. }
  195. } else {
  196. console.error('找不到BallController节点,无法创建小球');
  197. }
  198. }
  199. }
  200. // 增加金币按钮点击
  201. private onAddCoinClicked() {
  202. this.session.addCoins(this.ADD_COIN_AMOUNT);
  203. this.updateCoinDisplay();
  204. console.log(`增加${this.ADD_COIN_AMOUNT}金币`);
  205. }
  206. // 刷新方块按钮点击
  207. private onRefreshClicked() {
  208. if (!this.canSpendCoins(this.REFRESH_COST)) {
  209. this.showInsufficientCoinsUI();
  210. return;
  211. }
  212. // 扣除金币
  213. if (this.session.spendCoins(this.REFRESH_COST)) {
  214. this.updateCoinDisplay();
  215. // 刷新方块
  216. if (this.blockManager) {
  217. // 找到PlacedBlocks容器
  218. const placedBlocksContainer = find('Canvas/GameLevelUI/PlacedBlocks');
  219. if (placedBlocksContainer) {
  220. // 移除已放置方块的标签
  221. BlockTag.removeTagsInContainer(placedBlocksContainer);
  222. }
  223. // 刷新方块
  224. this.blockManager.refreshBlocks();
  225. console.log(`刷新方块成功,扣除${this.REFRESH_COST}金币`);
  226. } else {
  227. console.error('找不到BlockManager,无法刷新方块');
  228. }
  229. }
  230. }
  231. // 确认按钮点击(从GameManager迁移的onConfirmButtonClicked)
  232. public onConfirmButtonClicked() {
  233. // 使用统一的隐藏方法,包含动画
  234. this.hideBlockSelection();
  235. // 保存已放置的方块
  236. this.preservePlacedBlocks();
  237. // 回调通知GameManager
  238. if (this.onConfirmCallback) {
  239. this.onConfirmCallback();
  240. }
  241. // 恢复游戏
  242. const gamePause = GamePause.getInstance();
  243. if (gamePause) {
  244. gamePause.resumeGame();
  245. }
  246. }
  247. // 保存已放置的方块(从GameManager迁移)
  248. private preservePlacedBlocks() {
  249. if (this.blockManager) {
  250. this.blockManager.onGameStart();
  251. }
  252. }
  253. // 检查是否有足够金币
  254. private canSpendCoins(amount: number): boolean {
  255. return this.session.getCoins() >= amount;
  256. }
  257. // 更新金币显示
  258. private updateCoinDisplay() {
  259. if (this.coinLabelNode) {
  260. const label = this.coinLabelNode.getComponent(Label);
  261. if (label) {
  262. label.string = this.session.getCoins().toString();
  263. }
  264. }
  265. }
  266. // 显示金币不足UI
  267. private showInsufficientCoinsUI() {
  268. if (!this.insufficientCoinsUI) {
  269. console.error('金币不足UI节点未绑定,请在Inspector中拖拽Canvas/InsufficientCoinsUI节点');
  270. return;
  271. }
  272. // 显示金币不足UI
  273. this.insufficientCoinsUI.active = true;
  274. // 3秒后自动隐藏
  275. this.scheduleOnce(() => {
  276. if (this.insufficientCoinsUI && this.insufficientCoinsUI.isValid) {
  277. this.insufficientCoinsUI.active = false;
  278. }
  279. }, 3.0);
  280. console.log('金币不足!');
  281. }
  282. // === 公共方法:供GameManager调用 ===
  283. // 显示方块选择UI(用于游戏开始或下一波)
  284. public showBlockSelection(isNextWave: boolean = false) {
  285. // 检查游戏是否已结束(胜利或失败),如果是则不显示方块选择UI
  286. if (this.gameManager && this.gameManager.isGameOver()) {
  287. console.warn('[GameBlockSelection] 游戏已经结束(胜利或失败),不显示方块选择UI');
  288. return;
  289. }
  290. // 首先显示UI节点
  291. this.node.active = true;
  292. if (this.gridContainer) {
  293. this.gridContainer.active = true;
  294. }
  295. // 如果有BlockSelectionUI节点,确保它可见
  296. if (this.blockSelectionUINode) {
  297. this.blockSelectionUINode.active = true;
  298. }
  299. // 每次显示方块选择UI时,生成新的随机方块
  300. if (this.blockManager) {
  301. this.blockManager.refreshBlocks();
  302. console.log('[GameBlockSelection] 生成新的随机方块');
  303. } else {
  304. console.warn('[GameBlockSelection] BlockManager未找到,无法生成随机方块');
  305. }
  306. // 播放BlockSelectionUI出现动画
  307. this.playShowAnimation();
  308. console.log(`[GameBlockSelection] ${isNextWave ? '显示下一波方块选择UI' : '显示游戏开始方块选择UI'}`);
  309. }
  310. // 隐藏方块选择UI
  311. public hideBlockSelection() {
  312. // 播放隐藏动画,动画完成后隐藏UI
  313. this.playHideAnimation(() => {
  314. this.node.active = false;
  315. // 移除隐藏GridContainer的代码,因为GridContainer应该在游戏过程中保持可见
  316. // if (this.gridContainer) {
  317. // this.gridContainer.active = false;
  318. // }
  319. console.log('隐藏方块选择UI');
  320. });
  321. }
  322. // 播放显示动画
  323. private playShowAnimation() {
  324. console.log('播放显示动画playShowAnimation');
  325. if (this.gameStartMove && this.blockSelectionUINode && this.dibanNode) {
  326. // 设置GameStartMove的blockSelectionUI和dibanNode引用
  327. this.gameStartMove.blockSelectionUI = this.blockSelectionUINode;
  328. this.gameStartMove.dibanNode = this.dibanNode;
  329. // 每次显示BlockSelectionUI时,摄像头下移182px
  330. this.gameStartMove.moveDownInstant();
  331. console.log('摄像头下移182px');
  332. // 使用GameStartMove的上滑入场动画
  333. this.gameStartMove.slideUpFromBottom(300, 0.3);
  334. }
  335. }
  336. // 播放隐藏动画
  337. private playHideAnimation(onComplete?: () => void) {
  338. console.log('播放隐藏动画playHideAnimation');
  339. console.log('GameBlockSelection 引用检查:', {
  340. gameStartMove: !!this.gameStartMove,
  341. blockSelectionUINode: !!this.blockSelectionUINode,
  342. dibanNode: !!this.dibanNode,
  343. blockSelectionUINodeName: this.blockSelectionUINode?.name,
  344. dibanNodeName: this.dibanNode?.name
  345. });
  346. if (this.gameStartMove && this.blockSelectionUINode && this.dibanNode) {
  347. // 设置GameStartMove的blockSelectionUI和dibanNode引用
  348. this.gameStartMove.blockSelectionUI = this.blockSelectionUINode;
  349. this.gameStartMove.dibanNode = this.dibanNode;
  350. console.log('GameStartMove 引用设置后检查:', {
  351. gameStartMoveBlockSelectionUI: !!this.gameStartMove.blockSelectionUI,
  352. gameStartMoveDibanNode: !!this.gameStartMove.dibanNode
  353. });
  354. // 每次隐藏BlockSelectionUI时,摄像头上移182px
  355. this.gameStartMove.moveUpSmooth();
  356. console.log('摄像头上移182px');
  357. // 播放下滑隐藏动画,动画完成后执行回调
  358. this.gameStartMove.slideDibanDownAndHide(300, 0.3);
  359. // 由于slideDibanDownAndHide会自动隐藏blockSelectionUI和重置位置,我们需要在动画完成后执行回调
  360. if (onComplete) {
  361. this.scheduleOnce(() => {
  362. onComplete();
  363. }, 0.3); // 与动画时长一致
  364. }
  365. } else {
  366. console.log('GameBlockSelection 条件检查失败,无法播放动画');
  367. if (onComplete) {
  368. // 如果没有动画组件,直接执行回调
  369. onComplete();
  370. }
  371. }
  372. }
  373. // 设置确认回调
  374. public setConfirmCallback(callback: () => void) {
  375. this.onConfirmCallback = callback;
  376. }
  377. }