GainUI.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { _decorator, Component, Node, Label, Sprite, Button } from 'cc';
  2. import EventBus, { GameEvents } from '../Core/EventBus';
  3. import { JsonConfigLoader } from '../Core/JsonConfigLoader';
  4. import { BundleLoader } from '../Core/BundleLoader';
  5. const { ccclass, property } = _decorator;
  6. @ccclass('GainUI')
  7. export class GainUI extends Component {
  8. @property(Node) panel: Node = null; // 弹窗根节点
  9. @property(Label) titleLabel: Label = null; // 标题,如“新武器”
  10. @property(Sprite) iconSprite: Sprite = null; // 武器图标
  11. @property(Label) nameLabel: Label = null; // 武器名称
  12. @property(Button) confirmBtn: Button = null; // 确认按钮
  13. private weaponsConfig: any = null;
  14. private bundleLoader: BundleLoader = null;
  15. // 等待展示的武器名称队列(来自 UpgradeController 派发的 NEW_WEAPONS_UNLOCKED)
  16. private pendingWeapons: string[] = [];
  17. // 是否已经完成奖励动画(由 MoneyAni 派发 REWARD_ANIMATION_COMPLETED)
  18. private rewardAnimationCompleted = false;
  19. // 当前是否正在显示弹窗
  20. private showing = false;
  21. async onLoad() {
  22. this.bundleLoader = BundleLoader.getInstance();
  23. // 默认隐藏弹窗
  24. if (this.panel) this.panel.active = false;
  25. console.log('[GainUI] 已隐藏弹窗(默认状态)');
  26. // 绑定事件
  27. EventBus.getInstance().on(GameEvents.NEW_WEAPONS_UNLOCKED, this.onNewWeaponsUnlocked, this);
  28. EventBus.getInstance().on(GameEvents.REWARD_ANIMATION_COMPLETED, this.onRewardAnimationCompleted, this);
  29. // 绑定确认按钮
  30. this.confirmBtn?.node.on(Button.EventType.CLICK, this.onConfirm, this);
  31. }
  32. async start() {
  33. // 预加载武器配置,便于查找图标与名称
  34. try {
  35. this.weaponsConfig = await JsonConfigLoader.getInstance().loadConfig('weapons');
  36. } catch (err) {
  37. console.warn('[GainUI] 加载武器配置失败:', err);
  38. }
  39. }
  40. onDestroy() {
  41. EventBus.getInstance().off(GameEvents.NEW_WEAPONS_UNLOCKED, this.onNewWeaponsUnlocked, this);
  42. EventBus.getInstance().off(GameEvents.REWARD_ANIMATION_COMPLETED, this.onRewardAnimationCompleted, this);
  43. this.confirmBtn?.node.off(Button.EventType.CLICK, this.onConfirm, this);
  44. }
  45. // 处理新武器解锁事件
  46. private onNewWeaponsUnlocked = (weaponNames: string[]) => {
  47. if (!weaponNames || weaponNames.length === 0) return;
  48. this.pendingWeapons.push(...weaponNames);
  49. this.tryShowPopup();
  50. }
  51. // 处理奖励动画完成事件
  52. private onRewardAnimationCompleted = () => {
  53. this.rewardAnimationCompleted = true;
  54. this.tryShowPopup();
  55. }
  56. // 在满足条件时显示弹窗:需要奖励动画已完成且队列非空
  57. private async tryShowPopup() {
  58. if (this.showing) return;
  59. if (!this.rewardAnimationCompleted) return;
  60. if (this.pendingWeapons.length === 0) return;
  61. const nextName = this.pendingWeapons.shift();
  62. await this.renderWeapon(nextName);
  63. // 激活并确保置顶显示,避免被MainUI/TopBar覆盖
  64. if (this.panel) {
  65. this.panel.active = true;
  66. const parent = this.panel.parent;
  67. if (parent) {
  68. this.panel.setSiblingIndex(parent.children.length - 1);
  69. }
  70. }
  71. this.showing = true;
  72. }
  73. // 确认按钮逻辑:继续展示队列中的下一个或关闭弹窗
  74. private async onConfirm() {
  75. if (this.pendingWeapons.length > 0) {
  76. const nextName = this.pendingWeapons.shift();
  77. await this.renderWeapon(nextName);
  78. } else {
  79. if (this.panel) this.panel.active = false;
  80. this.showing = false;
  81. }
  82. }
  83. // 根据武器名称渲染弹窗内容(标题、图标、名称)
  84. private async renderWeapon(weaponName: string) {
  85. if (this.titleLabel) this.titleLabel.string = '新武器';
  86. if (this.nameLabel) this.nameLabel.string = weaponName || '';
  87. // 查找武器配置,加载图标
  88. const config = this.findWeaponConfigByName(weaponName);
  89. if (config && config.visualConfig && config.visualConfig.weaponSprites) {
  90. let spritePath: string;
  91. const sprites = config.visualConfig.weaponSprites;
  92. if (typeof sprites === 'string') {
  93. spritePath = sprites;
  94. } else {
  95. spritePath = sprites['I'] || sprites['H-I'] || sprites['L'] || sprites['S'] || sprites['D-T'];
  96. }
  97. if (spritePath && this.iconSprite) {
  98. await this.loadWeaponSprite(this.iconSprite, spritePath);
  99. }
  100. }
  101. }
  102. private findWeaponConfigByName(name: string) {
  103. if (!this.weaponsConfig || !this.weaponsConfig.weapons) return null;
  104. return this.weaponsConfig.weapons.find((w: any) => w.name === name);
  105. }
  106. private async loadWeaponSprite(sprite: Sprite, spritePath: string) {
  107. const bundlePath = spritePath.replace(/^images\//, '');
  108. try {
  109. const spriteFrame = await this.bundleLoader.loadSpriteFrame(bundlePath + '/spriteFrame');
  110. if (spriteFrame && sprite && sprite.isValid) {
  111. sprite.spriteFrame = spriteFrame;
  112. }
  113. } catch (err) {
  114. console.warn(`[GainUI] 加载武器图标失败: ${spritePath}`, err);
  115. }
  116. }
  117. }