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.

145 lines
4.8 KiB

3 weeks ago
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();
}
}