飞剑效果

boxTest
humingxing 23 hours ago
parent 53f5aeba5e
commit a63e605506
  1. 5
      assets/script/battle/presentation/FlySwordAtkPresenter.ts
  2. 73
      assets/script/battle/presentation/SkillVisualChannel.ts

@ -20,6 +20,8 @@ interface FlySwordPending {
buff2: string; buff2: string;
/** 距发射(秒) */ /** 距发射(秒) */
wait: number; wait: number;
/** 发次(弧线上下交错) */
shotIndex: number;
/** 表现通道 */ /** 表现通道 */
channel: SkillVisualChannel; channel: SkillVisualChannel;
} }
@ -62,6 +64,7 @@ export class FlySwordHost {
buff1, buff1,
buff2, buff2,
wait: launched * Math.max(0, intervalSec), wait: launched * Math.max(0, intervalSec),
shotIndex: launched,
channel, channel,
}); });
launched += 1; launched += 1;
@ -84,7 +87,7 @@ export class FlySwordHost {
p.wait -= dt; p.wait -= dt;
if (p.wait > 0) continue; if (p.wait > 0) continue;
this._pending.splice(i, 1); this._pending.splice(i, 1);
if (!p.channel.launchFlySword(p.caster, p.abilityId, p.target, p.scale, p.buff1, p.buff2)) { if (!p.channel.launchFlySword(p.caster, p.abilityId, p.target, p.scale, p.buff1, p.buff2, p.shotIndex)) {
console.log('[FlySword] 发射失败', p.target); console.log('[FlySword] 发射失败', p.target);
} }
} }

@ -103,6 +103,12 @@ const WATER_BALL_SPEED = BOW_ATK_ARROW_SPEED;
const FLY_SWORD_PREFAB = '飞剑'; const FLY_SWORD_PREFAB = '飞剑';
/** 飞剑默认 Spine 动画 */ /** 飞剑默认 Spine 动画 */
const FLY_SWORD_ANIM = '飞剑'; const FLY_SWORD_ANIM = '飞剑';
/** 飞剑弧高占射程比例(弦中点上下偏移) */
const FLY_SWORD_ARC_T = 0.32;
/** 飞剑弧高下限(像素) */
const FLY_SWORD_ARC_MIN = 56;
/** 同向高阶剑弧高相对基础的递增值 */
const FLY_SWORD_ARC_TIER = 0.38;
/** battleHero/prefab/skill/玉帝飞剑 */ /** battleHero/prefab/skill/玉帝飞剑 */
const YUDI_FLY_SWORD_PREFAB = '玉帝飞剑'; const YUDI_FLY_SWORD_PREFAB = '玉帝飞剑';
/** 飞剑预制体默认动画(带光效) */ /** 飞剑预制体默认动画(带光效) */
@ -182,6 +188,49 @@ class WaterBallRecycle {
} }
} }
/**
* 飞剑飞行绑定:追踪落点 + 落地回池。
*/
class FlySwordRecycle {
private static readonly _pool: FlySwordRecycle[] = [];
private _channel: SkillVisualChannel | null = null;
private _node: Node | null = null;
private _target: EntityId = 0;
/** 每帧刷新终点;目标失效则沿用上次 */
readonly resolveToWorld = (_node: Node): Vec3 | null => {
if (!(this._target > 0)) return null;
return SkillPresentationEnv.getTargetWorldPos(this._target);
};
/** 落地:回池飞剑 */
readonly onLand = (): void => {
const channel = this._channel;
const node = this._node;
this._channel = null;
this._node = null;
this._target = 0;
FlySwordRecycle._pool.push(this);
if (channel && node) channel.recycleWaterBall(node);
};
/**
* 取得绑定。
* @param channel 表现通道
* @param node 飞剑
* @param target 锁定目标
*/
static obtain(channel: SkillVisualChannel, node: Node, target: EntityId): FlySwordRecycle {
const rec = FlySwordRecycle._pool.length > 0
? FlySwordRecycle._pool.pop()!
: new FlySwordRecycle();
rec._channel = channel;
rec._node = node;
rec._target = target;
return rec;
}
}
/** 待发射的弓箭 */ /** 待发射的弓箭 */
interface BowArrowLaunch { interface BowArrowLaunch {
/** 代数,过期则丢弃 */ /** 代数,过期则丢弃 */
@ -1382,16 +1431,17 @@ export class SkillVisualChannel {
} }
/** /**
* 飞剑武器主动:实例化「飞剑」直线飞向目标,落地结算。 * 飞剑武器主动:弧线追踪飞向目标;多发时上下弧高交错。
* @param caster 施法者 * @param caster 施法者
* @param abilityId Ability id * @param abilityId Ability id
* @param target 锁定目标 * @param target 锁定目标
* @param scale 伤害倍率 * @param scale 伤害倍率
* @param buff1 atk_buff1 * @param buff1 atk_buff1
* @param buff2 atk_buff2 * @param buff2 atk_buff2
* @param shotIndex 发次(0 起,奇偶上下弧)
* @returns 是否发出 * @returns 是否发出
*/ */
launchFlySword(caster: EntityId, abilityId: string, target: EntityId, scale: number, buff1: string, buff2: string): boolean { launchFlySword(caster: EntityId, abilityId: string, target: EntityId, scale: number, buff1: string, buff2: string, shotIndex = 0): boolean {
const fromNode = this._weapon.skinAnim?.node ?? this._weapon.aim ?? this._weapon.root; const fromNode = this._weapon.skinAnim?.node ?? this._weapon.aim ?? this._weapon.root;
const fromWorld = fromNode?.isValid const fromWorld = fromNode?.isValid
? fromNode.worldPosition ? fromNode.worldPosition
@ -1411,17 +1461,19 @@ export class SkillVisualChannel {
const duration = Math.max(0.12, dist / WATER_BALL_SPEED); const duration = Math.max(0.12, dist / WATER_BALL_SPEED);
const node = this._spawnFlySwordPrefab(this._arrowFrom); const node = this._spawnFlySwordPrefab(this._arrowFrom);
if (!node) return false; if (!node) return false;
const arcHeight = this._flySwordArcHeight(dist, shotIndex);
const land = FlySwordRecycle.obtain(this, node, target);
SkillTransformDriver.animateParabola({ SkillTransformDriver.animateParabola({
node, node,
duration, duration,
fromWorld: this._arrowFrom, fromWorld: this._arrowFrom,
toWorld: this._arrowTo, toWorld: this._arrowTo,
arcHeight: 0, arcHeight,
resolveToWorld: land.resolveToWorld,
easing: SkillTransformDriver.easeLinear, easing: SkillTransformDriver.easeLinear,
faceTangent: true, faceTangent: true,
}); });
SkillTransformDriver.stepNow(); SkillTransformDriver.stepNow();
const land = WaterBallRecycle.obtain(this, node);
getBowAtkHost().spawnVolley({ getBowAtkHost().spawnVolley({
caster, caster,
target, target,
@ -1435,6 +1487,19 @@ export class SkillVisualChannel {
return true; return true;
} }
/**
* 飞剑弧高:奇偶上下交错,同向越高阶弧越大。
* @param dist 射程
* @param shotIndex 发次
*/
private _flySwordArcHeight(dist: number, shotIndex: number): number {
const idx = Math.max(0, Math.floor(shotIndex));
const base = Math.max(FLY_SWORD_ARC_MIN, dist * FLY_SWORD_ARC_T);
const sign = (idx % 2 === 0) ? 1 : -1;
const tier = Math.floor(idx / 2);
return sign * base * (1 + tier * FLY_SWORD_ARC_TIER);
}
launchVolleyShot(caster: EntityId, abilityId: string, target: EntityId, scale: number, buff1: string, buff2: string, shotIndex: number, aimWorld: Readonly<Vec3>): boolean { launchVolleyShot(caster: EntityId, abilityId: string, target: EntityId, scale: number, buff1: string, buff2: string, shotIndex: number, aimWorld: Readonly<Vec3>): boolean {
const castAnim = String(this._castAnimParams?.animation ?? '').trim(); const castAnim = String(this._castAnimParams?.animation ?? '').trim();
if (this._isStaffWeapon() || this._isStaffProjectileAnim(castAnim)) { if (this._isStaffWeapon() || this._isStaffProjectileAnim(castAnim)) {

Loading…
Cancel
Save