消消方块阵换皮表情
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

224 lines
8.0 KiB

import { _decorator, Asset, assetManager, AssetManager, Component, easing, instantiate, Node, NodePool, Prefab, sp, SpriteFrame, Tween, tween, UIOpacity, v3, Vec3, VideoPlayer } from 'cc';
import { IUIConfig, UIInfo } from './UIManager';
import { ResPakage } from '../res/ResourcesManager';
const { ccclass, property } = _decorator;
@ccclass('UIBase')
export class UIBase extends Component {
getResPkg(): ResPakage {
return null;
}
onLoad(): void {
}
uiInfo: UIInfo = null;
prema: any;
private _onOpenAnimEnd: Function;
private _onClose: Function;
private _showLoading: number = 0;
/** 底部波次宝箱打开 SkillSelect 时启用追踪日志 */
protected _skillSelectTreasureTrace = false;
/**
* 通过UI管理器打开面板时才会触发onAdded
* @param args
*/
onAdded(args: OpenParam) {
this.prema = args?.data;
this._onOpenAnimEnd = args?.onOpenAnimEnd;
this._onClose = args?.onClose;
this._skillSelectTreasureTrace = !!(args?.onSkillSelectOpenSuccess || args?.onSkillSelectOpenFail);
if (args?.showLoading !== undefined && args?.showLoading !== null) this._showLoading = args?.showLoading;
let resPkg = this.getResPkg();
if (resPkg) {
if (this._showLoading > 0) {
gg.game.hideLoading();
}
gg.res.loadResPakg(resPkg, (c, t) => {
}, () => {
// 分包/资源异步返回时用户可能已切走并销毁本面板,再调 onInit 会空引用(如 UIpageUserLevelup.refreshUI)
if (!this.isValid || !this.node?.isValid) {
if (this._showLoading > 0) {
gg.game.hideLoading();
}
return;
}
this.onInit();
if (this._showLoading > 0) {
gg.game.hideLoading();
}
});
} else {
this.onInit();
}
}
onInit() {
}
/**
* 预制的一个打开效果,子类需要时可直接调用
* @param callback
*/
showOpenCenterEff(callback?: Function) {
let center = this.node.getChildByName('center') || this.node.getChildByName('ui_center');
if (center) {
Tween.stopAllByTarget(center);
tween(center)
.set({ scale: Vec3.ZERO })
.to(0.3, { scale: Vec3.ONE }, { easing: easing.backOut })
.call(() => {
if (callback) callback();
if (this._onOpenAnimEnd) this._onOpenAnimEnd();
}).start();
}
}
showCloseCenterEff(type = 1, callback?: Function) {
let center = this.node.getChildByName('center') || this.node.getChildByName('ui_center');
if (center) {
if (type == 1) {
tween(center).to(0.2, { scale: Vec3.ZERO }).set({ scale: Vec3.ONE }).call(() => {
if (callback) callback();
}).start();
}
else if (type == 2) {
tween(center).to(0.2, { opacity: 0 }).set({ opacity: 255 }).call(() => {
if (callback) callback();
}).start();
}
else {
if (callback) callback();
}
}
return center != null;
}
/**
* 显示打开效果
* @param type 1 整个页面渐显 2 只渐显黑色半透明背景遮罩
*/
showOpenMaskEff(type = 1) {
if (type == 1) {
let o = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity)
tween(o).set({ opacity: 0 }).to(0.3, { opacity: 255 }).start();
} else {
let m = this.node.getChildByName('bgMask') || this.node.getChildByName('ui_bgMask') || this.node.getChildByName('ui_mask');
if (m) {
let o = m.getComponent(UIOpacity) || m.addComponent(UIOpacity)
tween(o).set({ opacity: 0 }).to(0.3, { opacity: 180 }).start();
}
}
}
closeMaskEff(callback?: Function) {
let m = this.node.getChildByName('bgMask') || this.node.getChildByName('ui_bgMask') || this.node.getChildByName('ui_mask');
if (m) {
let o = m.getComponent(UIOpacity) || m.addComponent(UIOpacity)
tween(o).to(0.2, { opacity: 0 }).call(() => {
if (callback) callback();
}).start();
} else {
if (callback) callback();
}
return m != null;
}
/**
* 关闭面板
* @param tweenType 1: 直接关闭 2: 弹窗缩回,同时背景渐变成透明 3: 不缩放,直接渐变到透明
* @returns
*/
async close(tweenType = 1) {
await gg.ui.closePanel(this.uiInfo.id, tweenType);
}
/** 销毁前停止子树 tween、Spine,减轻与渲染/合批并发时的异常(快速切页、SpriteFrame/getGFXTexture、spine wasm) */
private prepareDestroyForRender(): void {
if (!this.node?.isValid) return;
const stopTweenDeep = (n: Node) => {
if (!n?.isValid) return;
Tween.stopAllByTarget(n);
for (const c of n.children) {
stopTweenDeep(c);
}
};
stopTweenDeep(this.node);
const sks = this.node.getComponentsInChildren(sp.Skeleton);
for (const sk of sks) {
if (sk?.isValid) {
try {
// 缓存模式(SHARED_CACHE / PRIVATE_CACHE)下引擎禁止 clearTracks,会打 debug 警告
const skAny = sk as sp.Skeleton & { isAnimationCached?: () => boolean };
if (typeof skAny.isAnimationCached !== 'function' || !skAny.isAnimationCached()) {
sk.clearTracks();
}
sk.setCompleteListener(null);
} catch (_) { /* wasm 已释放等 */ }
}
}
}
destroyPanel(tweenType = 1) {
return new Promise((resolve, reject) => {
let fun = () => {
this.prepareDestroyForRender();
if (this.node.isValid) {
this.node.destroy();
}
// ResManager.Ins.releaseRes(this.uiconf.bundle, this.uiconf.path, this.uiconf.name);
resolve(true);
if (this._onClose) this._onClose();
}
let isClose = false;
if (tweenType == 2) {
this.closeMaskEff();
isClose = this.showCloseCenterEff(1, fun.bind(this));
if (isClose) return;
}
if (tweenType == 3) {
isClose = this.showCloseCenterEff(2, fun.bind(this));
if (isClose) return;
else {
tween(this.node).to(0.2, { opacity: 0 }).call(fun.bind(this)).start();
return
}
}
fun();
})
}
protected onDestroy(): void {
let resPkg = this.getResPkg();
if (resPkg) {
gg.res.clearResPkg(resPkg.name);
}
// Break references early to help GC and avoid accidental retention when panels are pooled/cached.
this.prema = null;
this._onOpenAnimEnd = null;
this._onClose = null;
this.uiInfo = null;
}
}
/**
* 打开面板参数
*/
export interface OpenParam {
onOpenAnimEnd?: Function;
onClose?: Function;
data?: any;
showLoading?: number;
/** 仅底部波次宝箱 SkillSelect:load + onInit 成功后回调 */
onSkillSelectOpenSuccess?: () => void;
/** 仅底部波次宝箱 SkillSelect:打开失败回调(detail 为错误摘要;context 含三选一 id 与本局武器/技能) */
onSkillSelectOpenFail?: (reason: string, detail?: string, context?: Record<string, unknown>) => void;
/** @internal 同一轮宝箱打开是否已上报过成功/失败,避免 init_error 与 panel_not_visible 重复上报 */
_skillSelectResultReported?: boolean;
}