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.
144 lines
4.8 KiB
144 lines
4.8 KiB
import { _decorator, Component, Label, Tween, tween, UITransform, Vec3, randomRange } from 'cc';
|
|
const { ccclass } = _decorator;
|
|
|
|
/**
|
|
* 伤害飘字:随机左右 → 解析抛物线(升→落,速度连续)+ 显隐/缩放。
|
|
*/
|
|
@ccclass('HurtScore')
|
|
export class HurtScore extends Component {
|
|
scoreLabel: Label = null;
|
|
|
|
/** 轨迹时间代理(与节点 tween 分开,销毁时一并停掉) */
|
|
private _pathMotion: { t: number } | null = null;
|
|
|
|
protected onDisable(): void {
|
|
this.unscheduleAllCallbacks();
|
|
Tween.stopAllByTarget(this.node);
|
|
if (this._pathMotion) {
|
|
Tween.stopAllByTarget(this._pathMotion);
|
|
this._pathMotion = null;
|
|
}
|
|
}
|
|
|
|
protected onLoad(): void {
|
|
this.scoreLabel = this.node.getComponent(Label);
|
|
}
|
|
|
|
/** 显示伤害值 */
|
|
showHurtNum(
|
|
hurtNum: number,
|
|
isCrit: boolean,
|
|
bRateAtk: boolean,
|
|
opts?: { hideBaoBadge?: boolean },
|
|
) {
|
|
this.unscheduleAllCallbacks();
|
|
Tween.stopAllByTarget(this.node);
|
|
if (this._pathMotion) {
|
|
Tween.stopAllByTarget(this._pathMotion);
|
|
this._pathMotion = null;
|
|
}
|
|
if (!this.scoreLabel?.isValid) {
|
|
return;
|
|
}
|
|
|
|
let numStr = '';
|
|
if (hurtNum >= 1000000) {
|
|
numStr = `${Math.floor(hurtNum / 100000) / 10}M`;
|
|
} else if (hurtNum >= 1000) {
|
|
numStr = `${Math.floor(hurtNum / 100) / 10}K`;
|
|
} else {
|
|
numStr = `${Math.floor(hurtNum)}`;
|
|
}
|
|
this.scoreLabel.string = `-${numStr}`;
|
|
|
|
const baoNode = this.node.getChildByName('暴');
|
|
const showBao = !opts?.hideBaoBadge && isCrit && !bRateAtk;
|
|
if (baoNode) {
|
|
baoNode.active = showBao;
|
|
}
|
|
|
|
if (bRateAtk) {
|
|
this.scheduleOnce(() => {
|
|
const width = this.scoreLabel.node.getComponent(UITransform).width;
|
|
const di = this.node.getChildByName('底');
|
|
if (di) {
|
|
di.getComponent(UITransform).width = width;
|
|
}
|
|
});
|
|
} else if (showBao) {
|
|
this.scheduleOnce(() => {
|
|
const width = this.scoreLabel.node.getComponent(UITransform).width;
|
|
if (baoNode?.isValid) {
|
|
baoNode.position = new Vec3(width / 2 + 2, 32, 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
this._playTween7(() => {
|
|
if (this.node?.isValid) {
|
|
gg.res.putNode(this.node);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 解析抛物线插值(非贝塞尔):y = y0 + vy·t + ½g·t²,x 匀速侧移。
|
|
* 顶点 vy 只瞬间为 0,无两段缓动接缝,也无控制点二次上拱。
|
|
* 数值对齐原效果:升 +10、落 100~130、侧移 ±20。
|
|
*/
|
|
private _playTween7(onComplete: () => void): void {
|
|
const root = this.node;
|
|
const isLeft = Math.random() < 0.5;
|
|
const start = root.position.clone();
|
|
const baseSx = root.scale.x;
|
|
const baseSy = root.scale.y;
|
|
|
|
root.opacity = 1;
|
|
root.setScale(baseSx * 0.8, baseSy * 0.8, 1);
|
|
root.setPosition(start);
|
|
|
|
const peakH = 10;
|
|
const side = isLeft ? -1 : 1;
|
|
const fallDist = randomRange(100, 130);
|
|
// tPeak 决定弧形节奏;speed 只压缩播放时间(轨迹不变)
|
|
const tPeak = 0.35;
|
|
const speed = 1.6; // 加快 60%
|
|
const baseDuration = tPeak * (1 + Math.sqrt(fallDist / peakH));
|
|
const duration = baseDuration / speed;
|
|
const tPeakPlay = tPeak / speed;
|
|
const g = -2 * peakH / (tPeakPlay * tPeakPlay);
|
|
const vy = -g * tPeakPlay;
|
|
const endX = start.x + side * 20;
|
|
const vx = (endX - start.x) / duration;
|
|
const fadeIn = Math.min(0.25 / speed, duration * 0.4);
|
|
|
|
const motion = { t: 0 };
|
|
this._pathMotion = motion;
|
|
|
|
tween(root)
|
|
.parallel(
|
|
tween(root)
|
|
.to(fadeIn, {
|
|
opacity: 255,
|
|
scale: new Vec3(baseSx, baseSy, 1),
|
|
}, { easing: 'quadOut' })
|
|
.to(Math.max(0.01, duration - fadeIn), { opacity: 0 }, { easing: 'sineIn' }),
|
|
tween(motion).to(duration, { t: duration }, {
|
|
onUpdate: () => {
|
|
if (!root.isValid) return;
|
|
const t = motion.t;
|
|
root.setPosition(
|
|
start.x + vx * t,
|
|
start.y + vy * t + 0.5 * g * t * t,
|
|
start.z,
|
|
);
|
|
},
|
|
}),
|
|
)
|
|
.call(() => {
|
|
this._pathMotion = null;
|
|
onComplete();
|
|
})
|
|
.start();
|
|
}
|
|
}
|
|
|