CharacterManager.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import { _decorator, Component, Node, Button, Vec3, tween, Tween, resources, sp } from 'cc';
  2. import { DialogueManager } from './DialogueManager';
  3. const { ccclass, property } = _decorator;
  4. @ccclass('CharacterManager')
  5. export class CharacterManager extends Component {
  6. @property({
  7. type: DialogueManager,
  8. tooltip: '对话框管理器'
  9. })
  10. dialogueManager: DialogueManager = null;
  11. @property({
  12. type: Node,
  13. tooltip: '人物节点'
  14. })
  15. characterNode: Node = null;
  16. @property({
  17. type: Button,
  18. tooltip: '放行按钮(向右移动)'
  19. })
  20. letPassButton: Button = null;
  21. @property({
  22. type: Button,
  23. tooltip: '赶走按钮(向左移动)'
  24. })
  25. dismissButton: Button = null;
  26. @property({
  27. tooltip: '人物移动动画持续时间(秒)'
  28. })
  29. moveDuration: number = 1.0;
  30. @property({
  31. tooltip: '人物移动距离(像素)'
  32. })
  33. moveDistance: number = 300;
  34. private initialPosition: Vec3 = null;
  35. private currentAnimation: Tween<Node> = null;
  36. start() {
  37. // 记录人物初始位置
  38. if (this.characterNode) {
  39. this.initialPosition = this.characterNode.position.clone();
  40. this.initialPosition.x += 400; // 初始位置的x坐标增加400
  41. }
  42. // 设置按钮事件监听
  43. if (this.letPassButton) {
  44. this.letPassButton.node.on(Button.EventType.CLICK, this.moveCharacterRight, this);
  45. }
  46. if (this.dismissButton) {
  47. this.dismissButton.node.on(Button.EventType.CLICK, this.moveCharacterLeft, this);
  48. }
  49. }
  50. /**
  51. * 设置角色外观
  52. * @param characterId 角色ID
  53. * @param skinName 皮肤名称
  54. * @param onComplete 外观设置完成后的回调函数
  55. */
  56. public setupCharacterAppearance(characterId: number, skinName: string, onComplete?: () => void): void {
  57. if (!this.characterNode) {
  58. console.error('人物节点未设置');
  59. return;
  60. }
  61. // 构建角色资源路径(左边补0确保两位数)
  62. const characterFolderName = characterId < 10 ? `0${characterId}` : `${characterId}`;
  63. const characterPath = `${characterFolderName}`;
  64. // 加载角色骨骼动画资源
  65. resources.load(`${characterPath}/${characterId}`, sp.SkeletonData, (err, skeletonData) => {
  66. if (err) {
  67. console.error(`加载角色资源失败: ${characterPath}/${characterId}`, err);
  68. return;
  69. }
  70. // 获取骨骼动画组件
  71. const skeletonComponent = this.characterNode.getComponent(sp.Skeleton);
  72. if (skeletonComponent) {
  73. // 设置骨骼数据
  74. skeletonComponent.skeletonData = skeletonData;
  75. skeletonComponent.setSkin(skinName);
  76. skeletonComponent.setAnimation(0, 'loop', true);
  77. console.log(`设置角色: ${characterId}, 皮肤: ${skinName}`);
  78. // 确保回调是一个函数再执行
  79. if (onComplete && typeof onComplete === 'function') {
  80. try {
  81. onComplete();
  82. } catch (error) {
  83. console.error('执行外观设置回调时出错:', error);
  84. }
  85. }
  86. } else {
  87. console.error('人物节点上没有sp.Skeleton组件');
  88. }
  89. });
  90. }
  91. /**
  92. * 人物移动到右侧(放行)
  93. * @param onComplete 移动完成后的回调函数
  94. */
  95. public moveCharacterRight(onComplete?: () => void): void {
  96. if (!this.characterNode || !this.initialPosition) return;
  97. // 隐藏对话框
  98. if (this.dialogueManager) {
  99. this.dialogueManager.hideDialogue();
  100. }
  101. // 停止当前动画
  102. if (this.currentAnimation) {
  103. this.currentAnimation.stop();
  104. }
  105. // 目标位置:向右移动
  106. const targetPos = new Vec3(
  107. this.initialPosition.x + this.moveDistance,
  108. this.initialPosition.y,
  109. this.initialPosition.z
  110. );
  111. // 创建动画
  112. this.currentAnimation = tween(this.characterNode)
  113. .to(this.moveDuration, { position: targetPos }, { easing: 'cubicOut' })
  114. .call(() => {
  115. // 动画完成回调
  116. this.currentAnimation = null;
  117. // 确保回调是一个函数再执行
  118. if (onComplete && typeof onComplete === 'function') {
  119. try {
  120. onComplete();
  121. } catch (error) {
  122. console.error('执行向右移动回调时出错:', error);
  123. }
  124. }
  125. })
  126. .start();
  127. }
  128. /**
  129. * 人物移动到左侧(赶走)
  130. * @param onComplete 移动完成后的回调函数
  131. */
  132. public moveCharacterLeft(onComplete?: () => void): void {
  133. if (!this.characterNode || !this.initialPosition) return;
  134. // 隐藏对话框
  135. if (this.dialogueManager) {
  136. this.dialogueManager.hideDialogue();
  137. }
  138. // 停止当前动画
  139. if (this.currentAnimation) {
  140. this.currentAnimation.stop();
  141. }
  142. // 目标位置:向左移动
  143. const targetPos = new Vec3(
  144. this.initialPosition.x - this.moveDistance,
  145. this.initialPosition.y,
  146. this.initialPosition.z
  147. );
  148. // 创建动画
  149. this.currentAnimation = tween(this.characterNode)
  150. .to(this.moveDuration, { position: targetPos }, { easing: 'cubicOut' })
  151. .call(() => {
  152. // 动画完成回调
  153. this.currentAnimation = null;
  154. // 确保回调是一个函数再执行
  155. if (onComplete && typeof onComplete === 'function') {
  156. try {
  157. onComplete();
  158. } catch (error) {
  159. console.error('执行向左移动回调时出错:', error);
  160. }
  161. }
  162. })
  163. .start();
  164. }
  165. /**
  166. * 新人物从左向右进入
  167. * @param onComplete 移动完成后的回调函数
  168. */
  169. public characterEnter(onComplete?: () => void): void {
  170. console.log('characterEnter');
  171. if (!this.characterNode) return;
  172. // 隐藏对话框
  173. if (this.dialogueManager) {
  174. this.dialogueManager.hideDialogue();
  175. }
  176. // 停止当前动画
  177. if (this.currentAnimation) {
  178. this.currentAnimation.stop();
  179. }
  180. // 设置起始位置(在左侧)
  181. const startPos = new Vec3(
  182. this.initialPosition.x - this.moveDistance,
  183. this.initialPosition.y,
  184. this.initialPosition.z
  185. );
  186. this.characterNode.position = startPos;
  187. // 创建动画,移动到初始位置
  188. this.currentAnimation = tween(this.characterNode)
  189. .to(this.moveDuration, { position: this.initialPosition }, { easing: 'cubicOut' })
  190. .call(() => {
  191. // 动画完成回调
  192. this.currentAnimation = null;
  193. // 确保回调是一个函数再执行
  194. if (onComplete && typeof onComplete === 'function') {
  195. try {
  196. onComplete();
  197. } catch (error) {
  198. console.error('执行角色进入回调时出错:', error);
  199. }
  200. }
  201. })
  202. .start();
  203. }
  204. /**
  205. * 重置人物位置到初始位置
  206. */
  207. public resetCharacterPosition(): void {
  208. if (this.characterNode && this.initialPosition) {
  209. // 停止当前动画
  210. if (this.currentAnimation) {
  211. this.currentAnimation.stop();
  212. this.currentAnimation = null;
  213. }
  214. // 直接设置到初始位置
  215. this.characterNode.position = this.initialPosition.clone();
  216. }
  217. }
  218. onDestroy() {
  219. // 移除按钮事件监听
  220. if (this.letPassButton) {
  221. this.letPassButton.node.off(Button.EventType.CLICK, this.moveCharacterRight, this);
  222. }
  223. if (this.dismissButton) {
  224. this.dismissButton.node.off(Button.EventType.CLICK, this.moveCharacterLeft, this);
  225. }
  226. }
  227. }