GameBlockSelection.ts 15 KB

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