PersonalInfoManager.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import { _decorator, Component, Node, Label, Sprite, Button, resources, SpriteFrame, tween, Vec3, UIOpacity, UITransform, Color } from 'cc';
  2. import { DataManager } from './DataManager';
  3. const { ccclass, property } = _decorator;
  4. @ccclass('PersonalInfoManager')
  5. export class PersonalInfoManager extends Component {
  6. @property({
  7. type: Node,
  8. tooltip: '个人资料UI面板'
  9. })
  10. personalInfoPanel: Node = null;
  11. @property({
  12. type: Button,
  13. tooltip: '关闭按钮'
  14. })
  15. closeButton: Button = null;
  16. @property({
  17. type: Sprite,
  18. tooltip: '角色头像显示'
  19. })
  20. characterAvatar: Sprite = null;
  21. @property({
  22. type: Label,
  23. tooltip: '角色信息文本'
  24. })
  25. infoText: Label = null;
  26. @property({
  27. type: Label,
  28. tooltip: '角色姓名、房间号、ID等信息标签'
  29. })
  30. nameInfoLabel: Label = null;
  31. @property({
  32. type: DataManager,
  33. tooltip: '数据管理器引用'
  34. })
  35. dataManager: DataManager = null;
  36. @property({
  37. tooltip: '动画时间(秒)',
  38. range: [0.1, 2.0, 0.1]
  39. })
  40. animDuration: number = 0.5;
  41. // 当前显示的角色ID
  42. private currentCharacterId: number = -1;
  43. private panelOpacity: UIOpacity = null;
  44. start() {
  45. // 初始化隐藏面板
  46. if (this.personalInfoPanel) {
  47. this.personalInfoPanel.active = false;
  48. // 确保面板有UIOpacity组件
  49. this.panelOpacity = this.personalInfoPanel.getComponent(UIOpacity);
  50. if (!this.panelOpacity) {
  51. this.panelOpacity = this.personalInfoPanel.addComponent(UIOpacity);
  52. }
  53. }
  54. // 注册关闭按钮事件
  55. this.setupCloseButton();
  56. }
  57. private setupCloseButton() {
  58. if (this.closeButton) {
  59. this.closeButton.node.off('click');
  60. this.closeButton.node.on('click', () => {
  61. this.hidePersonalInfoPanel();
  62. }, this);
  63. }
  64. }
  65. /**
  66. * 显示个人资料面板
  67. */
  68. public showPersonalInfoPanel() {
  69. if (this.personalInfoPanel) {
  70. this.personalInfoPanel.active = true;
  71. this.personalInfoPanel.setSiblingIndex(999);
  72. // 播放从口袋拿出来的动画
  73. this.playPocketOutAnimation();
  74. }
  75. }
  76. /**
  77. * 播放从口袋掏出的动画
  78. */
  79. private playPocketOutAnimation() {
  80. // 设置初始状态 - 从右侧口袋拿出
  81. this.personalInfoPanel.setScale(new Vec3(0.7, 0.2, 1)); // 扁平状态
  82. this.personalInfoPanel.setPosition(new Vec3(200, -180, 0)); // 从右侧口袋位置开始
  83. this.personalInfoPanel.setRotationFromEuler(new Vec3(0, 0, -20)); // 初始右倾斜角度
  84. if (this.panelOpacity) {
  85. this.panelOpacity.opacity = 50; // 半透明开始
  86. }
  87. // 创建动画 - 掏出口袋的感觉
  88. tween(this.personalInfoPanel)
  89. // 先上移一点,同时展开
  90. .to(this.animDuration * 0.6, {
  91. scale: new Vec3(0.9, 0.9, 1),
  92. position: new Vec3(100, -50, 0),
  93. eulerAngles: new Vec3(0, 0, -10) // 轻微倾斜,像是手拿着
  94. }, {
  95. easing: 'quadOut'
  96. })
  97. // 然后放到正确位置并恢复正常大小
  98. .to(this.animDuration * 0.4, {
  99. scale: new Vec3(1, 1, 1),
  100. position: new Vec3(0, 0, 0),
  101. eulerAngles: new Vec3(0, 0, 0)
  102. }, {
  103. easing: 'backOut'
  104. })
  105. .start();
  106. // 透明度动画
  107. if (this.panelOpacity) {
  108. tween(this.panelOpacity)
  109. .to(this.animDuration * 0.7, { opacity: 255 })
  110. .start();
  111. }
  112. }
  113. /**
  114. * 隐藏个人资料面板
  115. */
  116. public hidePersonalInfoPanel() {
  117. if (this.personalInfoPanel) {
  118. // 播放放回口袋的动画
  119. this.playPocketInAnimation(() => {
  120. this.personalInfoPanel.active = false;
  121. });
  122. }
  123. }
  124. /**
  125. * 播放放回口袋的动画
  126. */
  127. private playPocketInAnimation(callback?: Function) {
  128. // 创建放回口袋的动画
  129. tween(this.personalInfoPanel)
  130. // 先抬起并旋转
  131. .to(this.animDuration * 0.3, {
  132. position: new Vec3(80, -30, 0),
  133. eulerAngles: new Vec3(0, 0, -10),
  134. scale: new Vec3(0.95, 0.95, 1)
  135. }, {
  136. easing: 'sineIn'
  137. })
  138. // 然后放入口袋
  139. .to(this.animDuration * 0.4, {
  140. position: new Vec3(200, -180, 0),
  141. eulerAngles: new Vec3(0, 0, -20),
  142. scale: new Vec3(0.6, 0.2, 1) // 扁平化,像塞入口袋
  143. }, {
  144. easing: 'quadIn'
  145. })
  146. .call(() => {
  147. if (callback) callback();
  148. })
  149. .start();
  150. // 透明度动画
  151. if (this.panelOpacity) {
  152. tween(this.panelOpacity)
  153. .to(this.animDuration * 0.6, { opacity: 0 })
  154. .start();
  155. }
  156. }
  157. /**
  158. * 显示角色资料
  159. * @param data 角色资料数据
  160. */
  161. public displayCharacterInfo(data: any) {
  162. if (!data) {
  163. console.error('角色资料数据为空');
  164. return;
  165. }
  166. console.log(`PersonalInfoManager.displayCharacterInfo: ID=${data.characterId}, 头像路径=${data.avatarPath}`);
  167. // 保存当前显示的角色ID
  168. this.currentCharacterId = data.characterId;
  169. // 设置文本信息
  170. if (this.infoText && data.info) {
  171. this.infoText.string = data.info;
  172. }
  173. // 加载并设置头像
  174. if (this.characterAvatar && data.avatarPath) {
  175. const characterId = data.characterId;
  176. // 列出所有可能尝试的路径格式
  177. const pathsToTry = [
  178. `${data.avatarPath}/spriteFrame`,
  179. data.avatarPath,
  180. `avatars/${characterId}/avatar_${characterId}_5/spriteFrame`,
  181. `avatars/${characterId}/avatar_${characterId}_5`,
  182. `avatars/${characterId}/spriteFrame`,
  183. `avatars/${characterId}`
  184. ];
  185. // 递归尝试加载所有可能的路径
  186. this.tryLoadSpriteFrameWithPaths(this.characterAvatar, pathsToTry, 0);
  187. }
  188. // 获取更详细的NPC信息并显示
  189. this.updateDetailedNPCInfo(data.characterId);
  190. // 显示面板
  191. this.showPersonalInfoPanel();
  192. }
  193. /**
  194. * 递归尝试加载所有可能的路径
  195. * @param sprite 要设置的精灵组件
  196. * @param paths 尝试的路径数组
  197. * @param index 当前尝试的路径索引
  198. */
  199. private tryLoadSpriteFrameWithPaths(sprite: Sprite, paths: string[], index: number): void {
  200. if (index >= paths.length) {
  201. console.error('所有可能的路径都尝试失败,使用默认图像');
  202. // 所有路径都失败,创建一个默认的灰色方形作为备用
  203. this.createDefaultSpriteFrame(sprite);
  204. return;
  205. }
  206. const currentPath = paths[index];
  207. console.log(`PersonalInfoManager - 尝试加载路径(${index+1}/${paths.length}): ${currentPath}`);
  208. resources.load(currentPath, SpriteFrame, (err, spriteFrame) => {
  209. if (err) {
  210. console.warn(`路径加载失败: ${currentPath}`, err);
  211. // 尝试下一个路径
  212. this.tryLoadSpriteFrameWithPaths(sprite, paths, index + 1);
  213. return;
  214. }
  215. // 成功加载,设置spriteFrame
  216. sprite.spriteFrame = spriteFrame;
  217. console.log(`成功加载头像: ${currentPath}`);
  218. });
  219. }
  220. /**
  221. * 创建一个默认的SpriteFrame用于显示
  222. * @param sprite 要设置的精灵组件
  223. */
  224. private createDefaultSpriteFrame(sprite: Sprite): void {
  225. console.log('创建默认图像');
  226. // 使用内置资源或默认资源
  227. const defaultIcon = "default_sprite";
  228. resources.load(defaultIcon, SpriteFrame, (err, spriteFrame) => {
  229. if (err) {
  230. console.warn(`无法加载默认图像 ${defaultIcon}`, err);
  231. return;
  232. }
  233. sprite.spriteFrame = spriteFrame;
  234. console.log('已设置默认图像');
  235. });
  236. }
  237. /**
  238. * 更新详细的NPC信息
  239. * @param characterId 角色ID
  240. */
  241. private updateDetailedNPCInfo(characterId: number) {
  242. if (!this.nameInfoLabel) return;
  243. if (!this.dataManager) {
  244. console.error('未设置DataManager引用');
  245. return;
  246. }
  247. // 获取NPC数据
  248. const npcData = this.dataManager.getNPCById(characterId);
  249. if (!npcData) {
  250. console.error(`无法找到ID为${characterId}的NPC数据`);
  251. return;
  252. }
  253. // 获取通行证信息
  254. const passInfo = npcData.pass;
  255. // 获取个人信息
  256. const personalInfo = npcData.personal;
  257. // 构建详细信息文本
  258. let detailedInfo = '';
  259. // 添加姓名
  260. detailedInfo += `姓名: ${npcData.characterName}\n`;
  261. // 添加房间号
  262. if (passInfo && passInfo.room) {
  263. detailedInfo += `房间号: ${passInfo.room}\n`;
  264. }
  265. // 添加ID
  266. if (passInfo && passInfo.identityId) {
  267. detailedInfo += `ID: ${passInfo.identityId}\n`;
  268. } else if (personalInfo && personalInfo.id) {
  269. detailedInfo += `ID: ${personalInfo.id}\n`;
  270. }
  271. // 添加同住人信息
  272. if (personalInfo && personalInfo.hasRoommate && personalInfo.roommate) {
  273. detailedInfo += `同住人: ${personalInfo.roommate.id || '未知'}\n`;
  274. } else if (personalInfo && personalInfo.hasRoommate) {
  275. detailedInfo += `同住人: 有(未详)\n`;
  276. } else if (personalInfo) {
  277. detailedInfo += `同住人: 无\n`;
  278. }
  279. // 设置详细信息文本
  280. this.nameInfoLabel.string = detailedInfo;
  281. }
  282. onDestroy() {
  283. if (this.closeButton) {
  284. this.closeButton.node.off('click');
  285. }
  286. }
  287. }