380 lines
15 KiB
TypeScript
380 lines
15 KiB
TypeScript
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';
|
||
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;
|
||
|
||
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();
|
||
}
|
||
|
||
showTotalWinAnimaiton(startScore: number, endScore: number, cb) {
|
||
this.currTotaleMulti = 0
|
||
this.node.getChildByName('totalMulti').active = false;
|
||
|
||
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(() => {
|
||
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 = mulDesc?.Multied; // 最终得到的分数
|
||
// let mulValue = mulDesc?.Mul; // 总倍率
|
||
let slotGame = director.getScene()?.getComponentInChildren(SlotGame);
|
||
let bombInfos = slotGame?.getCurrentBombSettleInfos?.() || []; // 炸弹节点信息
|
||
|
||
if (!bombInfos.length) {
|
||
console.error('SlotGame not find bombInfos 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<void>((resolve) => {
|
||
tween(node)
|
||
.to(duration, { position: toPos }, { easing: 'quadInOut' })
|
||
.call(() => resolve())
|
||
.start();
|
||
});
|
||
};
|
||
|
||
// 放大动画(脉冲)的promise(动画完resolve)
|
||
let pulseAsync = (node: Node) => {
|
||
return new Promise<void>((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<void>((resolve) => {
|
||
this.scheduleOnce(() => resolve(), seconds);
|
||
});
|
||
};
|
||
|
||
// === 跑队列 ===
|
||
// 这部分run内部就是完整的队列模式,所有动画/节点操作都是await同步执行
|
||
let run = async () => {
|
||
// 1. 确认总倍率节点,并首先飞入信息栏
|
||
// let seedNode = bombInfos[0]?.multiNode;
|
||
let centerMulNode = this.node.getChildByName('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)
|
||
this.node.addChild(flyNode)
|
||
flyNode.setPosition(centerMulNode.getPosition())
|
||
await tweenToAsync(flyNode, centerLocalPos, 0.6);
|
||
flyNode.destroy()
|
||
this.showRoundWinMsg(1, this.currTotaleMulti)
|
||
}
|
||
|
||
let sumMul = this.currTotaleMulti;
|
||
// 2. 遍历每一个待结算的bomb多倍icon,按顺序“飞入信息栏”, 逐步累加
|
||
for (let i = 0; i < bombInfos.length; i++) {
|
||
let info = bombInfos[i];
|
||
if (!info?.multiNode?.isValid) continue;
|
||
|
||
let iconComp = info.iconNode?.getComponent(Icon);
|
||
// 2.1 播放爆炸动画 加入队列,爆炸完之后再进行下一步
|
||
await new Promise<void>((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. 展示最终总分获胜弹窗和新的总倍率
|
||
this.currTotaleMulti = sumMul
|
||
setMulText(centerMulNode, this.currTotaleMulti)
|
||
this.showTotalWinMsg(multiedScore);
|
||
// 直接调用回调(如果业务上需要动画结束后再回调可考虑调整)
|
||
if (callBack) callBack();
|
||
let slotBar = director.getScene()?.getComponentInChildren(SlotBar);
|
||
if (slotBar) slotBar.updateWinMsg(spinData.AllScore);
|
||
};
|
||
|
||
// 直接触发队列执行,不等待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();
|
||
}
|
||
|
||
|
||
}
|
||
|