import { _decorator, Component, director, error, instantiate, Node, Prefab, UITransform, Widget, assetManager, AssetManager, tween, v3, UIOpacity, Vec3, ProgressBar, Sprite, Tween, sp } from "cc"; const { ccclass, property } = _decorator; export enum GameUILayers { GAME, JOY_STICK, HUD, POPUP, ALERT, NOTICE, LOADING, OVERLAY } /**ui管理模块*/ class ui { private static _instance: ui; public static getInstance(): ui { if (!this._instance) this._instance = new ui(); return this._instance; } public delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } public delay_second(s: number): Promise { return new Promise(resolve => setTimeout(resolve, s * 1000)); } public progressBar_anim(bar: ProgressBar, progress: number, duration: number = 0.2): Promise { if (bar.progress >= progress) { bar.progress = progress; return; } else { return new Promise(resolve => tween(bar).to(duration, { progress: progress }).call(() => { resolve() }).start()); } } public fillRange_anim(sp: Sprite, progress: number, duration: number = 0.2, limt: -1 | 0 | 1 = 0): Promise { if ((limt > 0 && sp.fillRange >= progress) && (limt < 0 && sp.fillRange <= progress)) { sp.fillRange = progress; return; } else { return new Promise(resolve => tween(sp).to(duration, { fillRange: progress }).call(() => { resolve() }).start()); } } public scale_anim(node: Node, duration: number = 0.2, start: number = 0.5, end: number = 1): Promise { node.setScale(start, start, 1); return new Promise(resolve => tween(node).to(duration, { scale: v3(end, end, 1) }).call(() => { resolve() }).start()); } public scale_elasticOut_anim(node: Node, duration: number = 0.2, start: number = 0.5, end: number = 1): Promise { node.setScale(start, start, 1); return new Promise(resolve => tween(node).to(duration, { scale: v3(end, end, 1) }, { easing: 'elasticOut' }).call(() => { resolve() }).start()); } public fade_anim(node: Node, duration: number = 0.2, startOpacity: number = 0, targetOpacity: number = 255): Promise { const opacity = node.getComponent(UIOpacity) ?? node.addComponent(UIOpacity); opacity.opacity = startOpacity; return new Promise((resolve) => { tween(opacity).to(duration, { opacity: targetOpacity }).call(() => { resolve() }).start(); }); } public shake_anim(node: Node, duration: number = 0.2, intensity: number = 5, shakeCount: number = 7): Promise { return new Promise((resolve) => { const originalPosition = node.position.clone(); // 保存原始位置 const shakeDuration = duration / (shakeCount * 2); // 每次抖动的持续时间 const shakeTween = tween(node); for (let i = 0; i < shakeCount; i++) { const offsetX = (i % 2 === 0 ? intensity : -intensity); // 交替偏移 shakeTween.to(shakeDuration, { position: v3(originalPosition.x + offsetX, originalPosition.y, originalPosition.z) }) .to(shakeDuration, { position: originalPosition }); // 回到原位 } shakeTween.call(() => { resolve() }).start(); // 开始抖动动画 }); } public rotate_shake_anim(node: Node, duration: number = 0.3, intensity: number = 5, shakeCount: number = 5): Promise { return new Promise((resolve) => { const originalAngle = node.angle; // 保存原始角度 const shakeDuration = duration / (shakeCount * 2); // 每次晃动的持续时间 const shakeTween = tween(node); for (let i = 0; i < shakeCount; i++) { const offsetAngle = (i % 2 === 0 ? intensity : -intensity); // 交替旋转 shakeTween.to(shakeDuration, { angle: originalAngle + offsetAngle }) .to(shakeDuration, { angle: originalAngle }); // 回到原位 } shakeTween.call(() => { resolve() }).start(); // 开始角度晃动动画 }); } public scale_shake_anim(node: Node, duration: number = 0.3, intensity: number = 0.1, shakeCount: number = 10, reset: number | null = null): Promise { if (reset) { Tween.stopAllByTarget(node); node.setScale(reset, reset, reset); } return new Promise((resolve) => { const originalScale = node.scale.clone(); // 保存原始缩放 const shakeDuration = duration / (shakeCount * 2); // 每次震动的持续时间 const shakeTween = tween(node); for (let i = 0; i < shakeCount; i++) { const offsetScale = (i % 2 === 0 ? intensity : -intensity); // 交替缩放 shakeTween.to(shakeDuration, { scale: v3(originalScale.x + offsetScale, originalScale.y + offsetScale, originalScale.z) }) .to(shakeDuration, { scale: originalScale }); // 回到原位 } shakeTween.call(() => { resolve() }).start(); // 开始缩放震动动画 }); } public opacity_shake_anim(node: Node, duration: number = 0.8, intensity: number = 80, shakeCount: number = 5): Promise { const opacity = node.getComponent(UIOpacity) ?? node.addComponent(UIOpacity); const originalOpacity = opacity.opacity; const shakeDuration = duration / (shakeCount * 2); return new Promise((resolve) => { const shakeTween = tween(opacity); for (let i = 0; i < shakeCount; i++) { const offsetOpacity = (i % 2 === 0 ? intensity : -intensity); shakeTween.to(shakeDuration, { opacity: Math.min(Math.max(originalOpacity + offsetOpacity, 0), 255) }) .to(shakeDuration, { opacity: originalOpacity }); } shakeTween.call(() => { resolve() }).start(); // 开始透明度震动动画 }); } /**向上飘出*/ public move_up_anim(node: Node, height: number = 100, duration: number = 0.5): Promise { return new Promise((resolve) => { const opacity = node.getComponent(UIOpacity) ?? node.addComponent(UIOpacity); tween(opacity).to(duration, { opacity: 0 }).start(); tween(node) .to(duration, { position: node.position.clone().add(new Vec3(0, height, 0)) }) .call(() => { resolve() }) //完成动画 .start(); //开始动画 }); } /**处理弹跳动画*/ public bounce_anim(node: Node, height: number = 100, duration: number = 0.5): Promise { return new Promise((resolve) => { tween(node) .to(duration, { position: node.position.clone().add(new Vec3(0, height, 0)) }, { easing: 'bounceOut' }) // 向上弹跳 .to(duration, { position: node.position }, { easing: 'bounceIn' }) // 回到原位 .call(() => { resolve() }) //完成动画 .start(); //开始动画 }); } /** * 奖励物飞行动画 * @param node 奖励物节点 * @param startPos 起始爆开点 * @param targetPos 最终目标点 * @param explosionDistance 爆开距离 * @param explosionDuration 爆开时间 * @param stayDuration 停留时间 * @param moveDuration 移动到目标的时间 * @returns */ 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 { node.setPosition(startPos); return new Promise((resolve) => { node.setWorldPosition(startPos.x, startPos.y, startPos.z); const randomDirection = new Vec3(Math.random() * 2 - 1, Math.random() * 2 - 1, 0).normalize(); //随机方向 const explosionEndPos = startPos.add(randomDirection.multiplyScalar(explosionDistance)); // 爆炸效果的位置 tween(node) .to(explosionDuration, { worldPosition: explosionEndPos }) // 爆炸动画 .delay(stayDuration) //停留时间 .to(moveDuration, { worldPosition: targetPos }, { easing: 'cubicIn' }) .call(() => { resolve() }).start(); }); } /** * spine动画 * @param spine sp.Skeleton组件 * @param play_ani 要播放的动画 * @param next_ani 播放完成后进入的动画可以为空 * @returns */ public spine_anim(spine: sp.Skeleton, play_ani: string, next_ani?: string): Promise { return new Promise((resolve) => { spine.setAnimation(0, play_ani, false); spine.setCompleteListener(() => { if (next_ani) spine.setAnimation(0, next_ani, true); spine.setCompleteListener(null); resolve(); }); }); } private _uiCanvas: Node; private _uiRoot: Node; private createFullScreenNode() { let canvas = this._uiCanvas.getComponent(UITransform); let node = new Node(); node.layer = this._uiCanvas.layer; let uiTransform = node.addComponent(UITransform); uiTransform.width = canvas.width; uiTransform.height = canvas.height; let widget = node.addComponent(Widget); widget.isAlignBottom = true; widget.isAlignTop = true; widget.isAlignLeft = true; widget.isAlignRight = true; widget.left = 0; widget.right = 0; widget.top = 0; widget.bottom = 0; return node; } /**获取层级节点*/ public getLayerNode(layerIndex: number): Node { return this._uiRoot.children[layerIndex] || this._uiRoot; } private loadBundleAsync(bundleName: string): Promise { return new Promise((resolve, reject) => { assetManager.loadBundle(bundleName, null, (err, loadedBundle) => { if (err) { reject(err); } else { resolve(loadedBundle); } }); }); } private loadPrefabAsync(bundle: AssetManager.Bundle, prefabName: string): Promise { return new Promise((resolve, reject) => { bundle.load(prefabName, (err, data: Prefab) => { if (err) { reject(err); } else { resolve(data); } }); }); } } export const gui = ui.getInstance();