ai端没有波次倒计时前加金币

master
chenanghui 5 days ago
parent 0ca939894e
commit ddcb581ab3
  1. 2184
      assets/bundles/battleGame/prefab/BattleGame.prefab
  2. 2
      assets/res/font/str.txt
  3. 4
      assets/script/ui/BattleGame/Auto_BattleGame.ts
  4. 138
      assets/script/ui/BattleGame/BattlePeachCoinFlyPresenter.ts
  5. 64
      assets/script/ui/BattleGame/CoinFly.ts
  6. 21
      assets/script/ui/BattleGame/UIBattleGame.ts

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -28,6 +28,7 @@ export class Auto_BattleGame extends UIBase {
ui_textGridDown:Node;
ui_textGrids:Node;
ui_textGridUp:Node;
ui_nextWaveTime:Node;
ui_top:Node;
ui_title:Node;
btnPause:Node;
@ -36,7 +37,6 @@ export class Auto_BattleGame extends UIBase {
lb_money:Label;
ui_wave:Node;
lb_wave:Label;
ui_nextWaveTime:Node;
ui_otherskill1List:Node;
ui_otherskill2List:Node;
ui_otherBag:Node;
@ -73,6 +73,7 @@ export class Auto_BattleGame extends UIBase {
this.ui_textGridDown = find("ui_textGridDown",this.node);
this.ui_textGrids = find("ui_textGrids",this.node);
this.ui_textGridUp = find("ui_textGridUp",this.node);
this.ui_nextWaveTime = find("ui_nextWaveTime",this.node);
this.ui_top = find("ui_top",this.node);
this.ui_title = find("ui_top/ui_title",this.node);
this.btnPause = find("ui_top/btnPause",this.node);
@ -81,7 +82,6 @@ export class Auto_BattleGame extends UIBase {
this.lb_money = find("ui_top/ui_money/lb_money",this.node)?.getComponent(Label);
this.ui_wave = find("ui_top/ui_wave",this.node);
this.lb_wave = find("ui_top/ui_wave/lb_wave",this.node)?.getComponent(Label);
this.ui_nextWaveTime = find("ui_top/ui_wave/ui_nextWaveTime",this.node);
this.ui_otherskill1List = find("ui_top/ui_otherskill1List",this.node);
this.ui_otherskill2List = find("ui_top/ui_otherskill2List",this.node);
this.ui_otherBag = find("ui_top/ui_otherBag",this.node);

@ -7,12 +7,14 @@ import { CoinFly } from './CoinFly';
const MAX_FLY_COUNT = 8;
/** 多枚依次起飞间隔(秒) */
const FLY_STAGGER_SEC = 0.05;
/** 波次结算金币飞向 btnCoin 的时长(秒),慢于击杀掉落 */
const WAVE_COIN_FLY_SEC = 0.7;
/** 波次结算每枚金币的生成间隔(秒) */
/** 波次结算每枚金币的冒出间隔(秒) */
const WAVE_COIN_SPAWN_GAP_SEC = 0.15;
/** 波次结算金币起点相对屏幕中心的世界 Y 偏移 */
const WAVE_COIN_START_OFFSET_Y = 100;
/** 波次金币冒出时的水平散开(世界像素) */
const WAVE_POP_SPREAD_X = 140;
/** 波次金币冒出时的垂直散开(世界像素) */
const WAVE_POP_SPREAD_Y = 70;
/** coinFly prefab 默认 scale=0.6;原放大×2,再缩小 40% */
const FLY_VISUAL_SCALE = 0.6 * 2 * 0.6;
/** +1 相对桃子图标右侧本地偏移(父节点已缩放) */
@ -39,8 +41,16 @@ export class BattlePeachCoinFlyPresenter {
private _waveLayer: Node | null = null;
/** 波次金币起点世界坐标 */
private readonly _waveStart = new Vec3();
/** 波次金币正在按间隔生成 */
private _waveSpawning = false;
/** 已冒出、尚未一起飞的波次金币 */
private readonly _waveCoins: Node[] = [];
/** 本轮已生成枚数 */
private _waveSpawned = 0;
/** 本轮已完成冒出的枚数 */
private _wavePopped = 0;
/** 间隔生成已结束,等全部冒出后再一起飞 */
private _waveSpawnFinished = false;
/** 正在冒出并等待一起飞 */
private _waveAwaitFly = false;
/** 绑定飞向目标(btnCoin/金币)与飞行层(ui_fly) */
bind(moneyNode: Node | null | undefined, flyLayer: Node | null | undefined): void {
@ -93,7 +103,7 @@ export class BattlePeachCoinFlyPresenter {
}
/**
* 波次结算:按次数生成金币挂到 ui_fly,每枚间隔后再出下一枚,从起点慢速飞向 btnCoin。
* 波次结算:金币在不同位置间隔冒出,全部冒完后一起飞向 btnCoin。
* @param flyLayer BattleGame.ui_fly
* @param startWorld 起点世界坐标
* @param showGold 单枚金币上的显示数量
@ -115,13 +125,16 @@ export class BattlePeachCoinFlyPresenter {
this._waveShowGold = amount;
this._waveLayer = flyLayer;
this._waveStart.set(startWorld.x, startWorld.y + WAVE_COIN_START_OFFSET_Y, startWorld.z);
this._waveSpawning = true;
this._waveSpawned = 0;
this._wavePopped = 0;
this._waveSpawnFinished = false;
this._waveAwaitFly = true;
this._spawnOneWaveCoin();
}
/** 生成一枚波次金币,剩余的等间隔再生成 */
/** 在散开位置冒出一枚波次金币,剩余的等间隔再冒 */
private _spawnOneWaveCoin(): void {
if (!this._waveSpawning || this._waveLeft <= 0) {
if (!this._waveAwaitFly || this._waveLeft <= 0) {
this._stopWaveSpawn();
return;
}
@ -131,37 +144,130 @@ export class BattlePeachCoinFlyPresenter {
this._stopWaveSpawn();
return;
}
const n = this._spawnCoinFlies(this._waveStart.clone(), 1, null, true, WAVE_COIN_FLY_SEC, layer, `+${this._waveShowGold}`, 1);
if (n <= 0) {
const world = new Vec3(
this._waveStart.x + (Math.random() - 0.5) * WAVE_POP_SPREAD_X,
this._waveStart.y + (Math.random() - 0.5) * WAVE_POP_SPREAD_Y,
this._waveStart.z,
);
const node = this._createCoinNode(layer, world, `+${this._waveShowGold}`);
if (!node) {
console.log('[BattlePeachCoinFly] 波次金币未生成');
this._stopWaveSpawn();
return;
}
const comp = node.getComponent(CoinFly);
if (!comp) {
console.log('[BattlePeachCoinFly] coinFly 缺少 CoinFly 组件');
this._recycleWaveCoin(node);
this._stopWaveSpawn();
return;
}
this._waveCoins.push(node);
this._waveSpawned++;
this._waveLeft--;
comp.popStay(this._onOneWavePopped);
if (this._waveLeft <= 0) {
this._clearWaveSpawn();
this._waveSpawnFinished = true;
this._tryFlyWaveTogether();
return;
}
tween(this).delay(WAVE_COIN_SPAWN_GAP_SEC).call(this._onWaveCoinGap).start();
}
/** 间隔结束,生成下一枚波次金币 */
/** 间隔结束,再冒出一枚波次金币 */
private readonly _onWaveCoinGap = (): void => {
this._spawnOneWaveCoin();
};
/** 停掉尚未生成的波次金币 */
/** 一枚波次金币冒出结束 */
private readonly _onOneWavePopped = (): void => {
if (!this._waveAwaitFly) return;
this._wavePopped++;
this._tryFlyWaveTogether();
};
/** 全部冒出后,一起飞向 btnCoin */
private _tryFlyWaveTogether(): void {
if (!this._waveAwaitFly || !this._waveSpawnFinished) return;
if (this._wavePopped < this._waveSpawned) return;
this._waveAwaitFly = false;
const money = this._moneyNode;
const end = money?.isValid ? money.worldPosition.clone() : null;
const coins = this._takeWaveCoins();
this._clearWaveSpawn();
if (!end) {
console.log('[BattlePeachCoinFly] 波次金币缺少飞向目标');
for (let i = 0; i < coins.length; i++) this._recycleWaveCoin(coins[i]);
return;
}
for (let i = 0; i < coins.length; i++) {
const node = coins[i];
if (!node?.isValid) continue;
const comp = node.getComponent(CoinFly);
if (!comp) {
this._recycleWaveCoin(node);
continue;
}
comp.flyTo(end, this._onPeachDone);
}
}
/**
* 取出一枚金币并挂上数字,先不飞。
* @param layer 父节点
* @param world 世界坐标
* @param plusText 金币上的数字
*/
private _createCoinNode(layer: Node, world: Readonly<Vec3>, plusText: string): Node | null {
if (!layer.active) layer.active = true;
const node = gg.res.getNode('prefab/', 'coinFly', GBundle.BattleGame);
if (!node?.isValid) {
console.log('[BattlePeachCoinFly] coinFly 节点池不足');
return null;
}
layer.addChild(node);
node.setScale(FLY_VISUAL_SCALE, FLY_VISUAL_SCALE, 1);
node.setWorldPosition(world.x, world.y, world.z);
this._attachPlus(node, plusText);
return node;
}
/**
* 回池一枚尚未飞走的波次金币。
* @param node 金币节点
*/
private _recycleWaveCoin(node: Node | null): void {
if (!node?.isValid) return;
Tween.stopAllByTarget(node);
this._putPlusChild(node);
gg.res.putNode(node);
}
/** 取出并清空等待一起飞的金币,避免清长度时把同一数组引用一起清空 */
private _takeWaveCoins(): Node[] {
const coins: Node[] = [];
for (let i = 0; i < this._waveCoins.length; i++) coins.push(this._waveCoins[i]);
this._waveCoins.length = 0;
return coins;
}
/** 停掉尚未一起飞的波次金币 */
private _stopWaveSpawn(): void {
Tween.stopAllByTarget(this);
this._waveAwaitFly = false;
const coins = this._takeWaveCoins();
for (let i = 0; i < coins.length; i++) this._recycleWaveCoin(coins[i]);
this._clearWaveSpawn();
}
/** 清空波次金币生成状态,不打断已飞出的金币 */
/** 清空波次金币生成状态,不回收已经开始飞行的金币 */
private _clearWaveSpawn(): void {
this._waveSpawning = false;
this._waveLeft = 0;
this._waveShowGold = 0;
this._waveLayer = null;
this._waveSpawned = 0;
this._wavePopped = 0;
this._waveSpawnFinished = false;
}
/**

@ -48,6 +48,70 @@ export class CoinFly extends Component {
.start();
}
/** 弹跳结束回调(只蹦不飞) */
private _afterPop: (() => void) | null = null;
/** 直飞结束回调 */
private _afterFly: ((host?: Node) => void) | null = null;
/** 弹跳结束 */
private readonly _finishPopStay = (): void => {
const done = this._afterPop;
this._afterPop = null;
done?.();
};
/** 直飞结束:回调后回池 */
private readonly _finishFlyTo = (): void => {
const node = this.node;
const done = this._afterFly;
this._afterFly = null;
done?.(node);
if (node?.isValid) gg.res.putNode(node);
};
/**
* 只蹦一下并停在落点,不飞向终点、不回池。
* @param onDone 弹跳结束
*/
popStay(onDone: (() => void) | null = null): void {
const node = this.node;
this._afterPop = onDone;
if (!node?.isValid) {
this._finishPopStay();
return;
}
Tween.stopAllByTarget(node);
const start = node.worldPosition.clone();
const peak = new Vec3(start.x, start.y + BOUNCE_WORLD_OFFSET_Y, start.z);
tween(node)
.to(0.2, { worldPosition: peak }, { easing: 'backOut' })
.to(0.2, { worldPosition: start }, { easing: 'backOut' })
.delay(0.3)
.call(this._finishPopStay)
.start();
}
/**
* 从当前位置飞向终点后回池。时长与击杀掉落的短飞行一致。
* @param endPos 终点世界坐标
* @param callfunc 到达回调(传入本节点)
* @param flySec 飞行时长(秒);≤0 用击杀掉落的短飞行
*/
flyTo(endPos: Vec3, callfunc: ((host?: Node) => void) | null = null, flySec = 0): void {
const node = this.node;
this._afterFly = callfunc;
if (!node?.isValid) {
this._finishFlyTo();
return;
}
Tween.stopAllByTarget(node);
const sec = flySec > 0 ? flySec : 0.1 + Math.random() * 0.1;
tween(node)
.to(sec, { worldPosition: endPos.clone() })
.call(this._finishFlyTo)
.start();
}
/**
* 从当前位置直接飘向目标(无蹦跳、无额外等待)。
* @param endPos 终点世界坐标

@ -512,7 +512,8 @@ export class UIBattleGame extends Auto_BattleGame {
}
/**
* 非首波倒计时开始时发一次金币。首波倒计时 waveIndex 仍是 -1。
* 非首波倒计时开始时,本机与 AI 各发一次金币。首波倒计时 waveIndex 仍是 -1。
* 飞金币只播本机。
* @param endedWaveIndex 刚结束、正在等待下一波的波次下标
* @param remain 倒计时剩余秒
*/
@ -527,6 +528,22 @@ export class UIBattleGame extends Auto_BattleGame {
battle.addPlayerGold(battle.localPlayerId, gold);
this._refreshPeachUi();
this._playWaveSettleCoin(gold);
this._grantWaveGoldToOthers(gold);
}
/**
* 非本机玩家槽入账同样的波次金币,不播飞金币。
* @param gold 本次入账金币
*/
private _grantWaveGoldToOthers(gold: number): void {
const battle = this._battle;
if (!battle) return;
const players = battle.session.players;
for (let i = 0; i < players.length; i++) {
const id = players[i].id;
if (id === battle.localPlayerId) continue;
battle.addPlayerGold(id, gold);
}
}
/**
@ -564,7 +581,7 @@ export class UIBattleGame extends Auto_BattleGame {
}
/**
* 按次数生成金币挂到 ui_fly,从屏幕中心上移后慢速飞向 btnCoin。
* 按次数生成金币挂到 ui_fly,先在不同位置间隔冒出,再一起飞向 btnCoin。
* 单枚显示为结算金币除以生成次数;入账仍是结算金币总额。
* @param gold 本次入账的金币总数
*/

Loading…
Cancel
Save