import { _decorator, Component, director, instantiate, Label, Node, sp, Sprite, Tween, tween, UIOpacity, UITransform, v3, Vec3 } from 'cc'; import { I18nManager } from '../../Main/Scripts/managers/I18nManager'; import { getLanguage, gold2cash } from '../../Main/Scripts/main/comm'; import { UIManager } from '../../Main/Scripts/managers/UIManager'; import { AudioManager } from '../../Main/Scripts/managers/AudioManager'; import { Icon } from './Icon'; import { SlotGame } from './SlotGame'; import { SlotBar } from './SlotBar'; import { NET_MODE } from 'cc/env'; const { ccclass, property } = _decorator; @ccclass('SlotMsg') export class SlotMsg extends Component { // 展示文字信息 // @property(sp.Skeleton) // smallMsgSpine: sp.Skeleton = null; // // 展示普通赢分信息 // @property(sp.Skeleton) // middleMsgSpine: sp.Skeleton = null; // // 展示总赢分信息 // @property(sp.Skeleton) // largeMsgSpine: sp.Skeleton = null; // 展示文字信息 @property(Sprite) smallMsgSpine: Sprite = null; // 展示普通赢分信息 @property(Sprite) middleMsgSpine: Sprite = null; // 展示总赢分信息 @property(Sprite) largeMsgSpine: Sprite = null; @property(Sprite) i18nSpriteMsg: Sprite = null; @property(Node) grayMsgNode: Node = null; @property(Node) multiNode: Node = null; protected onLoad(): void { this.grayMsgNode.active = false; } startPos: Vec3 = new Vec3(-259.5, 0, 0); maxWidth: number = 540; currTotaleMulti: number = 0 currRoundWinMsg: string = '' showLabelMsgForTween() { Tween.stopAllByTarget(this.i18nSpriteMsg.node); this.smallMsgSpine.node.active = true; // this.smallMsgSpine.setAnimation(0, '2', false); // this.smallMsgSpine.setCompleteListener(() => { // this.smallMsgSpine.setAnimation(0, '1', true); // this.smallMsgSpine.setCompleteListener(null); // }); this.middleMsgSpine.node.active = false; this.largeMsgSpine.node.active = false; let add = 0; let isFirst = true; let startPos = new Vec3(0, 0, 0); let endPos = new Vec3(0, 0, 0); let speed = 1080 / 8; let delayTime = 0; let moveTime = 0; // 通过数字获取name let spriteName = ['Normal8', 'Normal9', 'Normal10', 'Normal11']; let setSpriteFrame = () => { startPos.x = this.startPos.x; let spriteFrame = I18nManager.instance.getSpriteFrame(spriteName[add]); this.i18nSpriteMsg.spriteFrame = spriteFrame; let distane = spriteFrame.rect.width + 109; let X = -430 - distane; endPos.x = X; delayTime = 1; moveTime = distane / speed; if (spriteFrame.rect.width < this.maxWidth) { startPos.x = -spriteFrame.rect.width / 2; endPos.x = -spriteFrame.rect.width / 2; delayTime = 2; moveTime = 1; } //带图的文本稍微上移几个像素 if (add == 0) { startPos.y = 5; endPos.y = 5; } } setSpriteFrame(); let tweenFun = () => { tween(this.i18nSpriteMsg.node) .set({ position: startPos }) .delay(delayTime) .to(moveTime, { position: endPos }) .call(() => { isFirst = false; add = add + 1 >= spriteName.length ? 0 : add + 1; setSpriteFrame(); tweenFun(); }) .start(); } tweenFun(); } setTotaleMulti(mul: number) { this.currTotaleMulti = mul } showTotalWinAnimaiton(startScore: number, endScore: number, cb) { this.smallMsgSpine.node.active = false; this.middleMsgSpine.node.active = false; this.largeMsgSpine.node.active = true; // this.largeMsgSpine.setAnimation(0, '2', false); // this.largeMsgSpine.setCompleteListener(() => { // this.largeMsgSpine.setAnimation(0, '1', true); // this.largeMsgSpine.setCompleteListener(null); // }); let msg = this.largeMsgSpine.node.getChildByName('msg'); let winCount = msg.getChildByName('winCount'); winCount.getComponent(Label).string = gold2cash(startScore); AudioManager.instance.playSFX('Gold_Up'); UIManager.instance.tweenScorelinear(startScore, endScore, 2) .onUpdate((v: number) => { winCount.getComponent(Label).string = gold2cash(v); }) .onComplete(() => { winCount.getComponent(Label).string = gold2cash(endScore); AudioManager.instance.stopSFX('Gold_Up'); tween(winCount) .to(0.2, { scale: v3(1.1, 1.1, 1.1) }) .to(0.2, { scale: v3(1, 1, 1) }) .start() this.scheduleOnce(() => { director.getScene()?.getComponentInChildren(SlotGame).setFreeTotalMulti(1) if (cb) cb(); }, 1) }) .start(); } showRoundWinMsg(endScore: number, currMulti: number = 0) { if (endScore === 0) return; this.smallMsgSpine.node.active = false; this.middleMsgSpine.node.active = true; this.largeMsgSpine.node.active = false; // this.middleMsgSpine.setAnimation(0, '2', false); // this.middleMsgSpine.setCompleteListener(() => { // this.middleMsgSpine.setAnimation(0, '1', true); // this.middleMsgSpine.setCompleteListener(null); // }); let msg = this.middleMsgSpine.node.getChildByName('msg'); let winCount = msg.getChildByName('winCount'); if (currMulti == 0) { winCount.getComponent(Label).string = gold2cash(endScore); this.currRoundWinMsg = winCount.getComponent(Label).string AudioManager.instance.playSFX('Slot_Msg_Normal_Win'); } else { winCount.getComponent(Label).string = this.currRoundWinMsg + ' x ' + currMulti } } showTotalWinMsg(endScore: number) { if (endScore === 0) return; this.smallMsgSpine.node.active = false; this.middleMsgSpine.node.active = false; this.largeMsgSpine.node.active = true; // this.largeMsgSpine.setAnimation(0, '2', false); // this.largeMsgSpine.setCompleteListener(() => { // this.largeMsgSpine.setAnimation(0, '1', true); // this.largeMsgSpine.setCompleteListener(null); // }); let msg = this.largeMsgSpine.node.getChildByName('msg'); let winCount = msg.getChildByName('winCount'); winCount.getComponent(Label).string = gold2cash(endScore); AudioManager.instance.playSFX('Slot_Msg_Total_Win'); } hideWinSpine() { this.smallMsgSpine.node.active = true; this.middleMsgSpine.node.active = false; this.largeMsgSpine.node.active = false; } /** * 展示多倍炸弹倍率动画 * @param spinData 当前spin数据 * @param callBack 动画结束回调 * * 此函数内部的run是async,从上到下用await/同步promise的方式,严格保证顺序和队列效果,一步一步地执行动画和界面更新。 * 下面会对run里每一行的逻辑做中文注释,逐步解析 */ showMultiWinAnimation(spinData, callBack: () => void) { // 获取倍率描述,多模式下兼容字段名 // let mulDesc = spinData?.WinInfo?.MulDesc || spinData?.WinInfo?.muldesc; let multiedScore = spinData?.RoundInfo.AllScoreMul; // 最终得到的分数 // let mulValue = mulDesc?.Mul; // 总倍率 let slotGame = director.getScene()?.getComponentInChildren(SlotGame); let multiInfos = slotGame?.getCurrentMultiSettleInfos?.() || []; // 炸弹节点信息 if (!multiInfos.length) { console.error('SlotGame not find multiInfos msg'); return; } // 提示分数的spine节点和最终显示位置 let msg = this.largeMsgSpine.node.getChildByName('msg'); let winCount = msg.getChildByName('winCount'); let uiTf = this.node.getComponent(UITransform); let centerLocalPos = v3(50, -500, 0); // 信息栏坐标(本地) let targetLocalPos = uiTf.convertToNodeSpaceAR(winCount.getWorldPosition()); // 目标分数字显示位置 // 设置一个节点的多倍文本 let setMulText = (node: Node, mul: number) => { // let labNode = node.getChildByName('lab'); if (!node) return; let lab = node.getComponent(Label); if (!lab) return; lab.string = `x${mul}`; }; // 节点tween到目标位置的promise(动画完resolve) let tweenToAsync = (node: Node, toPos: Vec3, duration: number) => { return new Promise((resolve) => { tween(node) .to(duration, { position: toPos }, { easing: 'quadIn' }) .call(() => resolve()) .start(); }); }; // 放大动画(脉冲)的promise(动画完resolve) let pulseAsync = (node: Node) => { return new Promise((resolve) => { tween(node) .to(0.2, { scale: v3(1.5, 1.5, 1.5) }, { easing: 'quadOut' }) .call(() => resolve()) .start(); }); }; // 延迟promise,用于动画间隔 let delayAsync = (seconds: number) => { return new Promise((resolve) => { this.scheduleOnce(() => resolve(), seconds); }); }; // === 跑队列 === // 这部分run内部就是完整的队列模式,所有动画/节点操作都是await同步执行 let run = async () => { // 1. 确认总倍率节点,并首先飞入信息栏 let centerMulNode = slotGame.node.getChildByPath('FreeGameBg1/totalMulti'); // this.node.addChild(centerMulNode); // centerMulNode.setPosition(centerLocalPos); // centerMulNode.setScale(1, 1, 1); // centerMulNode.active = true; // setMulText(centerMulNode, 0); //总倍率有值时要第一个参与倍率计算 if (this.currTotaleMulti > 0) { let flyNode = instantiate(centerMulNode) flyNode.setPosition(centerMulNode.getPosition()) this.node.addChild(flyNode) await tweenToAsync(flyNode, centerLocalPos, 0.6); flyNode.destroy() this.showRoundWinMsg(1, this.currTotaleMulti) } let sumMul = this.currTotaleMulti; // 2. 遍历每一个待结算的Multi多倍icon,按顺序“飞入信息栏”, 逐步累加 for (let i = 0; i < multiInfos.length; i++) { let info = multiInfos[i]; if (!info?.multiNode?.isValid) continue; let iconComp = info.iconNode?.getComponent(Icon); // 2.1 播放爆炸动画 加入队列,爆炸完之后再进行下一步 await new Promise((resolve) => { iconComp.playMultiSpine(); this.scheduleOnce(() => resolve(), 0.2); }); // 2.2 当前multiNode放大(脉冲动画),await保证上一步结束 await pulseAsync(info.multiNode); // 2.3 复制multiNode,实际用于“飞入信息栏” let sourceWorldPos = info.multiNode.getWorldPosition(); let sourceLocalPos = uiTf.convertToNodeSpaceAR(sourceWorldPos); let flyNode = instantiate(info.multiNode); info.multiNode.active = false; this.node.addChild(flyNode); flyNode.setPosition(sourceLocalPos); // 2.4 飞到信息栏动画 await tweenToAsync(flyNode, centerLocalPos, 0.6); flyNode.destroy(); // 2.5 累加倍率,信息栏文本根据每个倍率到达而改变 sumMul += info.mul; this.showRoundWinMsg(1, sumMul) // centerMulNode.active = true; // setMulText(centerMulNode, sumMul); // 2.6 中心放大动画(脉冲) // await pulseAsync(centerMulNode); // 2.7 小延迟,准备下一个icon await delayAsync(0.1); } // 4. 中心multi飞到最终目标位置 // await tweenToAsync(centerMulNode, targetLocalPos, 0.2); // centerMulNode.destroy(); // 5. 展示最终总分获胜弹窗和新的总倍率 let showCurrMulti = () => { return new Promise((resolve) => { this.setTotaleMulti(sumMul) let totalMultiBgSpin = slotGame.node.getChildByPath('FreeGameBg1/qiu') tween(totalMultiBgSpin) .to(0.2, { scale: v3(1.2, 1.2, 1) }) .to(0.2, { scale: v3(1, 1, 1) }) .call(() => { slotGame.setFreeTotalMulti(this.currTotaleMulti) resolve() }) .start() }); } if (spinData.IsFree) { await showCurrMulti() } this.showTotalWinMsg(multiedScore); // 直接调用回调(如果业务上需要动画结束后再回调可考虑调整) if (callBack) callBack(); let slotBar = director.getScene()?.getComponentInChildren(SlotBar); if (slotBar) slotBar.updateWinMsg(spinData.AllScore, false); }; // 直接触发队列执行,不等待run()结束,动画流程内部用await限定顺序,外部流程不依赖 run(); } showTipSmall(tips: string, Type: string = "") { Tween.stopAllByTarget(this.grayMsgNode); let bgWidth, bgHeigt = 0; if (Type != "") { this.grayMsgNode.getChildByName('layout').getChildByName('fast').active = true; bgWidth = 142; this.grayMsgNode.getChildByPath('layout/fast/toast_Turboon').active = Type == "onFast"; this.grayMsgNode.getChildByPath('layout/fast/toast_Turbooff').active = Type == "offFast"; } else { this.grayMsgNode.getChildByName('layout').getChildByName('fast').active = false; bgWidth = 100; } let lab = this.grayMsgNode.getChildByPath('layout/lab').getComponent(Label); lab.string = tips; lab.updateRenderData(true); let tipWidth = this.grayMsgNode.getChildByPath('layout/lab').getComponent(UITransform).width; let frame = this.grayMsgNode.getChildByName('sp'); frame.getComponent(UITransform).setContentSize(tipWidth + bgWidth, 132); this.grayMsgNode.setScale(0, 0, 0); this.grayMsgNode.active = true; tween(this.grayMsgNode) .set({ scale: v3(0, 0, 0) }) .to(0.1, { scale: v3(1, 1, 1) }) .delay(1) .call(() => { this.grayMsgNode.active = false; }) .start(); } }