Melee feel overhaul: SwordCombat anims at heavy cadence, salvage axe, damage-at-contact + buffer + dash law (guidelines forks 1-6)
07-18/19/20 arc: LightCombo01/HeavyCombo01 clips onto Swing1-3/Slam at ~natural speed (26-tick window, 30-tick recover, DPS-held damage retune), Menacing01 combat idle (InCombat), SM_Wep_Axe_Large_01 rigid-skinned to Hand_R at x1.25, LANTERN FX retone + blade-smear ribbon. Forks 1-6 per Combat_Attack_Feel_Guidelines (design review wf_000bc247): cleave resolves at the CONTACT tick (MeleeCleavePending schedule-and-consume, knob 31, 0=legacy; connect cues moved to contact), MeleeRange 2.2 + reach-only finisher mult 1.25 (knob 30), BufferedAttackTick GhostField input buffer (knob 29, unlock-edge validity), dash refused pre-contact (lookup-based). Tests: MeleeComboTests pin legacy knobs; +3 fork tests (414 green); live server proof: HP drop exactly at swing+16 under tick batching. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,10 +51,11 @@ namespace ProjectM.Client
|
||||
|
||||
static readonly FastAnimatorParameter k_ComboStep = new FastAnimatorParameter("ComboStep"); // replicated MeleeCombo.Step -> per-step swing clips (Swing1/2/3)
|
||||
static readonly FastAnimatorParameter k_IsCone = new FastAnimatorParameter("IsCone"); // ability archetype (replicated AbilityRef + blob): Cone -> two-hand slam, else the shot
|
||||
static readonly FastAnimatorParameter k_InCombat = new FastAnimatorParameter("InCombat"); // 07-18: Menacing01 combat idle -- swing recency (FeelConfig.CombatIdleHoldSec)
|
||||
|
||||
// Ticks after a swing-start that IsAttacking stays true (drives the MeleeSwing state). Kept < the swing lock
|
||||
// (MeleeRecoverTicks ~16) so a CHAINED swing re-pulses the bool false->true and re-triggers the Any State
|
||||
// transition per hit. ~0.22s @ 60Hz. Presentation-only.
|
||||
// (MeleeRecoverTicks ~30) so a CHAINED swing re-pulses the bool false->true and re-triggers the Any State
|
||||
// transition per hit. ~0.43s @ 60Hz (07-19 heavy retime). Presentation-only.
|
||||
const uint k_AttackAnimTicks = PlayerAimSystem.CastFacingTicks; // structurally tied (07-16 review): body-yaw cast-turn and the swing anim pulse must agree
|
||||
|
||||
// Remote prevPos cache (per ghost Entity). Pruned every frame (a vanished remote = a despawn).
|
||||
@@ -85,6 +86,8 @@ static readonly FastAnimatorParameter k_ComboStep = new FastAnimatorParameter(
|
||||
NetworkTick serverTick = SystemAPI.TryGetSingleton<NetworkTime>(out var nt) ? nt.ServerTick : default;
|
||||
// Ability blob for the per-class special read (Cone -> slam anim); default(BlobAssetReference) if absent.
|
||||
var abilityBlob = SystemAPI.TryGetSingleton<AbilityDatabase>(out var adbSingleton) ? adbSingleton.Value : default;
|
||||
// 07-18 combat idle: swing-recency window (the same wrap-safe SwingActive predicate as the anim pulse, wider). 60Hz sim ticks.
|
||||
uint combatIdleTicks = (uint)math.max(1f, FeelConfig.CombatIdleHoldSec * 60f);
|
||||
|
||||
// 07-16 banking: signed facing turn rate -> Bank (-1..1, + = bank right, leaning INTO the turn).
|
||||
// Sampled main-thread (a single local player), smoothed so the additive pose eases in and out.
|
||||
@@ -115,6 +118,7 @@ static readonly FastAnimatorParameter k_ComboStep = new FastAnimatorParameter(
|
||||
comboStep = k_ComboStep, isCone = k_IsCone, abilityDb = abilityBlob,
|
||||
serverTick = serverTick, attackTicks = k_AttackAnimTicks,
|
||||
bank = k_Bank, bankValue = _bankSmoothed,
|
||||
inCombat = k_InCombat, combatIdleTicks = combatIdleTicks,
|
||||
strideScale = k_StrideScale, trudgeNaturalSpeed = math.max(0.5f, FeelConfig.TrudgeNaturalSpeed),
|
||||
runNaturalSpeed = math.max(0.5f, FeelConfig.RunNaturalSpeed),
|
||||
};
|
||||
@@ -131,6 +135,7 @@ static readonly FastAnimatorParameter k_ComboStep = new FastAnimatorParameter(
|
||||
dt = dt,
|
||||
strideScale = k_StrideScale, trudgeNaturalSpeed = math.max(0.5f, FeelConfig.TrudgeNaturalSpeed),
|
||||
runNaturalSpeed = math.max(0.5f, FeelConfig.RunNaturalSpeed),
|
||||
inCombat = k_InCombat, combatIdleTicks = combatIdleTicks,
|
||||
prevPos = _prevPos,
|
||||
seen = seen,
|
||||
};
|
||||
@@ -162,6 +167,8 @@ static readonly FastAnimatorParameter k_ComboStep = new FastAnimatorParameter(
|
||||
{
|
||||
public FastAnimatorParameter bank;
|
||||
public float bankValue;
|
||||
public FastAnimatorParameter inCombat;
|
||||
public uint combatIdleTicks;
|
||||
public FastAnimatorParameter strideScale;
|
||||
public float trudgeNaturalSpeed;
|
||||
public float runNaturalSpeed;
|
||||
@@ -205,6 +212,7 @@ public FastAnimatorParameter moveX, moveZ, speed, isDead, isAttacking, isFiring,
|
||||
|
||||
if (a.HasParameter(comboStep)) a.SetParameterValue(comboStep, (int)melee.Step);
|
||||
if (a.HasParameter(isCone)) a.SetParameterValue(isCone, coneL);
|
||||
if (a.HasParameter(inCombat)) a.SetParameterValue(inCombat, TickWindowMath.SwingActive(melee, serverTick, combatIdleTicks));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +233,8 @@ public FastAnimatorParameter moveX, moveZ, speed, isDead, isAttacking, isFiring,
|
||||
public float runNaturalSpeed;
|
||||
public NativeParallelHashMap<Entity, float3> prevPos;
|
||||
public NativeParallelHashSet<Entity> seen;
|
||||
public FastAnimatorParameter inCombat;
|
||||
public uint combatIdleTicks;
|
||||
|
||||
void Execute(
|
||||
Entity e,
|
||||
@@ -257,6 +267,7 @@ public FastAnimatorParameter moveX, moveZ, speed, isDead, isAttacking, isFiring,
|
||||
a.SetParameterValue(strideScale, StrideScaleValue(math.length(vel.xz), stats.MoveSpeed, trudgeNaturalSpeed, runNaturalSpeed));
|
||||
if (a.HasParameter(comboStep)) a.SetParameterValue(comboStep, (int)melee.Step);
|
||||
if (a.HasParameter(isCone)) a.SetParameterValue(isCone, coneR);
|
||||
if (a.HasParameter(inCombat)) a.SetParameterValue(inCombat, TickWindowMath.SwingActive(melee, serverTick, combatIdleTicks));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user