feat: 完成肉鸽回合制游戏核心玩法迭代
新增连杀系统、金币飞行特效、波次HUD、屏幕震动等表现功能,重构怪物刷出逻辑与难度动态调节,调整回合回血比例,优化游戏节奏与体验。 主要变更: 1. 调整回合回血比例从0.5到0.4,优化前期节奏 2. 新增连杀计数与奖励系统,支持5/10/20连杀触发对应表现 3. 实现怪物死亡掉落金币的抛物线飞行特效 4. 增加波次进度HUD,显示剩余怪物与全局回合进度 5. 新增屏幕震动工具与战斗横幅统一展示系统 6. 重构怪物刷出逻辑,支持按回合类型调整刷怪间隔,加入清场加速机制 7. 优化动态难度调节算法,增加滞回与指数平滑,避免难度突变 8. 新增Boss预警、登场事件与回合清屏事件,完善事件总线 9. 调整怪物数量阈值与回合倒计时档位,适配新的节奏设计
This commit is contained in:
101
assets/script/game/map/CoinFlyComp.ts
Normal file
101
assets/script/game/map/CoinFlyComp.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user