PersonalInfoManager.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import { _decorator, Component, Node, Button, Sprite, Label, SpriteFrame, resources, tween, Vec3, UIOpacity } from 'cc';
  2. const { ccclass, property } = _decorator;
  3. @ccclass('PersonalInfoManager')
  4. export class PersonalInfoManager extends Component {
  5. @property({
  6. type: Node,
  7. tooltip: '个人资料UI面板'
  8. })
  9. personalInfoPanel: Node = null;
  10. @property({
  11. type: Button,
  12. tooltip: '关闭按钮'
  13. })
  14. closeButton: Button = null;
  15. @property({
  16. type: Sprite,
  17. tooltip: '角色头像显示'
  18. })
  19. characterAvatar: Sprite = null;
  20. @property({
  21. type: Label,
  22. tooltip: '角色信息文本'
  23. })
  24. infoText: Label = null;
  25. @property({
  26. tooltip: '动画时间(秒)',
  27. range: [0.1, 2.0, 0.1]
  28. })
  29. animDuration: number = 0.5;
  30. // 当前显示的角色ID
  31. private currentCharacterId: number = -1;
  32. private panelOpacity: UIOpacity = null;
  33. start() {
  34. // 初始化隐藏面板
  35. if (this.personalInfoPanel) {
  36. this.personalInfoPanel.active = false;
  37. // 确保面板有UIOpacity组件
  38. this.panelOpacity = this.personalInfoPanel.getComponent(UIOpacity);
  39. if (!this.panelOpacity) {
  40. this.panelOpacity = this.personalInfoPanel.addComponent(UIOpacity);
  41. }
  42. }
  43. // 注册关闭按钮事件
  44. this.setupCloseButton();
  45. }
  46. private setupCloseButton() {
  47. if (this.closeButton) {
  48. this.closeButton.node.off('click');
  49. this.closeButton.node.on('click', () => {
  50. this.hidePersonalInfoPanel();
  51. }, this);
  52. }
  53. }
  54. /**
  55. * 显示个人资料面板
  56. */
  57. public showPersonalInfoPanel() {
  58. if (this.personalInfoPanel) {
  59. this.personalInfoPanel.active = true;
  60. this.personalInfoPanel.setSiblingIndex(999);
  61. // 播放从口袋拿出来的动画
  62. this.playPocketOutAnimation();
  63. }
  64. }
  65. /**
  66. * 播放从口袋掏出的动画
  67. */
  68. private playPocketOutAnimation() {
  69. // 设置初始状态 - 从右侧口袋拿出
  70. this.personalInfoPanel.setScale(new Vec3(0.7, 0.2, 1)); // 扁平状态
  71. this.personalInfoPanel.setPosition(new Vec3(200, -180, 0)); // 从右侧口袋位置开始
  72. this.personalInfoPanel.setRotationFromEuler(new Vec3(0, 0, -20)); // 初始右倾斜角度
  73. if (this.panelOpacity) {
  74. this.panelOpacity.opacity = 50; // 半透明开始
  75. }
  76. // 创建动画 - 掏出口袋的感觉
  77. tween(this.personalInfoPanel)
  78. // 先上移一点,同时展开
  79. .to(this.animDuration * 0.6, {
  80. scale: new Vec3(0.9, 0.9, 1),
  81. position: new Vec3(100, -50, 0),
  82. eulerAngles: new Vec3(0, 0, -10) // 轻微倾斜,像是手拿着
  83. }, {
  84. easing: 'quadOut'
  85. })
  86. // 然后放到正确位置并恢复正常大小
  87. .to(this.animDuration * 0.4, {
  88. scale: new Vec3(1, 1, 1),
  89. position: new Vec3(0, 0, 0),
  90. eulerAngles: new Vec3(0, 0, 0)
  91. }, {
  92. easing: 'backOut'
  93. })
  94. .start();
  95. // 透明度动画
  96. if (this.panelOpacity) {
  97. tween(this.panelOpacity)
  98. .to(this.animDuration * 0.7, { opacity: 255 })
  99. .start();
  100. }
  101. }
  102. /**
  103. * 隐藏个人资料面板
  104. */
  105. public hidePersonalInfoPanel() {
  106. if (this.personalInfoPanel) {
  107. // 播放放回口袋的动画
  108. this.playPocketInAnimation(() => {
  109. this.personalInfoPanel.active = false;
  110. });
  111. }
  112. }
  113. /**
  114. * 播放放回口袋的动画
  115. */
  116. private playPocketInAnimation(callback?: Function) {
  117. // 创建放回口袋的动画
  118. tween(this.personalInfoPanel)
  119. // 先抬起并旋转
  120. .to(this.animDuration * 0.3, {
  121. position: new Vec3(80, -30, 0),
  122. eulerAngles: new Vec3(0, 0, -10),
  123. scale: new Vec3(0.95, 0.95, 1)
  124. }, {
  125. easing: 'sineIn'
  126. })
  127. // 然后放入口袋
  128. .to(this.animDuration * 0.4, {
  129. position: new Vec3(200, -180, 0),
  130. eulerAngles: new Vec3(0, 0, -20),
  131. scale: new Vec3(0.6, 0.2, 1) // 扁平化,像塞入口袋
  132. }, {
  133. easing: 'quadIn'
  134. })
  135. .call(() => {
  136. if (callback) callback();
  137. })
  138. .start();
  139. // 透明度动画
  140. if (this.panelOpacity) {
  141. tween(this.panelOpacity)
  142. .to(this.animDuration * 0.6, { opacity: 0 })
  143. .start();
  144. }
  145. }
  146. /**
  147. * 显示角色资料
  148. * @param data 角色资料数据
  149. */
  150. public displayCharacterInfo(data: any) {
  151. if (!data) {
  152. console.error('角色资料数据为空');
  153. return;
  154. }
  155. // 保存当前显示的角色ID
  156. this.currentCharacterId = data.characterId;
  157. // 设置文本信息
  158. if (this.infoText && data.info) {
  159. this.infoText.string = data.info;
  160. }
  161. // 加载并设置头像
  162. if (this.characterAvatar && data.avatarPath) {
  163. resources.load(data.avatarPath, SpriteFrame, (err, spriteFrame) => {
  164. if (err) {
  165. console.error(`加载头像失败: ${data.avatarPath}`, err);
  166. return;
  167. }
  168. this.characterAvatar.spriteFrame = spriteFrame;
  169. });
  170. }
  171. // 显示面板
  172. this.showPersonalInfoPanel();
  173. }
  174. onDestroy() {
  175. if (this.closeButton) {
  176. this.closeButton.node.off('click');
  177. }
  178. }
  179. }