123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- import { _decorator, Component, director, error, instantiate, Node, Prefab, UITransform, Widget, assetManager, AssetManager, tween, v3, UIOpacity, Vec3, ProgressBar, Sprite, Tween } from "cc";
- import ui_base from "./ui_base";
- import { ResolutionAutoFit } from "./ui_ResolutionAutoFit";
- const { ccclass, property } = _decorator;
- @ccclass('ui_updater')
- class ui_updater extends Component {
- update(dt: number) {
- ui_base.updateAll(dt);
- }
- }
- export enum GameUILayers {
- GAME,
- JOY_STICK,
- HUD,
- POPUP,
- ALERT,
- NOTICE,
- LOADING,
- OVERLAY
- }
- type Constructor<T extends ui_base> = new () => T;
- /**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<void> {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- public delay_second(s: number): Promise<void> {
- return new Promise(resolve => setTimeout(resolve, s * 1000));
- }
- public progressBar_anim(bar: ProgressBar, progress: number, duration: number = 0.2): Promise<void> {
- 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<void> {
- 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<void> {
- 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<void> {
- 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<void> {
- 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<void> {
- 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<void> {
- 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<void> {
- 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<void> {
- 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 bounce_anim(node: Node, height: number = 100, duration: number = 0.5): Promise<void> {
- 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<void> {
- node.setPosition(startPos);
- return new Promise((resolve) => {
- //随机方向
- 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();
- });
- }
- 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;
- }
- private getLayers(): GameUILayers[] {
- return Object.values(GameUILayers).filter(value => typeof value === 'number') as GameUILayers[];
- }
- /**
- * @en init,`don't call more than once`.
- * @zh 初始化UIMgr,`不要多次调用`
- * */
- public init(uiCanvas: Node | Prefab) {
- if (this._uiCanvas) return;
- if (!uiCanvas) throw error('uiCanvas must be a Node or Prefab');
- if (uiCanvas instanceof Node) {
- this._uiCanvas = uiCanvas;
- }
- else {
- this._uiCanvas = instantiate(uiCanvas);
- director.getScene().addChild(this._uiCanvas);
- }
- this._uiCanvas.name = '__ui_canvas__';
- director.addPersistRootNode(this._uiCanvas);
- if (!this._uiCanvas.getComponent(ui_updater)) {
- this._uiCanvas.addComponent(ui_updater);
- }
- let canvas = this._uiCanvas.getComponent(UITransform);
- this._uiCanvas.addComponent(ResolutionAutoFit);
- this._uiRoot = this.createFullScreenNode();
- this._uiRoot.name = 'root'
- canvas.node.addChild(this._uiRoot);
- const layers = this.getLayers();
- //create layers
- for (let i = 0; i < layers.length; ++i) {
- let layerNode = this.createFullScreenNode();
- layerNode.name = 'layer_' + GameUILayers[layers[i]];
- this._uiRoot.addChild(layerNode);
- }
- }
- /**获取层级节点*/
- public getLayerNode(layerIndex: number): Node {
- return this._uiRoot.children[layerIndex] || this._uiRoot;
- }
- /**关闭所有界面*/
- public closeAll() {
- ui_base.closeAll();
- }
- /**关闭和释放所有界面 */
- public closeAndReleaseAll() {
- ui_base.closeAndReleaseAll();
- }
- /**关闭某个界面*/
- public close<T extends ui_base>(uiCls: Constructor<T>): void {
- this.get(uiCls)?.close();
- }
-
- /**获取界面*/
- public get<T extends ui_base>(uiCls: Constructor<T>): T {
- let all = (ui_base as any)._uis;
- for (let i = 0; i < all.length; ++i) {
- let c = all[i];
- if (c instanceof uiCls) {
- return c;
- }
- }
- return null;
- }
- /**某个界面是否显示中*/
- public isShowing<T extends ui_base>(uiCls: Constructor<T>): boolean {
- return (ui_base as any)._hasCls(uiCls);
- }
- private _clss_loading: Set<any> = new Set();
- /**是否有正在加载中的界面*/
- public isLoading<T extends ui_base>(uiCls: Constructor<T> | null): boolean {
- if (!uiCls) return this._clss_loading.size > 0;
- return this._clss_loading.has(uiCls);
- }
- /***
- * @en show ui by the given parameters.
- * @zh 显示UI
- * @param uiCls the class, must inherits from the class `UIController`.
- * @returns the instance of `uiCls`
- * */
- public async show<T extends ui_base>(uiCls: Constructor<T>, ...data: any[]): Promise<T | null> {
- // if (this.isLoading(uiCls)) return null;
- // if (this.isShowing(uiCls)) return null;
- let ui = new uiCls();
- this._clss_loading.add(uiCls);
- let bundleName = ui.bundle;
- if (bundleName) {
- let bundle = assetManager.getBundle(bundleName);
- if (!bundle) {
- try {
- const loadedBundle = await this.loadBundleAsync(bundleName);
- return await this._create(loadedBundle, ui, uiCls, ...data);
- } catch (err) {
- console.error(err);
- this._clss_loading.delete(uiCls);
- }
- } else {
- return await this._create(bundle, ui, uiCls, ...data);
- }
- } else {
- this._clss_loading.delete(uiCls);
- console.error("ui no bundle name");
- return null;
- }
- }
- private async _create<T extends ui_base>(bundle: AssetManager.Bundle, ui: T, uiCls: Constructor<T>, ...p: any[]): Promise<T> {
- try {
- const data: Prefab = await this.loadPrefabAsync(bundle, ui.prefab);
- let node: Node = instantiate(data);
- let parent = this.getLayerNode(ui.layer);
- parent.addChild(node);
- (ui as any)._setup(uiCls, node, ...p);
- this._clss_loading.delete(uiCls);
- return ui;
- } catch (err) {
- console.error(err);
- this._clss_loading.delete(uiCls);
- return;
- }
- }
- private loadBundleAsync(bundleName: string): Promise<AssetManager.Bundle> {
- 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<Prefab> {
- return new Promise((resolve, reject) => {
- bundle.load(prefabName, (err, data: Prefab) => {
- if (err) {
- reject(err);
- } else {
- resolve(data);
- }
- });
- });
- }
- }
- export const gui = ui.getInstance();
- export { ui_base };
|