new dash implementation

This commit is contained in:
2026-01-30 17:06:33 +01:00
parent 3102e1b2bf
commit caa53d5ade
15 changed files with 9302 additions and 173 deletions

View File

@@ -21,18 +21,19 @@ namespace Beyond
[Header("Beyond's Dash Settings")]
public string dashBlendTreeState = "Dash_Directional";
public string dashHorizontalParam = "DashHorizontal";
public string dashVerticalParam = "DashVertical";
// Internal flag to lock animator parameters during dash
private bool _lockAnimParamsForDash = false;
[Tooltip("If your animation is slightly rotated (Red arrow offset), add an angle here to compensate (e.g. -45 or 45).")]
public float dashAngleCorrection = 0f;
[Tooltip("Print debug info to Console when dashing")]
public bool debugDash = true;
public bool GodMode
{
get => m_GodMode;
set
{
m_GodMode = value;
isImmortal = m_GodMode;
}
set { m_GodMode = value; isImmortal = m_GodMode; }
}
protected override bool canRecoverHealth
@@ -44,21 +45,13 @@ namespace Beyond
}
}
public bool IsDashingOrRolling()
{
return m_isDashing || isRolling;
}
public bool IsDashingOrRolling() => m_isDashing || isRolling;
protected override void Start()
{
base.Start();
}
protected override void RollBehavior()
{
base.RollBehavior();
}
public override void Roll()
{
Dash();
@@ -71,71 +64,59 @@ namespace Beyond
ReduceStamina(rollStamina, false);
currentStaminaRecoveryDelay = 2f;
// 1. Handle Stationary (Backwards Dash)
if (input.sqrMagnitude < 0.05f)
// Setup Camera Vectors
Transform camT = Camera.main != null ? Camera.main.transform : transform;
// Flatten camera vectors so looking up/down doesn't affect dash length
Vector3 camFwd = Vector3.Scale(camT.forward, new Vector3(1, 0, 1)).normalized;
Vector3 camRight = Vector3.Scale(camT.right, new Vector3(1, 0, 1)).normalized;
Vector3 targetWorldDir = Vector3.zero;
// 1. Determine Target World Direction
if (input.sqrMagnitude < 0.1f)
{
ApplyDashParams(0f, -1f);
// NO INPUT -> Move strictly "Into the Screen" (Towards Camera)
// This is the negative of the Camera Forward vector.
targetWorldDir = -camFwd;
if (debugDash) Debug.Log($"[Dash] No Input. Target: BACKWARD ({targetWorldDir})");
}
else
{
// 2. Handle Directional Input (Camera Relative -> Character Relative)
// DIRECTIONAL INPUT
// FIX: Use input.z for Forward/Backward, NOT input.y
targetWorldDir = (camFwd * input.z + camRight * input.x).normalized;
// A. Get the Input Direction relative to the Camera
Vector3 inputDir = Vector3.zero;
if (Camera.main != null)
{
// Convert joystick input (X,Y) into World Space based on Camera facing
Vector3 camFwd = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;
Vector3 camRight = Vector3.Scale(Camera.main.transform.right, new Vector3(1, 0, 1)).normalized;
inputDir = (camFwd * input.y + camRight * input.x).normalized;
}
else
{
// Fallback if no camera found
inputDir = new Vector3(input.x, 0, input.y).normalized;
}
// B. Convert that World Direction into the Character's Local Space
// This tells us: "Is the input to the Left, Right, or Forward of ME?"
Vector3 localDir = transform.InverseTransformDirection(inputDir);
// C. Send to Animator
ApplyDashParams(localDir.x, localDir.z);
if (debugDash) Debug.Log($"[Dash] Input: {input}. Target World Dir: {targetWorldDir}");
}
// 3. Play Animation
// 2. Convert World Direction to Character Local Space
// "Where is the Target World Dir relative to ME?"
Vector3 localDir = transform.InverseTransformDirection(targetWorldDir);
// 3. Apply Angle Correction (If animation is skewed 45 degrees)
if (dashAngleCorrection != 0f)
{
localDir = Quaternion.Euler(0, dashAngleCorrection, 0) * localDir;
}
// 4. Normalize (Ensure full blend magnitude)
localDir.y = 0;
localDir.Normalize();
// 5. Send to Animator (Dedicated Parameters)
animator.SetFloat(dashHorizontalParam, localDir.x);
animator.SetFloat(dashVerticalParam, localDir.z);
if (debugDash)
{
Debug.Log($"[Dash Final] Local X: {localDir.x} | Local Z: {localDir.z}");
}
// 6. Play Animation
animator.CrossFadeInFixedTime(dashBlendTreeState, rollTransition, baseLayer);
// 4. Lock parameters briefly so Invector doesn't overwrite them immediately
StartCoroutine(LockDashParamsRoutine());
}
// Helper to set params and lock the update loop
private void ApplyDashParams(float x, float y)
{
_lockAnimParamsForDash = true;
animator.SetFloat(vAnimatorParameters.InputHorizontal, x);
animator.SetFloat(vAnimatorParameters.InputVertical, y);
}
// Release the lock after a short delay (once the blend has firmly started)
private IEnumerator LockDashParamsRoutine()
{
yield return new WaitForSeconds(0.2f); // Adjust this if the blend pops back too soon
_lockAnimParamsForDash = false;
}
// --- CRITICAL FIX: Override Invector's Parameter Update ---
public override void UpdateAnimatorParameters()
{
// If we are locking parameters for the dash, DO NOT let the base class overwrite them.
if (_lockAnimParamsForDash) return;
// Otherwise, run standard Invector logic
base.UpdateAnimatorParameters();
}
// Standard overrides below...
// --- Standard Boilerplate Below ---
public void OnEvadeStart() { if (!m_GodMode) isImmortal = true; }
public void OnEvadeEnd() { if (!m_GodMode) isImmortal = false; }
public override void ActionsControl() { base.ActionsControl(); m_isDashing = IsAnimatorTag("IsDashing"); }
@@ -148,30 +129,18 @@ namespace Beyond
{
int deadLayer = 0;
var info = animatorStateInfos.GetStateInfoUsingTag("Dead");
if (info != null)
{
if (!animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.99f && groundDistance <= 0.15f) RemoveComponents();
}
if (info != null && !animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.99f && groundDistance <= 0.15f) RemoveComponents();
}
else if (deathBy == DeathBy.AnimationWithRagdoll)
{
int deadLayer = 0;
var info = animatorStateInfos.GetStateInfoUsingTag("Dead");
if (info != null)
{
if (!animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.8f) onActiveRagdoll.Invoke(null);
}
}
else if (deathBy == DeathBy.Ragdoll)
{
onActiveRagdoll.Invoke(null);
if (info != null && !animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.8f) onActiveRagdoll.Invoke(null);
}
else if (deathBy == DeathBy.Ragdoll) onActiveRagdoll.Invoke(null);
}
public void RemoveAnimatorTags()
{
animatorStateInfos.stateInfos.vToList().ForEach(infos => infos.tags.Clear());
}
public void RemoveAnimatorTags() => animatorStateInfos.stateInfos.vToList().ForEach(infos => infos.tags.Clear());
public override void ControlAnimatorRootMotion()
{
@@ -191,24 +160,17 @@ namespace Beyond
if (useRootMotion) MoveCharacter(moveDirection);
}
protected override void OnTriggerEnter(Collider other)
{
if (!m_ignoreTriggers) onActionEnter.Invoke(other);
}
protected override void OnTriggerEnter(Collider other) { if (!m_ignoreTriggers) onActionEnter.Invoke(other); }
protected override void OnTriggerStay(Collider other)
{
try { CheckForAutoCrouch(other); }
catch (UnityException e) { Debug.LogWarning(e.Message); }
try { CheckForAutoCrouch(other); } catch (UnityException e) { Debug.LogWarning(e.Message); }
if (!m_ignoreTriggers) base.OnTriggerStay(other);
}
protected override void OnTriggerExit(Collider other)
{
AutoCrouchExit(other);
if (!m_ignoreTriggers) base.OnTriggerExit(other);
}
void DrawWeaponLowLeft() { }
public override void ControlRotationType()
@@ -217,10 +179,7 @@ namespace Beyond
bool validInput = input != Vector3.zero || (isStrafing ? strafeSpeed.rotateWithCamera : freeSpeed.rotateWithCamera);
if (validInput)
{
if (lockAnimMovement)
{
inputSmooth = Vector3.Lerp(inputSmooth, input, (isStrafing ? strafeSpeed.movementSmooth : freeSpeed.movementSmooth) * Time.deltaTime);
}
if (lockAnimMovement) inputSmooth = Vector3.Lerp(inputSmooth, input, (isStrafing ? strafeSpeed.movementSmooth : freeSpeed.movementSmooth) * Time.deltaTime);
Vector3 dir = (isStrafing && isGrounded && (!isSprinting || sprintOnlyFree == false) || (freeSpeed.rotateWithCamera && input == Vector3.zero)) && rotateTarget ? rotateTarget.forward : moveDirection;
if (isStrafing || !useAnimationBasedRotation) RotateToDirection(dir);
}