ui.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { _decorator, Component, director, error, instantiate, Node, Prefab, UITransform, Widget, assetManager, AssetManager, tween, v3, UIOpacity, Vec3, ProgressBar, Sprite, Tween, sp } from "cc";
  2. const { ccclass, property } = _decorator;
  3. export enum GameUILayers {
  4. GAME,
  5. JOY_STICK,
  6. HUD,
  7. POPUP,
  8. ALERT,
  9. NOTICE,
  10. LOADING,
  11. OVERLAY
  12. }
  13. /**ui管理模块*/
  14. class ui {
  15. private static _instance: ui;
  16. public static getInstance(): ui {
  17. if (!this._instance) this._instance = new ui();
  18. return this._instance;
  19. }
  20. public delay(ms: number): Promise<void> {
  21. return new Promise(resolve => setTimeout(resolve, ms));
  22. }
  23. public delay_second(s: number): Promise<void> {
  24. return new Promise(resolve => setTimeout(resolve, s * 1000));
  25. }
  26. public progressBar_anim(bar: ProgressBar, progress: number, duration: number = 0.2): Promise<void> {
  27. if (bar.progress >= progress) {
  28. bar.progress = progress;
  29. return;
  30. } else {
  31. return new Promise(resolve => tween(bar).to(duration, { progress: progress }).call(() => { resolve() }).start());
  32. }
  33. }
  34. public fillRange_anim(sp: Sprite, progress: number, duration: number = 0.2, limt: -1 | 0 | 1 = 0): Promise<void> {
  35. if ((limt > 0 && sp.fillRange >= progress) && (limt < 0 && sp.fillRange <= progress)) {
  36. sp.fillRange = progress;
  37. return;
  38. } else {
  39. return new Promise(resolve => tween(sp).to(duration, { fillRange: progress }).call(() => { resolve() }).start());
  40. }
  41. }
  42. public scale_anim(node: Node, duration: number = 0.2, start: number = 0.5, end: number = 1): Promise<void> {
  43. node.setScale(start, start, 1);
  44. return new Promise(resolve => tween(node).to(duration, { scale: v3(end, end, 1) }).call(() => { resolve() }).start());
  45. }
  46. public scale_elasticOut_anim(node: Node, duration: number = 0.2, start: number = 0.5, end: number = 1): Promise<void> {
  47. node.setScale(start, start, 1);
  48. return new Promise(resolve => tween(node).to(duration, { scale: v3(end, end, 1) }, { easing: 'elasticOut' }).call(() => { resolve() }).start());
  49. }
  50. public fade_anim(node: Node, duration: number = 0.2, startOpacity: number = 0, targetOpacity: number = 255): Promise<void> {
  51. const opacity = node.getComponent(UIOpacity) ?? node.addComponent(UIOpacity);
  52. opacity.opacity = startOpacity;
  53. return new Promise((resolve) => { tween(opacity).to(duration, { opacity: targetOpacity }).call(() => { resolve() }).start(); });
  54. }
  55. public shake_anim(node: Node, duration: number = 0.2, intensity: number = 5, shakeCount: number = 7): Promise<void> {
  56. return new Promise((resolve) => {
  57. const originalPosition = node.position.clone(); // 保存原始位置
  58. const shakeDuration = duration / (shakeCount * 2); // 每次抖动的持续时间
  59. const shakeTween = tween(node);
  60. for (let i = 0; i < shakeCount; i++) {
  61. const offsetX = (i % 2 === 0 ? intensity : -intensity); // 交替偏移
  62. shakeTween.to(shakeDuration, { position: v3(originalPosition.x + offsetX, originalPosition.y, originalPosition.z) })
  63. .to(shakeDuration, { position: originalPosition }); // 回到原位
  64. }
  65. shakeTween.call(() => { resolve() }).start(); // 开始抖动动画
  66. });
  67. }
  68. public rotate_shake_anim(node: Node, duration: number = 0.3, intensity: number = 5, shakeCount: number = 5): Promise<void> {
  69. return new Promise((resolve) => {
  70. const originalAngle = node.angle; // 保存原始角度
  71. const shakeDuration = duration / (shakeCount * 2); // 每次晃动的持续时间
  72. const shakeTween = tween(node);
  73. for (let i = 0; i < shakeCount; i++) {
  74. const offsetAngle = (i % 2 === 0 ? intensity : -intensity); // 交替旋转
  75. shakeTween.to(shakeDuration, { angle: originalAngle + offsetAngle })
  76. .to(shakeDuration, { angle: originalAngle }); // 回到原位
  77. }
  78. shakeTween.call(() => { resolve() }).start(); // 开始角度晃动动画
  79. });
  80. }
  81. public scale_shake_anim(node: Node, duration: number = 0.3, intensity: number = 0.1, shakeCount: number = 10, reset: number | null = null): Promise<void> {
  82. if (reset) {
  83. Tween.stopAllByTarget(node);
  84. node.setScale(reset, reset, reset);
  85. }
  86. return new Promise((resolve) => {
  87. const originalScale = node.scale.clone(); // 保存原始缩放
  88. const shakeDuration = duration / (shakeCount * 2); // 每次震动的持续时间
  89. const shakeTween = tween(node);
  90. for (let i = 0; i < shakeCount; i++) {
  91. const offsetScale = (i % 2 === 0 ? intensity : -intensity); // 交替缩放
  92. shakeTween.to(shakeDuration, { scale: v3(originalScale.x + offsetScale, originalScale.y + offsetScale, originalScale.z) })
  93. .to(shakeDuration, { scale: originalScale }); // 回到原位
  94. }
  95. shakeTween.call(() => { resolve() }).start(); // 开始缩放震动动画
  96. });
  97. }
  98. public opacity_shake_anim(node: Node, duration: number = 0.8, intensity: number = 80, shakeCount: number = 5): Promise<void> {
  99. const opacity = node.getComponent(UIOpacity) ?? node.addComponent(UIOpacity);
  100. const originalOpacity = opacity.opacity;
  101. const shakeDuration = duration / (shakeCount * 2);
  102. return new Promise((resolve) => {
  103. const shakeTween = tween(opacity);
  104. for (let i = 0; i < shakeCount; i++) {
  105. const offsetOpacity = (i % 2 === 0 ? intensity : -intensity);
  106. shakeTween.to(shakeDuration, { opacity: Math.min(Math.max(originalOpacity + offsetOpacity, 0), 255) })
  107. .to(shakeDuration, { opacity: originalOpacity });
  108. }
  109. shakeTween.call(() => { resolve() }).start(); // 开始透明度震动动画
  110. });
  111. }
  112. /**向上飘出*/
  113. public move_up_anim(node: Node, height: number = 100, duration: number = 0.5): Promise<void> {
  114. return new Promise((resolve) => {
  115. const opacity = node.getComponent(UIOpacity) ?? node.addComponent(UIOpacity);
  116. tween(opacity).to(duration, { opacity: 0 }).start();
  117. tween(node)
  118. .to(duration, { position: node.position.clone().add(new Vec3(0, height, 0)) })
  119. .call(() => { resolve() }) //完成动画
  120. .start(); //开始动画
  121. });
  122. }
  123. /**处理弹跳动画*/
  124. public bounce_anim(node: Node, height: number = 100, duration: number = 0.5): Promise<void> {
  125. return new Promise((resolve) => {
  126. tween(node)
  127. .to(duration, { position: node.position.clone().add(new Vec3(0, height, 0)) }, { easing: 'bounceOut' }) // 向上弹跳
  128. .to(duration, { position: node.position }, { easing: 'bounceIn' }) // 回到原位
  129. .call(() => { resolve() }) //完成动画
  130. .start(); //开始动画
  131. });
  132. }
  133. /**
  134. * 奖励物飞行动画
  135. * @param node 奖励物节点
  136. * @param startPos 起始爆开点
  137. * @param targetPos 最终目标点
  138. * @param explosionDistance 爆开距离
  139. * @param explosionDuration 爆开时间
  140. * @param stayDuration 停留时间
  141. * @param moveDuration 移动到目标的时间
  142. * @returns
  143. */
  144. public reward_fly_anim(node: Node, startPos: Vec3, targetPos: Vec3, explosionDistance: number = 100, explosionDuration: number = 0.3, stayDuration: number = 0.5, moveDuration: number = 0.3): Promise<void> {
  145. node.setPosition(startPos);
  146. return new Promise((resolve) => {
  147. node.setWorldPosition(startPos.x, startPos.y, startPos.z);
  148. const randomDirection = new Vec3(Math.random() * 2 - 1, Math.random() * 2 - 1, 0).normalize(); //随机方向
  149. const explosionEndPos = startPos.add(randomDirection.multiplyScalar(explosionDistance)); // 爆炸效果的位置
  150. tween(node)
  151. .to(explosionDuration, { worldPosition: explosionEndPos }) // 爆炸动画
  152. .delay(stayDuration) //停留时间
  153. .to(moveDuration, { worldPosition: targetPos }, { easing: 'cubicIn' })
  154. .call(() => { resolve() }).start();
  155. });
  156. }
  157. /**
  158. * spine动画
  159. * @param spine sp.Skeleton组件
  160. * @param play_ani 要播放的动画
  161. * @param next_ani 播放完成后进入的动画可以为空
  162. * @returns
  163. */
  164. public spine_anim(spine: sp.Skeleton, play_ani: string, next_ani?: string): Promise<void> {
  165. return new Promise((resolve) => {
  166. spine.setAnimation(0, play_ani, false);
  167. spine.setCompleteListener(() => {
  168. if (next_ani) spine.setAnimation(0, next_ani, true);
  169. spine.setCompleteListener(null);
  170. resolve();
  171. });
  172. });
  173. }
  174. private _uiCanvas: Node;
  175. private _uiRoot: Node;
  176. private createFullScreenNode() {
  177. let canvas = this._uiCanvas.getComponent(UITransform);
  178. let node = new Node();
  179. node.layer = this._uiCanvas.layer;
  180. let uiTransform = node.addComponent(UITransform);
  181. uiTransform.width = canvas.width;
  182. uiTransform.height = canvas.height;
  183. let widget = node.addComponent(Widget);
  184. widget.isAlignBottom = true;
  185. widget.isAlignTop = true;
  186. widget.isAlignLeft = true;
  187. widget.isAlignRight = true;
  188. widget.left = 0;
  189. widget.right = 0;
  190. widget.top = 0;
  191. widget.bottom = 0;
  192. return node;
  193. }
  194. /**获取层级节点*/
  195. public getLayerNode(layerIndex: number): Node {
  196. return this._uiRoot.children[layerIndex] || this._uiRoot;
  197. }
  198. private loadBundleAsync(bundleName: string): Promise<AssetManager.Bundle> {
  199. return new Promise((resolve, reject) => {
  200. assetManager.loadBundle(bundleName, null, (err, loadedBundle) => {
  201. if (err) {
  202. reject(err);
  203. } else {
  204. resolve(loadedBundle);
  205. }
  206. });
  207. });
  208. }
  209. private loadPrefabAsync(bundle: AssetManager.Bundle, prefabName: string): Promise<Prefab> {
  210. return new Promise((resolve, reject) => {
  211. bundle.load(prefabName, (err, data: Prefab) => {
  212. if (err) {
  213. reject(err);
  214. } else {
  215. resolve(data);
  216. }
  217. });
  218. });
  219. }
  220. }
  221. export const gui = ui.getInstance();