2fcff9a7a1
Extends the DR-022 player pipeline to Husk enemies. A Husk is an ownerless interpolated ghost = structurally a remote player, so the new client-only EnemyAnimationDriveSystem mirrors PlayerAnimationDriveSystem's remote path: velocity from LocalTransform-delta (prevPos cache, pruned every frame), facing from LocalTransform.Rotation (AnimParamMath.PlanarForward), maxSpeed from baked EnemyStats, IsAttacking from the already-replicated AttackWindup telegraph. No new [GhostField], no server/asmdef/ghost-hash change. Monster-mash roster: Werewolf (Grunt), Werewolf-Undead (Swarmer), Kaiju (Brute), built by the reusable, GUID-preserving EnemyRigTools editor tool (materials + AC_EnemyTopDown + EnemyAttackWindup clip + 3 rigged prefabs). WaveSystem now preserves the baked variant Scale (was reset to 1 by LocalTransform.FromPosition). See DR-023. EditMode 208/208; validated in Play (rigs skin, scales replicate, locomotion + attack telegraph drive correctly). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
2.3 KiB
C#
44 lines
2.3 KiB
C#
using Unity.Mathematics;
|
|
|
|
namespace ProjectM.Simulation
|
|
{
|
|
/// <summary>
|
|
/// Pure mapping from authoritative planar velocity + facing to client animation blend params.
|
|
/// Forward (along facing) -> MoveZ+, right of facing -> MoveX+ (matches the slim controller's 2D
|
|
/// strafe tree: +X=right, +Z=forward). EditMode-tested, no World needed. Burst-safe (no enums/strings).
|
|
/// </summary>
|
|
public static class AnimParamMath
|
|
{
|
|
/// <param name="planarVelocity">World velocity (KinematicCharacterBody.RelativeVelocity for the owner,
|
|
/// or LocalTransform position-delta/dt for remotes). Y is ignored.</param>
|
|
/// <param name="facing">Normalized world planar facing (PlayerFacing.Direction, world XZ as x,y). May be zero.</param>
|
|
/// <param name="maxSpeed">EffectiveCharacterStats.MoveSpeed (normalizer; guarded > 0).</param>
|
|
/// <returns>x = MoveX (strafe, -1..1), y = MoveZ (fwd/back, -1..1), z = Speed (0..1).</returns>
|
|
public static float3 LocomotionParams(float3 planarVelocity, float2 facing, float maxSpeed)
|
|
{
|
|
float2 vWorld = planarVelocity.xz;
|
|
float safeMax = math.max(maxSpeed, 1e-4f);
|
|
float speed = math.saturate(math.length(vWorld) / safeMax);
|
|
|
|
// Degenerate facing -> default to world +Z so the basis is well-defined.
|
|
float2 fwd = math.lengthsq(facing) > 1e-6f ? math.normalize(facing) : new float2(0f, 1f);
|
|
float2 right = new float2(fwd.y, -fwd.x); // 90deg clockwise in XZ (right-hand of forward)
|
|
|
|
float2 local = new float2(math.dot(vWorld, right), math.dot(vWorld, fwd)) / safeMax;
|
|
local = math.clamp(local, -1f, 1f);
|
|
return new float3(local.x, local.y, speed);
|
|
}
|
|
/// <summary>
|
|
/// Planar (XZ) forward from a world rotation, normalized. Degenerate -> world +Z. Used as the facing
|
|
/// for enemies (the server writes LocalTransform.Rotation toward the target each tick in EnemyAISystem).
|
|
/// </summary>
|
|
public static float2 PlanarForward(quaternion rot)
|
|
{
|
|
float3 f = math.mul(rot, new float3(0f, 0f, 1f));
|
|
float2 p = f.xz;
|
|
return math.lengthsq(p) > 1e-6f ? math.normalize(p) : new float2(0f, 1f);
|
|
}
|
|
|
|
}
|
|
}
|