|
|
|
@ -7,12 +7,14 @@ import { CoinFly } from './CoinFly'; |
|
|
|
const MAX_FLY_COUNT = 8; |
|
|
|
const MAX_FLY_COUNT = 8; |
|
|
|
/** 多枚依次起飞间隔(秒) */ |
|
|
|
/** 多枚依次起飞间隔(秒) */ |
|
|
|
const FLY_STAGGER_SEC = 0.05; |
|
|
|
const FLY_STAGGER_SEC = 0.05; |
|
|
|
/** 波次结算金币飞向 btnCoin 的时长(秒),慢于击杀掉落 */ |
|
|
|
/** 波次结算每枚金币的冒出间隔(秒) */ |
|
|
|
const WAVE_COIN_FLY_SEC = 0.7; |
|
|
|
|
|
|
|
/** 波次结算每枚金币的生成间隔(秒) */ |
|
|
|
|
|
|
|
const WAVE_COIN_SPAWN_GAP_SEC = 0.15; |
|
|
|
const WAVE_COIN_SPAWN_GAP_SEC = 0.15; |
|
|
|
/** 波次结算金币起点相对屏幕中心的世界 Y 偏移 */ |
|
|
|
/** 波次结算金币起点相对屏幕中心的世界 Y 偏移 */ |
|
|
|
const WAVE_COIN_START_OFFSET_Y = 100; |
|
|
|
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% */ |
|
|
|
/** coinFly prefab 默认 scale=0.6;原放大×2,再缩小 40% */ |
|
|
|
const FLY_VISUAL_SCALE = 0.6 * 2 * 0.6; |
|
|
|
const FLY_VISUAL_SCALE = 0.6 * 2 * 0.6; |
|
|
|
/** +1 相对桃子图标右侧本地偏移(父节点已缩放) */ |
|
|
|
/** +1 相对桃子图标右侧本地偏移(父节点已缩放) */ |
|
|
|
@ -39,8 +41,16 @@ export class BattlePeachCoinFlyPresenter { |
|
|
|
private _waveLayer: Node | null = null; |
|
|
|
private _waveLayer: Node | null = null; |
|
|
|
/** 波次金币起点世界坐标 */ |
|
|
|
/** 波次金币起点世界坐标 */ |
|
|
|
private readonly _waveStart = new Vec3(); |
|
|
|
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) */ |
|
|
|
/** 绑定飞向目标(btnCoin/金币)与飞行层(ui_fly) */ |
|
|
|
bind(moneyNode: Node | null | undefined, flyLayer: Node | null | undefined): void { |
|
|
|
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 flyLayer BattleGame.ui_fly |
|
|
|
* @param startWorld 起点世界坐标 |
|
|
|
* @param startWorld 起点世界坐标 |
|
|
|
* @param showGold 单枚金币上的显示数量 |
|
|
|
* @param showGold 单枚金币上的显示数量 |
|
|
|
@ -115,13 +125,16 @@ export class BattlePeachCoinFlyPresenter { |
|
|
|
this._waveShowGold = amount; |
|
|
|
this._waveShowGold = amount; |
|
|
|
this._waveLayer = flyLayer; |
|
|
|
this._waveLayer = flyLayer; |
|
|
|
this._waveStart.set(startWorld.x, startWorld.y + WAVE_COIN_START_OFFSET_Y, startWorld.z); |
|
|
|
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(); |
|
|
|
this._spawnOneWaveCoin(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** 生成一枚波次金币,剩余的等间隔再生成 */ |
|
|
|
/** 在散开位置冒出一枚波次金币,剩余的等间隔再冒 */ |
|
|
|
private _spawnOneWaveCoin(): void { |
|
|
|
private _spawnOneWaveCoin(): void { |
|
|
|
if (!this._waveSpawning || this._waveLeft <= 0) { |
|
|
|
if (!this._waveAwaitFly || this._waveLeft <= 0) { |
|
|
|
this._stopWaveSpawn(); |
|
|
|
this._stopWaveSpawn(); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -131,37 +144,130 @@ export class BattlePeachCoinFlyPresenter { |
|
|
|
this._stopWaveSpawn(); |
|
|
|
this._stopWaveSpawn(); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
const n = this._spawnCoinFlies(this._waveStart.clone(), 1, null, true, WAVE_COIN_FLY_SEC, layer, `+${this._waveShowGold}`, 1); |
|
|
|
const world = new Vec3( |
|
|
|
if (n <= 0) { |
|
|
|
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] 波次金币未生成'); |
|
|
|
console.log('[BattlePeachCoinFly] 波次金币未生成'); |
|
|
|
this._stopWaveSpawn(); |
|
|
|
this._stopWaveSpawn(); |
|
|
|
return; |
|
|
|
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--; |
|
|
|
this._waveLeft--; |
|
|
|
|
|
|
|
comp.popStay(this._onOneWavePopped); |
|
|
|
if (this._waveLeft <= 0) { |
|
|
|
if (this._waveLeft <= 0) { |
|
|
|
this._clearWaveSpawn(); |
|
|
|
this._waveSpawnFinished = true; |
|
|
|
|
|
|
|
this._tryFlyWaveTogether(); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
tween(this).delay(WAVE_COIN_SPAWN_GAP_SEC).call(this._onWaveCoinGap).start(); |
|
|
|
tween(this).delay(WAVE_COIN_SPAWN_GAP_SEC).call(this._onWaveCoinGap).start(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** 间隔结束,生成下一枚波次金币 */ |
|
|
|
/** 间隔结束,再冒出一枚波次金币 */ |
|
|
|
private readonly _onWaveCoinGap = (): void => { |
|
|
|
private readonly _onWaveCoinGap = (): void => { |
|
|
|
this._spawnOneWaveCoin(); |
|
|
|
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 { |
|
|
|
private _stopWaveSpawn(): void { |
|
|
|
Tween.stopAllByTarget(this); |
|
|
|
Tween.stopAllByTarget(this); |
|
|
|
|
|
|
|
this._waveAwaitFly = false; |
|
|
|
|
|
|
|
const coins = this._takeWaveCoins(); |
|
|
|
|
|
|
|
for (let i = 0; i < coins.length; i++) this._recycleWaveCoin(coins[i]); |
|
|
|
this._clearWaveSpawn(); |
|
|
|
this._clearWaveSpawn(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** 清空波次金币生成状态,不打断已飞出的金币 */ |
|
|
|
/** 清空波次金币生成状态,不回收已经开始飞行的金币 */ |
|
|
|
private _clearWaveSpawn(): void { |
|
|
|
private _clearWaveSpawn(): void { |
|
|
|
this._waveSpawning = false; |
|
|
|
|
|
|
|
this._waveLeft = 0; |
|
|
|
this._waveLeft = 0; |
|
|
|
this._waveShowGold = 0; |
|
|
|
this._waveShowGold = 0; |
|
|
|
this._waveLayer = null; |
|
|
|
this._waveLayer = null; |
|
|
|
|
|
|
|
this._waveSpawned = 0; |
|
|
|
|
|
|
|
this._wavePopped = 0; |
|
|
|
|
|
|
|
this._waveSpawnFinished = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
|