Pronounced combat animation: 3-swing sword combo, per-class specials, sword in hand
BLENDER (5 new/re-authored clips, all full-body, mirrored-left-arm axes FIXED - Synty L shoulders mirror X/twist vs R, verified by axis probes): - A_Swing_R2L / A_Swing_L2R (0.47s): horizontal slash + backhand with real anticipation (torso wound 25 deg), 2-frame strikes, follow-through past the target. - A_Swing_Finisher (0.60s): overhead crash with a crouch drop into impact. - A_Special_Slam (0.53s): two-handed ground slam - the Warrior cone finally LOOKS like a cone attack. - A_Fire_OneHand re-authored punchier (cock -> full-extension thrust with torso commit -> recoil kick). Export lesson: Blender 4.4 slotted actions - reassigning animation_data. action does NOT bind the slot; force animation_data.action_slot before export/render or you bake a stale pose. CONTROLLER: MeleeSwing (one clip for everything) replaced by Swing1/2/3 keyed on new ComboStep int param (replicated MeleeCombo.Step); Fire gated !IsCone (Ranger shot), new SpecialSlam state on IsFiring+IsCone (Warrior). DRIVE SYSTEM: writes ComboStep from the replicated combo step and IsCone from the replicated AbilityRef -> AbilityDatabase blob archetype (works for remote teammates too). SWORD: SM_Wep_Sword_01 (SciFiSpace - same pack/atlas as the soldier) as a mesh-sub-asset child of Hand_R, grip rot (90,0,0) tuned live against the running entity. Required RigDefinitionAuthoring boneEntityStrippingMode Automatic -> None so the hand bone entity exists + is posed (stripped bones never animate attachments). Play-verified: the energy blade rides the fist. 456/456 EditMode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,8 @@ namespace ProjectM.Client
|
||||
static readonly FastAnimatorParameter k_IsAttacking = new FastAnimatorParameter("IsAttacking");
|
||||
static readonly FastAnimatorParameter k_IsFiring = new FastAnimatorParameter("IsFiring"); // Blender-authored A_Fire_OneHand (B7)
|
||||
static readonly FastAnimatorParameter k_IsDashing = new FastAnimatorParameter("IsDashing"); // Blender-authored A_Dash_Lean (local player only - DashState is not replicated)
|
||||
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
|
||||
|
||||
// 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
|
||||
@@ -72,12 +74,15 @@ namespace ProjectM.Client
|
||||
|
||||
// Current authoritative tick for the swing-window check (default = invalid -> IsAttacking stays false).
|
||||
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;
|
||||
|
||||
// --- LOCAL owner (CC velocity) ---
|
||||
var localJob = new LocalDriveJob
|
||||
{
|
||||
moveX = k_MoveX, moveZ = k_MoveZ, speed = k_Speed, isDead = k_IsDead,
|
||||
isAttacking = k_IsAttacking, isFiring = k_IsFiring, isDashing = k_IsDashing,
|
||||
comboStep = k_ComboStep, isCone = k_IsCone, abilityDb = abilityBlob,
|
||||
serverTick = serverTick, attackTicks = k_AttackAnimTicks,
|
||||
};
|
||||
Dependency = localJob.ScheduleParallel(Dependency);
|
||||
@@ -88,6 +93,7 @@ namespace ProjectM.Client
|
||||
{
|
||||
moveX = k_MoveX, moveZ = k_MoveZ, speed = k_Speed, isDead = k_IsDead,
|
||||
isAttacking = k_IsAttacking, isFiring = k_IsFiring, isDashing = k_IsDashing,
|
||||
comboStep = k_ComboStep, isCone = k_IsCone, abilityDb = abilityBlob,
|
||||
serverTick = serverTick, attackTicks = k_AttackAnimTicks,
|
||||
dt = dt,
|
||||
prevPos = _prevPos,
|
||||
@@ -139,7 +145,8 @@ namespace ProjectM.Client
|
||||
[WithPresent(typeof(Dead))]
|
||||
partial struct LocalDriveJob : IJobEntity
|
||||
{
|
||||
public FastAnimatorParameter moveX, moveZ, speed, isDead, isAttacking, isFiring, isDashing;
|
||||
public FastAnimatorParameter moveX, moveZ, speed, isDead, isAttacking, isFiring, isDashing, comboStep, isCone;
|
||||
public BlobAssetReference<AbilityDatabaseBlob> abilityDb;
|
||||
public NetworkTick serverTick;
|
||||
public uint attackTicks;
|
||||
|
||||
@@ -152,6 +159,7 @@ namespace ProjectM.Client
|
||||
in MeleeCombo melee,
|
||||
in AbilityCooldown fireCooldown,
|
||||
in EffectiveAbilityStats abilityStats,
|
||||
in AbilityRef abilityRef,
|
||||
in DashState dashState,
|
||||
EnabledRefRO<Dead> dead)
|
||||
{
|
||||
@@ -170,6 +178,14 @@ namespace ProjectM.Client
|
||||
dashActive = dStart.IsValid && dEnd.IsValid && !dStart.IsNewerThan(serverTick) && dEnd.IsNewerThan(serverTick);
|
||||
}
|
||||
if (a.HasParameter(isDashing)) a.SetParameterValue(isDashing, dashActive);
|
||||
if (a.HasParameter(comboStep)) a.SetParameterValue(comboStep, (int)melee.Step);
|
||||
bool cone = false;
|
||||
if (abilityDb.IsCreated)
|
||||
{
|
||||
ref var adb = ref abilityDb.Value;
|
||||
if (adb.TryGetAbility(abilityRef.Id, out var adef)) cone = adef.Archetype == (byte)AbilityArchetype.Cone;
|
||||
}
|
||||
if (a.HasParameter(isCone)) a.SetParameterValue(isCone, cone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +196,8 @@ namespace ProjectM.Client
|
||||
[WithPresent(typeof(Dead))]
|
||||
partial struct RemoteDriveJob : IJobEntity
|
||||
{
|
||||
public FastAnimatorParameter moveX, moveZ, speed, isDead, isAttacking, isFiring, isDashing;
|
||||
public FastAnimatorParameter moveX, moveZ, speed, isDead, isAttacking, isFiring, isDashing, comboStep, isCone;
|
||||
public BlobAssetReference<AbilityDatabaseBlob> abilityDb;
|
||||
public NetworkTick serverTick;
|
||||
public uint attackTicks;
|
||||
public float dt;
|
||||
@@ -197,6 +214,7 @@ namespace ProjectM.Client
|
||||
in MeleeCombo melee,
|
||||
in AbilityCooldown fireCooldown,
|
||||
in EffectiveAbilityStats abilityStats,
|
||||
in AbilityRef abilityRef,
|
||||
EnabledRefRO<Dead> dead)
|
||||
{
|
||||
seen.Add(e);
|
||||
@@ -213,6 +231,14 @@ namespace ProjectM.Client
|
||||
if (a.HasParameter(isFiring)) a.SetParameterValue(isFiring,
|
||||
FireActive(fireCooldown.NextFireTick, abilityStats.CooldownTicks, serverTick, attackTicks));
|
||||
if (a.HasParameter(isDashing)) a.SetParameterValue(isDashing, false); // DashState is not replicated to remotes
|
||||
if (a.HasParameter(comboStep)) a.SetParameterValue(comboStep, (int)melee.Step);
|
||||
bool cone = false;
|
||||
if (abilityDb.IsCreated)
|
||||
{
|
||||
ref var adb = ref abilityDb.Value;
|
||||
if (adb.TryGetAbility(abilityRef.Id, out var adef)) cone = adef.Archetype == (byte)AbilityArchetype.Cone;
|
||||
}
|
||||
if (a.HasParameter(isCone)) a.SetParameterValue(isCone, cone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user