Files
pixelheros/assets/script/game/map/CoinFlyComp.ts
pan 409113e269 feat: 完成肉鸽回合制游戏核心玩法迭代
新增连杀系统、金币飞行特效、波次HUD、屏幕震动等表现功能,重构怪物刷出逻辑与难度动态调节,调整回合回血比例,优化游戏节奏与体验。

主要变更:
1.  调整回合回血比例从0.5到0.4,优化前期节奏
2.  新增连杀计数与奖励系统,支持5/10/20连杀触发对应表现
3.  实现怪物死亡掉落金币的抛物线飞行特效
4.  增加波次进度HUD,显示剩余怪物与全局回合进度
5.  新增屏幕震动工具与战斗横幅统一展示系统
6.  重构怪物刷出逻辑,支持按回合类型调整刷怪间隔,加入清场加速机制
7.  优化动态难度调节算法,增加滞回与指数平滑,避免难度突变
8.  新增Boss预警、登场事件与回合清屏事件,完善事件总线
9.  调整怪物数量阈值与回合倒计时档位,适配新的节奏设计
2026-08-11 19:00:23 +08:00

102 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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);
}
}