新增连杀系统、金币飞行特效、波次HUD、屏幕震动等表现功能,重构怪物刷出逻辑与难度动态调节,调整回合回血比例,优化游戏节奏与体验。 主要变更: 1. 调整回合回血比例从0.5到0.4,优化前期节奏 2. 新增连杀计数与奖励系统,支持5/10/20连杀触发对应表现 3. 实现怪物死亡掉落金币的抛物线飞行特效 4. 增加波次进度HUD,显示剩余怪物与全局回合进度 5. 新增屏幕震动工具与战斗横幅统一展示系统 6. 重构怪物刷出逻辑,支持按回合类型调整刷怪间隔,加入清场加速机制 7. 优化动态难度调节算法,增加滞回与指数平滑,避免难度突变 8. 新增Boss预警、登场事件与回合清屏事件,完善事件总线 9. 调整怪物数量阈值与回合倒计时档位,适配新的节奏设计
102 lines
3.7 KiB
TypeScript
102 lines
3.7 KiB
TypeScript
/**
|
||
* @file CoinFlyComp.ts
|
||
* @description 金币飞行表现组件(表现层,纯视觉)
|
||
*
|
||
* 职责:
|
||
* 1. 监听 CoinFly 事件(怪物死亡掉金币,账务已由 MissionEconomy 即时结算)。
|
||
* 2. 从怪物位置生成金币 icon,抛物线散开后加速飞入钱包 icon。
|
||
* 3. 独立 NodePool 管理金币节点(上限 30),不复用伤害飘字池。
|
||
*
|
||
* 使用 oops-framework 模块:oops.message(事件解耦)。
|
||
* 编辑器绑定:flyLayer(全屏容器,最高 sibling)、coinIconSrc(钱包 coin/icon 节点,取其 spriteFrame)。
|
||
*/
|
||
import { _decorator, Node, Sprite, UITransform, tween, v3, Vec3, NodePool } from "cc";
|
||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||
import { GameEvent } from "../common/config/GameEvent";
|
||
|
||
const { ccclass, property } = _decorator;
|
||
|
||
/** 普通怪金币枚数 */
|
||
const COIN_COUNT_NORMAL = 3;
|
||
/** Boss 金币枚数 */
|
||
const COIN_COUNT_BOSS = 8;
|
||
/** 对象池上限(防节点泄漏) */
|
||
const POOL_MAX = 30;
|
||
|
||
@ccclass('CoinFlyComp')
|
||
export class CoinFlyComp extends CCComp {
|
||
@property({ type: Node, tooltip: "金币飞行容器(全屏节点,置于最高 sibling 盖在所有 UI 之上)" })
|
||
flyLayer: Node | null = null;
|
||
|
||
@property({ type: Node, tooltip: "钱包金币 icon 节点(运行期取其 spriteFrame 克隆)" })
|
||
coinIconSrc: Node | null = null;
|
||
|
||
/** 金币节点对象池 */
|
||
private pool: NodePool = new NodePool();
|
||
|
||
onLoad() {
|
||
// oops.message 全局事件总线:金币飞行
|
||
oops.message.on(GameEvent.CoinFly, this.onCoinFly, this);
|
||
}
|
||
|
||
onDestroy() {
|
||
oops.message.off(GameEvent.CoinFly, this.onCoinFly, this);
|
||
this.pool.clear();
|
||
}
|
||
|
||
private onCoinFly(event: string, data: { worldPos: Vec3 | null; gold: number; isBoss: boolean }) {
|
||
if (!this.flyLayer || !this.coinIconSrc || !data.worldPos) return;
|
||
|
||
const uiTransform = this.flyLayer.getComponent(UITransform);
|
||
if (!uiTransform) return;
|
||
|
||
const startPos = uiTransform.convertToNodeSpaceAR(data.worldPos);
|
||
const endPos = uiTransform.convertToNodeSpaceAR(this.coinIconSrc.worldPosition);
|
||
const count = data.isBoss ? COIN_COUNT_BOSS : COIN_COUNT_NORMAL;
|
||
|
||
for (let i = 0; i < count; i++) {
|
||
this.spawnCoin(startPos, endPos, i * 0.03);
|
||
}
|
||
}
|
||
|
||
/** 生成一枚金币:上抛散开 → 加速飞向钱包 → 回收 */
|
||
private spawnCoin(startPos: Vec3, endPos: Vec3, delay: number) {
|
||
const coin = this.pool.size() > 0 ? this.pool.get()! : this.createCoinNode();
|
||
coin.parent = this.flyLayer;
|
||
coin.setPosition(startPos);
|
||
coin.setScale(v3(1, 1, 1));
|
||
|
||
const scatter = v3(
|
||
startPos.x + (Math.random() * 120 - 60),
|
||
startPos.y + 60 + Math.random() * 40,
|
||
0);
|
||
|
||
tween(coin)
|
||
.delay(delay)
|
||
.to(0.25, { position: scatter }, { easing: "quadOut" })
|
||
.to(0.4, { position: endPos }, { easing: "quadIn" })
|
||
.call(() => this.recycleCoin(coin))
|
||
.start();
|
||
}
|
||
|
||
private createCoinNode(): Node {
|
||
const node = new Node("coin_fly");
|
||
node.addComponent(UITransform).setContentSize(32, 32);
|
||
const sp = node.addComponent(Sprite);
|
||
const srcSp = this.coinIconSrc?.getComponent(Sprite);
|
||
if (srcSp?.spriteFrame) {
|
||
sp.spriteFrame = srcSp.spriteFrame;
|
||
}
|
||
return node;
|
||
}
|
||
|
||
private recycleCoin(coin: Node) {
|
||
if (this.pool.size() >= POOL_MAX) {
|
||
coin.destroy();
|
||
return;
|
||
}
|
||
this.pool.put(coin);
|
||
}
|
||
}
|