UpgradeAni.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. import { _decorator, Component, Node, Tween, tween, Vec3, Material, Sprite, resources, Label, JsonAsset } from 'cc';
  2. import { JsonConfigLoader } from '../../Core/JsonConfigLoader';
  3. import { Audio } from '../../AudioManager/AudioManager';
  4. import BlinkScaleAnimator from '../../Animations/BlinkScaleAnimator';
  5. const { ccclass, property } = _decorator;
  6. /**
  7. * 植物升级动画控制器
  8. * 负责管理植物升级相关的动画效果
  9. */
  10. @ccclass('UpgradeAni')
  11. export class UpgradeAni extends Component {
  12. @property(Node)
  13. weaponSpriteNode: Node = null;
  14. @property(Node)
  15. upgradeBtnNode: Node = null; // Canvas/UpgradePanel/UpgradeBtn/UP节点
  16. @property(Label)
  17. currentDamageLabel: Label = null; // Canvas/UpgradePanel/NumberBack/CurrentDamage节点
  18. private blinkComp: BlinkScaleAnimator | null = null; // 闪烁动画组件引用
  19. private weaponsConfig: any = null; // 武器配置数据
  20. /**
  21. * 植物升级成功动画
  22. * 播放植物图标的放大缩小动画,并在升级期间应用扫描效果材质,同时播放升级音效
  23. * @param weaponIconNode 植物图标节点
  24. */
  25. public playWeaponUpgradeAnimation(weaponIconNode: Node): Promise<void> {
  26. return new Promise((resolve) => {
  27. if (!weaponIconNode) {
  28. resolve();
  29. return;
  30. }
  31. // 停止之前的动画,防止快速点击时动画冲突
  32. Tween.stopAllByTarget(weaponIconNode);
  33. // 重置到原始缩放状态,确保动画从正确的状态开始
  34. weaponIconNode.setScale(Vec3.ONE);
  35. // 保存原始缩放值
  36. const originalScale = Vec3.ONE.clone();
  37. let weaponSprite: Sprite = null;
  38. let originalMaterial: Material = null;
  39. console.log('[UpgradeAni] 开始武器升级动画,weaponSpriteNode:', this.weaponSpriteNode);
  40. // 播放植物升级音效
  41. Audio.playUISound('data/弹球音效/equipment level up finish');
  42. // 使用装饰器引用的WeaponSprite节点,获取其Sprite组件并保存原始材质
  43. if (this.weaponSpriteNode) {
  44. console.log('[UpgradeAni] 找到weaponSpriteNode,开始获取Sprite组件');
  45. weaponSprite = this.weaponSpriteNode.getComponent(Sprite);
  46. if (weaponSprite) {
  47. console.log('[UpgradeAni] 找到Sprite组件,开始加载材质');
  48. // 保存原始材质,优先保存customMaterial,如果没有则保存当前material
  49. originalMaterial = weaponSprite.customMaterial || weaponSprite.material;
  50. console.log('[UpgradeAni] 原始材质:', originalMaterial);
  51. // 加载并应用扫描效果材质
  52. resources.load('shaders/scan-effect', Material, (err, scanMaterial) => {
  53. console.log('[UpgradeAni] 材质加载回调,err:', err, 'scanMaterial:', scanMaterial);
  54. if (!err && scanMaterial && weaponSprite) {
  55. weaponSprite.material = scanMaterial;
  56. console.log('[UpgradeAni] 应用扫描效果材质成功');
  57. } else {
  58. console.warn('[UpgradeAni] 加载扫描效果材质失败:', err);
  59. }
  60. });
  61. } else {
  62. console.warn('[UpgradeAni] weaponSpriteNode上没有找到Sprite组件');
  63. }
  64. } else {
  65. console.warn('[UpgradeAni] weaponSpriteNode为null,请在编辑器中设置');
  66. }
  67. // 使用代码驱动的缩放动画:放大到1.5倍再缩回原始大小
  68. const upDuration = 0.25;
  69. const downDuration = 0.25;
  70. const scaleFactor = 1.5;
  71. const easingUp = 'sineOut';
  72. const easingDown = 'sineIn';
  73. const targetScale = new Vec3(
  74. originalScale.x * scaleFactor,
  75. originalScale.y * scaleFactor,
  76. originalScale.z
  77. );
  78. tween(weaponIconNode)
  79. .to(upDuration, { scale: targetScale }, { easing: easingUp })
  80. .to(downDuration, { scale: originalScale }, { easing: easingDown })
  81. .call(() => {
  82. // 动画结束后恢复原始材质
  83. if (weaponSprite && originalMaterial) {
  84. if (weaponSprite.customMaterial === originalMaterial) {
  85. weaponSprite.material = weaponSprite.customMaterial;
  86. } else {
  87. weaponSprite.material = originalMaterial;
  88. }
  89. console.log('[UpgradeAni] 恢复原始材质成功');
  90. } else if (weaponSprite) {
  91. if (weaponSprite.customMaterial) {
  92. weaponSprite.material = weaponSprite.customMaterial;
  93. console.log('[UpgradeAni] 恢复到customMaterial');
  94. } else {
  95. weaponSprite.material = null;
  96. console.log('[UpgradeAni] 恢复到默认材质');
  97. }
  98. }
  99. resolve();
  100. })
  101. .start();
  102. });
  103. }
  104. /**
  105. * 显示升级面板动画
  106. */
  107. public showPanel(): Promise<void> {
  108. return new Promise((resolve) => {
  109. const panel = this.node;
  110. if (!panel) {
  111. resolve();
  112. return;
  113. }
  114. // 确保面板激活
  115. panel.active = true;
  116. // 停止之前的动画
  117. Tween.stopAllByTarget(panel);
  118. // 设置初始状态:缩放为0
  119. panel.setScale(Vec3.ZERO);
  120. // 播放弹出动画
  121. tween(panel)
  122. .to(0.3, { scale: Vec3.ONE }, {
  123. easing: 'backOut'
  124. })
  125. .call(() => {
  126. resolve();
  127. })
  128. .start();
  129. });
  130. }
  131. /**
  132. * 隐藏升级面板动画
  133. */
  134. public hidePanel(): Promise<void> {
  135. return new Promise((resolve) => {
  136. const panel = this.node;
  137. if (!panel) {
  138. resolve();
  139. return;
  140. }
  141. // 停止之前的动画
  142. Tween.stopAllByTarget(panel);
  143. // 播放缩小动画
  144. tween(panel)
  145. .to(0.2, { scale: Vec3.ZERO }, {
  146. easing: 'backIn'
  147. })
  148. .call(() => {
  149. // 动画结束后隐藏面板
  150. panel.active = false;
  151. resolve();
  152. })
  153. .start();
  154. });
  155. }
  156. /**
  157. * 立即隐藏面板(无动画)
  158. */
  159. public hidePanelImmediate(): void {
  160. const panel = this.node;
  161. if (panel) {
  162. // 停止所有动画
  163. Tween.stopAllByTarget(panel);
  164. // 立即隐藏
  165. panel.active = false;
  166. panel.setScale(Vec3.ONE);
  167. }
  168. }
  169. /**
  170. * 开始升级按钮闪烁动画
  171. * 当钞票足够升级时调用此方法
  172. */
  173. public startUpgradeBtnBlink(): void {
  174. if (!this.upgradeBtnNode) {
  175. console.warn('[UpgradeAni] upgradeBtnNode为null,请在编辑器中设置Canvas/UpgradePanel/UpgradeBtn/UP节点');
  176. return;
  177. }
  178. // 停止之前的闪烁动画
  179. this.stopUpgradeBtnBlink();
  180. // 重置到原始缩放状态
  181. this.upgradeBtnNode.setScale(Vec3.ONE);
  182. // 使用预挂载通用组件进行循环闪烁(放大缩小)
  183. const comp = this.upgradeBtnNode.getComponent(BlinkScaleAnimator);
  184. if (!comp) {
  185. console.warn('[UpgradeAni] 未挂载 BlinkScaleAnimator 组件,请在编辑器中为 upgradeBtnNode 预先添加该组件(默认禁用)。');
  186. } else {
  187. comp.scaleFactor = 1.5;
  188. comp.upDuration = 0.5;
  189. comp.downDuration = 0.5;
  190. comp.easingUp = 'sineInOut';
  191. comp.easingDown = 'sineInOut';
  192. comp.playOnEnable = false;
  193. comp.play();
  194. this.blinkComp = comp;
  195. }
  196. console.log('[UpgradeAni] 开始升级按钮闪烁动画');
  197. }
  198. /**
  199. * 停止升级按钮闪烁动画
  200. * 当钞票不够升级时调用此方法
  201. */
  202. public stopUpgradeBtnBlink(): void {
  203. if (this.blinkComp) {
  204. this.blinkComp.stop();
  205. this.blinkComp = null;
  206. }
  207. if (this.upgradeBtnNode) {
  208. Tween.stopAllByTarget(this.upgradeBtnNode);
  209. this.upgradeBtnNode.setScale(Vec3.ONE);
  210. }
  211. console.log('[UpgradeAni] 停止升级按钮闪烁动画');
  212. }
  213. /**
  214. * 检查升级按钮是否正在闪烁
  215. * @returns 是否正在闪烁
  216. */
  217. public isUpgradeBtnBlinking(): boolean {
  218. return this.blinkComp !== null;
  219. }
  220. /**
  221. * 加载武器配置数据
  222. */
  223. public async loadWeaponsConfig(): Promise<void> {
  224. try {
  225. this.weaponsConfig = await JsonConfigLoader.getInstance().loadConfig('weapons');
  226. console.log('[UpgradeAni] 武器配置加载成功:', this.weaponsConfig);
  227. } catch (error) {
  228. console.error('[UpgradeAni] 加载武器配置失败:', error);
  229. throw error;
  230. }
  231. }
  232. /**
  233. * 从JSON配置中获取武器在指定等级的伤害值
  234. * @param weaponId 武器ID
  235. * @param level 武器等级
  236. * @returns 伤害值
  237. */
  238. public getWeaponDamageFromConfig(weaponId: string, level: number): number {
  239. if (!this.weaponsConfig || !this.weaponsConfig.weapons) {
  240. console.warn('[UpgradeAni] 武器配置未加载');
  241. return 0;
  242. }
  243. const weaponConfig = this.weaponsConfig.weapons.find((w: any) => w.id === weaponId);
  244. if (!weaponConfig) {
  245. console.warn(`[UpgradeAni] 未找到武器配置: ${weaponId}`);
  246. return 0;
  247. }
  248. if (level === 0) {
  249. return 0; // 未解锁武器伤害为0
  250. }
  251. // 优先从upgradeConfig中获取伤害值
  252. if (weaponConfig.upgradeConfig && weaponConfig.upgradeConfig.levels) {
  253. const levelConfig = weaponConfig.upgradeConfig.levels[level.toString()];
  254. if (levelConfig && typeof levelConfig.damage === 'number') {
  255. return levelConfig.damage;
  256. }
  257. }
  258. // 如果upgradeConfig中没有伤害值,使用基础伤害 + 等级加成
  259. const baseDamage = weaponConfig.stats?.damage || 0;
  260. return baseDamage + (level - 1);
  261. }
  262. /**
  263. * 更新当前伤害显示
  264. * @param weaponId 武器ID
  265. * @param level 武器等级
  266. */
  267. public updateCurrentDamageDisplay(weaponId: string, level: number): void {
  268. if (!this.currentDamageLabel) {
  269. console.warn('[UpgradeAni] currentDamageLabel未设置,请在编辑器中设置Canvas/UpgradePanel/NumberBack/CurrentDamage节点');
  270. return;
  271. }
  272. const damage = this.getWeaponDamageFromConfig(weaponId, level);
  273. this.currentDamageLabel.string = damage.toString();
  274. console.log(`[UpgradeAni] 更新伤害显示: ${weaponId} 等级${level} 伤害${damage}`);
  275. }
  276. }