From a148b980169a42f0ada2ed4d22300752ce465b63 Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 6 Jul 2025 23:32:18 -0700 Subject: [PATCH] Hone jump and dash delays tied to attacking and to each other --- .../Characters/Player/Classes/ClassBase.cs | 62 +++++++- .../Player/Classes/MeleeFighterClass.cs | 42 ++++-- .../Characters/Player/PlayerMainController.cs | 132 +++++++++++++++--- 3 files changed, 199 insertions(+), 37 deletions(-) diff --git a/Assets/Scripts/Runtime/Characters/Player/Classes/ClassBase.cs b/Assets/Scripts/Runtime/Characters/Player/Classes/ClassBase.cs index 53322a4..cbc737e 100644 --- a/Assets/Scripts/Runtime/Characters/Player/Classes/ClassBase.cs +++ b/Assets/Scripts/Runtime/Characters/Player/Classes/ClassBase.cs @@ -1,12 +1,18 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; using System.Text; +using System.Threading; using System.Threading.Tasks; +using Unity.VisualScripting; using UnityEngine; +[System.Serializable] public class ClassBase { + [HideInInspector] public float TimeElapsed; [HideInInspector] public int MaxHealth; [HideInInspector] public int MaxMana; [HideInInspector] public float AttackPower; @@ -20,6 +26,8 @@ public class ClassBase { protected FloatingTextSpawner TextPopUp; /// Always in the order of Up, Down, Left, Right public string AnimationToPlay = ""; + public Dictionary Skills = new Dictionary(); + [SerializeField] private List AvailableSkills = new List(); public ClassBase(Player player) { Player = player; @@ -28,7 +36,59 @@ public class ClassBase { TextPopUp = new FloatingTextSpawner(player.transform); } - virtual public void Tick() { } + public void GenerateAvailableSkillsList() { + AvailableSkills.AddRange(Skills.Values); + } + + virtual public void Tick() { + TimeElapsed = Time.time; + ClassSkill.TimeElapsed = TimeElapsed; + AvailableSkills.ForEach((x) => x.UpdateCooldown()); + foreach (var skill in Skills.Values) { skill.UpdateCooldown(); } + } virtual public void HandlePrimaryAttack() { } virtual public void HandleSecondaryAttack() { } + + + [Serializable] + public class ClassSkill { + public static float TimeElapsed; + public string Name { get; private set; } + public float CooldownTime { get; private set; } + public float CooldownTimeElapsed { get; private set; } + public float CooldownRef { get; private set; } + public int SkillCountMax { get; private set; } + public int SkillCount { get; private set; } = 0; + public bool IsPaused { get; set; } + public bool IsNotReady { get { return SkillCount == 0; } } + protected bool SkillCountMaxed { get { return SkillCount == SkillCountMax; } } + protected float CooldownLeftInRatio { get { return SkillCountMaxed ? 1 : (CooldownTimeElapsed / CooldownTime); } } + + + public ClassSkill(string skillName, float cooldownTime, int skillCount) { + Name = skillName; + CooldownTime = cooldownTime; + SkillCountMax = skillCount; + } + + public bool IsReady(bool useSkill = true) { + if (SkillCount == 0) return false; + if (useSkill) { + if (SkillCountMaxed) CooldownRef = Time.time; + SkillCount--; + Debug.Log($"[{Name}] Using skill [{SkillCount}/{SkillCountMax}]"); + } + return true; + } + + + public void UpdateCooldown() { + if (SkillCount == SkillCountMax) return; + CooldownTimeElapsed = TimeElapsed - CooldownRef; + if (CooldownTimeElapsed < CooldownTime) return; + SkillCount++; + CooldownRef = Time.time; + Debug.Log($"[{Name}] Updating skill count [{SkillCount}/{SkillCountMax}]"); + } + } } diff --git a/Assets/Scripts/Runtime/Characters/Player/Classes/MeleeFighterClass.cs b/Assets/Scripts/Runtime/Characters/Player/Classes/MeleeFighterClass.cs index 5f6cb69..2e0c1ad 100644 --- a/Assets/Scripts/Runtime/Characters/Player/Classes/MeleeFighterClass.cs +++ b/Assets/Scripts/Runtime/Characters/Player/Classes/MeleeFighterClass.cs @@ -1,4 +1,7 @@ -using UnityEngine; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Unity.VisualScripting; +using UnityEngine; [System.Serializable] public class MeleeFighterClass : ClassBase { @@ -10,16 +13,19 @@ public class MeleeFighterClass : ClassBase { public float ChargeValue; public bool AllowBladeVortex; public int ChargeTick; - public float TimeElapsed; + public float ComboTimeElapsed; public float Cooldown; public bool TimesUp; - private bool HandlingOnStateEnter; + public VfxHandlerBase VfxKineticSurgeHandler { get { return Player.VfxKineticSurgeHandler; } } public MeleeFighterClass(Player player) : base(player) { - + Skills.Add((int)AttackState.PhaseCleave, new ClassSkill("PhaseCleave", 2f, 3)); + Skills.Add((int)AttackState.BladeVortex, new ClassSkill("BladeVortex", 2f, 3)); + Skills.Add((int)AttackState.Shockwave, new ClassSkill("Shockwave", 2f, 3)); + GenerateAvailableSkillsList(); } @@ -43,8 +49,10 @@ public class MeleeFighterClass : ClassBase { override public void Tick() { - TimeElapsed = Time.time - LastComboTime; - TimesUp = TimeElapsed > ComboResetTime; + base.Tick(); + + ComboTimeElapsed = Time.time - LastComboTime; + TimesUp = ComboTimeElapsed > ComboResetTime; switch (CurrentState) { case AttackState.None: @@ -52,7 +60,7 @@ public class MeleeFighterClass : ClassBase { return; case AttackState.BasicAttack1: - AllowBladeVortex = TimeElapsed <= 0.1f; + AllowBladeVortex = ComboTimeElapsed <= 0.1f; break; @@ -70,7 +78,7 @@ public class MeleeFighterClass : ClassBase { // break; } - if (CurrentState == AttackState.BasicAttack3 && TimeElapsed > 0.1) { + if (CurrentState == AttackState.BasicAttack3 && ComboTimeElapsed > 0.1) { ChangeState(AttackState.KineticSurgeRelease); AttackAnimator.MeleeResetKineticCharge(); AttackAnimator.MeleeKineticSurge(); @@ -80,7 +88,7 @@ public class MeleeFighterClass : ClassBase { //Player.MoveSpeed = PlayerOriginalSpeed; } - if (!ChargingAnAttack && TimesUp) { + if (!ChargingAnAttack && TimesUp || Player.IsJumping) { Player.MoveSpeedDampener = 1; ChangeState(AttackState.None); AttackAnimator.MeleeResetKineticCharge(); @@ -118,9 +126,10 @@ public class MeleeFighterClass : ClassBase { if ((Time.time - Player.DashTime) <= 0.1f) ChangeState(AttackState.PhaseCleave, 1.5f); - else if (Player.IsJumping) - ChangeState(AttackState.Shockwave, 0); - else + else if (Player.IsJumping) { + if (Player.ActionAfterJumpReady) + ChangeState(AttackState.Shockwave, 0); + } else ChangeState(AttackState.BasicAttack1, 0.35f); break; @@ -145,7 +154,7 @@ public class MeleeFighterClass : ClassBase { switch (CurrentState) { case AttackState.None: /*PlayerOriginalSpeed = Player.MoveSpeed*/ - + if (Player.IsDashing) ChangeState(AttackState.None); @@ -158,6 +167,10 @@ public class MeleeFighterClass : ClassBase { case AttackState.BasicAttack1: if (!AllowBladeVortex) return; + if (!Skills[(int)AttackState.BladeVortex].IsReady()) { + ChangeState(AttackState.None); + return; + } Cooldown = 0; ChangeState(AttackState.BladeVortex, 0.4f, 1.5f); break; @@ -182,7 +195,7 @@ public class MeleeFighterClass : ClassBase { float resetTime = 0.3f, float cooldown = 0.05f) { - if (TimeElapsed < Cooldown) return; + if (ComboTimeElapsed < Cooldown) return; CurrentState = state; Player.MoveSpeedDampener = (state == AttackState.None) ? 1 : 1 / decreasedSpeed; if (state == AttackState.None) return; @@ -232,6 +245,7 @@ public class MeleeFighterClass : ClassBase { } } + public void RotateTowardsMouse() { return; Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); diff --git a/Assets/Scripts/Runtime/Characters/Player/PlayerMainController.cs b/Assets/Scripts/Runtime/Characters/Player/PlayerMainController.cs index e1d640f..0dcb03d 100644 --- a/Assets/Scripts/Runtime/Characters/Player/PlayerMainController.cs +++ b/Assets/Scripts/Runtime/Characters/Player/PlayerMainController.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Linq; using UnityEditor; using UnityEngine; +using UnityEngine.EventSystems; using UnityEngine.UI; [SelectionBase] @@ -10,7 +11,7 @@ public class Player : MonoBehaviour { [SerializeField] public BuilderManager Builder; [Header("Movement")] - [SerializeField] private Rigidbody2D RigidBody; + [SerializeField] private Rigidbody2D Rigidbody; [SerializeField] public Animator Animator; [SerializeField] private SpriteRenderer Renderer; @@ -24,9 +25,14 @@ public class Player : MonoBehaviour { [Header("Movement Attributes")] [SerializeField] public float MoveSpeed = 8; [SerializeField] public float MoveSpeedDampener = 1; - [SerializeField] private float DashMultiplier = 60; - [SerializeField] private float DriftSpeed = 60; - [SerializeField] private float DriftFactorial = 0.85f; + [SerializeField] private float DashSpeedInitial = 200; + [SerializeField] private float DashSpeed = 0; + [SerializeField] private float DashDecayRate = 30; + [SerializeField] private float DashDelay1 = 0.05f; + [SerializeField] private float DashDelay2 = 0.05f; + [SerializeField] private float DashCooldown = 0.1f; + //[SerializeField] private float DriftSpeed = 60; + //[SerializeField] private float DriftFactorial = 0.85f; [SerializeField] private float JumpDelay = 0.3f; [Header("VFX")] @@ -42,11 +48,21 @@ public class Player : MonoBehaviour { public Vector2 PrevDirection = Vector2.zero; private Vector2 MoveDirection = Vector2.zero; + private Vector2 DashDirection = Vector2.zero; private Directions FaceDir = Directions.Down; public bool IsJumping { get; set; } + public float LastJumpTime { get; private set; } + public Coroutine CoroutineJumpReset { get; private set; } + + public bool ActionAfterJumpReady { + get { + return (!IsJumping || (Time.time - LastJumpTime > 0.07f)); + } + } public bool IsDashing { get; private set; } public float DashTime { get; private set; } + public bool CanDash { get; private set; } = true; public Vector2 DriftDirection { get; private set; } = Vector2.zero; private float Drift = 0; @@ -65,8 +81,8 @@ public class Player : MonoBehaviour { } private void Update() { - GatherInput(); KeyPressActions(); + GatherInput(); ActiveClass.Tick(); } @@ -77,7 +93,7 @@ public class Player : MonoBehaviour { private void KeyPressActions() { if (Input.GetKeyDown(KeyCode.LeftShift) || Input.GetKeyDown(KeyCode.RightShift)) { - Dash(MoveDirection); + DoDash(); } else if (!IsJumping && Input.GetKeyDown(KeyCode.Space)) { Jump(); } else if (Input.GetKeyDown(KeyCode.F1)) { @@ -95,25 +111,77 @@ public class Player : MonoBehaviour { else if (Input.GetKeyDown(KeyCode.C)) ActiveClass.HandleSecondaryAttack(); } + private void DoDash() { + if (!CanDash) return; + if (!ActionAfterJumpReady) return; + CanDash = false; + IsDashing = true; + DashDirection = MoveDirection.normalized; + DashSpeed = DashSpeedInitial; + //StartCoroutine(DoDash(MoveDirection)); + } + private void Dash(Vector2 direction) { - if (Drift > 0.5f) return; + if (direction == Vector2.zero) return; + //if (Drift > 0.5f) return; + if (!ActionAfterJumpReady) return; IsDashing = true; DashTime = Time.time; - RigidBody.linearVelocity = (direction.normalized * MoveSpeed * DashMultiplier) / MoveSpeedDampener; + + //Rigidbody.linearVelocity = direction.normalized * speed; float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg + 180; if (direction.normalized != Vector2.zero) VfxDashHandler.PlayAll(this.transform.position, Quaternion.Euler(0f, 0f, angle)); DriftDirection = direction.normalized; - Drift = DriftSpeed; + //Drift = speed; + //Rigidbody.AddForce(direction * speed, ForceMode2D.Impulse); + + //Rigidbody.AddForce(direction.normalized * speed, ForceMode.Acceleration); IsDashing = false; } + IEnumerator DoDash(Vector2 direction) { + + var startTime = Time.time; + var speed = MoveSpeed * DashSpeedInitial / MoveSpeedDampener; + //if (IsJumping) speed /= 2; + Rigidbody.linearVelocity = Vector2.zero; + //Rigidbody.AddForce(direction * speed, ForceMode2D.Impulse); + var mag = (direction.normalized * speed).magnitude; + Rigidbody.linearVelocity = direction.normalized * speed; + var isJumping = IsJumping; + + if (isJumping) Jump(); + while ((Time.time - startTime) < DashDelay1) + yield return null; + //yield return new WaitForSeconds(DashDelay1); + Rigidbody.linearVelocity = Vector2.zero; + var endTime = Time.time; + Debug.Log($"[{Mathf.Abs(mag):0.00}] Time elapsed {(endTime - startTime):0.000}"); + + yield return new WaitForSeconds(DashDelay2); + IsDashing = false; + + yield return new WaitForSeconds(DashCooldown); + while (IsJumping) + yield return null; + + CanDash = true; + } + + private void Jump() { + if (SkillInUse) return; IsJumping = true; + LastJumpTime = Time.time; foreach (var col in BoxColliders) col.enabled = false; - StartCoroutine(ResetJumpAfterDelay()); + if (CoroutineJumpReset != null) + StopCoroutine(CoroutineJumpReset); + CoroutineJumpReset = StartCoroutine(ResetJumpAfterDelay()); + //Rigidbody.AddForce(MoveDirection.normalized * MoveSpeed / 2, ForceMode2D.Force); + MoveSpeedDampener = 2; } private IEnumerator ResetJumpAfterDelay() { @@ -121,6 +189,7 @@ public class Player : MonoBehaviour { foreach (var col in BoxColliders) col.enabled = true; IsJumping = false; + MoveSpeedDampener = 1; } private void SetClass(int classIdx) { @@ -143,6 +212,13 @@ public class Player : MonoBehaviour { + + + + + + + private void FixedUpdate() { MovementUpdate(); CalculateFacingDirection(); @@ -150,15 +226,27 @@ public class Player : MonoBehaviour { } private void MovementUpdate() { - if (IsDashing) return; - - RigidBody.linearVelocity = (MoveDirection.normalized * MoveSpeed) / MoveSpeedDampener; - if (Drift > 0.2f) { - RigidBody.linearVelocity += DriftDirection * Drift; - Drift *= DriftFactorial; - } else { - Drift = 0f; + //if (IsJumping && !IsDashing) { + // Rigidbody.AddForce(MoveDirection.normalized * MoveSpeed / 5, ForceMode2D.Force); + // return; + //} + var movement = (MoveDirection.normalized * MoveSpeed) / MoveSpeedDampener; + //var movement = MoveDirection.normalized * MoveSpeed; + if (IsDashing) { + if (DashSpeed < 0.2f) { + DashSpeed = 0; + CanDash = true; + IsDashing = false; + } else if (DashSpeed > (movement.magnitude * 2)) { + if (IsJumping) Jump(); + movement = DashDirection * DashSpeed; + DashSpeed *= Mathf.Exp(-DashDecayRate * Time.deltaTime); + } else { + movement = ((DashDirection * DashSpeed) + movement) / 2; + DashSpeed *= Mathf.Exp(-DashDecayRate * Time.deltaTime); + } } + Rigidbody.linearVelocity = movement; } @@ -201,13 +289,13 @@ public class Player : MonoBehaviour { } private void DrawPlayerDirection() { - if (RigidBody == null) RigidBody = GetComponent(); - if (RigidBody == null) return; + if (Rigidbody == null) Rigidbody = GetComponent(); + if (Rigidbody == null) return; Gizmos.color = Color.cyan; - Vector3 start = RigidBody.transform.position; - Vector3 end = start + (Vector3)(RigidBody.linearVelocity * 1); + Vector3 start = Rigidbody.transform.position; + Vector3 end = start + (Vector3)(Rigidbody.linearVelocity * 1); Gizmos.DrawLine(start, end); DrawArrowHead(end, (end - start).normalized);