Hey, thank you for taking the time to read my post. I have this general problem with the stamina system in my game where whenever the character switches running direction, like for instance if they were running right and they immediately started running left, it uses the initial stamina cost instead of the constant stamina cost for running. It also makes the run stamina deplete much faster if you do it multiple times. If anyone has a solution for this, please let me know!! Thank you!
Here’s a video showcasing what I mean:
Here’s the code for the playercontroller (I know its messy but im a pretty new programmer so im sorry about any inconvenience):
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(CharacterController), typeof(PlayerInput))]
public class PlayerController : MonoBehaviour
{
public float jumpButtonGrace;
public bool hasPowerup;
public Vector3 moveTo;
public LayerMask RaycastIgnore;
public HealthBar healthBar;
public ManaBar manaBar;
public StaminaBar staminaBar;
[SerializeField]
private float currentSpeed;
[SerializeField]
private float walkSpeed = 7f;
[SerializeField]
private float runSpeed = 12f;
private float staminaRegenStart;
private float runDrainStaminaTick = .5f;
private int jumpCost = 10;
private int initialRunCost = 10;
private int runCost = 2;
[SerializeField]
private float jumpHeight = 1.0f;
[SerializeField]
private float gravityValue = -9.81f;
[SerializeField]
private float rotationSpeed = 5f;
[SerializeField]
private GameObject projectilePrefab;
[SerializeField]
private Transform magicCasterTransform;
[SerializeField]
private Transform projectileParent;
[SerializeField]
private int maxHealth = 500;
[SerializeField]
private int currentHealth;
[SerializeField]
private int maxMana = 100;
[SerializeField]
private int currentMana;
[SerializeField]
private int maxStamina = 100;
[SerializeField]
private int currentStamina;
[SerializeField]
private float skyRaycastDist = 100f;
private float originalStepOffset;
private float? lastGroundedTime;
private float? jumpButtonPressedTime;
public bool isRunning;
public bool isMoving;
private CharacterController characterController;
private FireBallController fireBallController;
private PlayerInput playerInput;
private Coroutine regen;
private Coroutine runStam;
private Vector3 playerVelocity;
private Vector3 lastPos;
private bool groundedPlayer;
private Transform cameraTransform;
private InputAction moveAction;
private InputAction jumpAction;
private InputAction runAction;
private InputAction shootAction;
private WaitForSeconds staminaRegenTick = new WaitForSeconds (0.1f);
private void Awake()
{
currentSpeed = walkSpeed;
currentHealth = maxHealth;
currentMana = maxMana;
currentStamina = maxStamina;
healthBar.SetMaxHealth(maxHealth);
manaBar.SetMaxMana(maxMana);
staminaBar.SetMaxStamina(maxStamina);
isRunning = false;
characterController = GetComponent<CharacterController>();
playerInput = GetComponent<PlayerInput>();
cameraTransform = Camera.main.transform;
moveAction = playerInput.actions["Move"];
jumpAction = playerInput.actions["Jump"];
runAction = playerInput.actions["Run"];
shootAction = playerInput.actions["Shoot"];
originalStepOffset = characterController.stepOffset;
Cursor.lockState = CursorLockMode.Locked;
//FireBallController fireBallController = GameObject.Find("FireBallController").GetComponent<FireBallController>();
}
void Update()
{
lastPos = gameObject.transform.position;
groundedPlayer = characterController.isGrounded;
Vector2 input = moveAction.ReadValue<Vector2>();
Vector3 move = new Vector3(input.x, 0, input.y);
move = move.x * cameraTransform.right.normalized + move.z * cameraTransform.forward.normalized;
move.y = 0f;
characterController.Move(move * Time.deltaTime * currentSpeed);
if (gameObject.transform.position == lastPos)
{
isRunning = false;
}
else if (gameObject.transform.position != lastPos && currentSpeed == runSpeed && isMoving == true)
{
InitialRun();
isRunning = true;
}
//Sets the time since the player was grounded
if (groundedPlayer && playerVelocity.y < 0)
{
lastGroundedTime = Time.time;
}
//Sets the time since the jump button was pressed
if (jumpAction.triggered)
{
jumpButtonPressedTime = Time.time;
}
//I'm not entirely sure what this does, but it makes the playerVelocity do some maths and resets the other variables
if (Time.time - lastGroundedTime <= jumpButtonGrace)
{
characterController.stepOffset = originalStepOffset;
playerVelocity.y = -0.5f;
if (Time.time - jumpButtonPressedTime <= jumpButtonGrace)
{
UseStamina(jumpCost);
playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
jumpButtonPressedTime = null;
lastGroundedTime = null;
}
}
//Makes character Jump after adding playerVelocity with the gravityValue to make jump more natural
playerVelocity.y += gravityValue * Time.deltaTime;
characterController.Move(playerVelocity * Time.deltaTime);
//Makes character rotate with Camera
Quaternion targetRotation = Quaternion.Euler(0, cameraTransform.eulerAngles.y, 0);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
//When the Run or Shoot action happens, calls this code
private void OnEnable()
{
runAction.performed += _ => InitialRun();
runAction.canceled += _ => Walking();
shootAction.performed += ShootGun;
}
//When the Run action stops in PlayerInput map, decreases player speed
private void OnDisable()
{
runAction.performed -= _ => Walking();
runAction.canceled -= _ => InitialRun();
shootAction.performed -= ShootGun;
}
private void Walking()
{
isMoving = false;
isRunning = false;
currentSpeed = walkSpeed;
RegenStamina();
}
private void InitialRun()
{
if (currentStamina - initialRunCost >= 0 && isRunning == false)
{
UseStamina(initialRunCost);
currentSpeed = runSpeed;
isRunning = true;
isMoving = true;
Running();
}
}
private void Running()
{
StartCoroutine(RunDrainStamina());
}
private void ShootGun(InputAction.CallbackContext context)
{
if (hasPowerup == true && currentMana >= 5)
{
RaycastHit hit;
Physics.Raycast(cameraTransform.position, cameraTransform.forward, out hit, Mathf.Infinity, ~RaycastIgnore);
Vector3 moveTo = (hit.point - gameObject.transform.position).normalized;
GameObject fireBall = GameObject.Instantiate(projectilePrefab, magicCasterTransform.position, Quaternion.identity, projectileParent);
FireBallController fireBallController = fireBall.GetComponent<FireBallController>();
fireBallController.moveTo = moveTo;
if (Physics.Raycast(cameraTransform.position, cameraTransform.forward, out hit, Mathf.Infinity, ~RaycastIgnore))//&& hit.collider.tag != "FireHit"
{
fireBallController.target = hit.point;
fireBallController.hit = true;
}
else
{
fireBallController.moveTo = (cameraTransform.forward + fireBall.transform.forward * skyRaycastDist).normalized;
fireBallController.hit = false;
}
//GameObject fireBall = GameObject.Instantiate(projectilePrefab, magicCasterTransform.position, Quaternion.identity, projectileParent);
}
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
healthBar.SetHealth(currentHealth);
if (currentHealth <= 0) Invoke(nameof(DestroyPlayer), 0f);
}
public void DestroyPlayer()
{
Destroy(gameObject);
}
public void LoseMana(int manaDrain)
{
currentMana -= manaDrain;
manaBar.SetMana(currentMana);
}
public void UseStamina(int staminaDrain)
{
if (currentStamina - staminaDrain >= 0)
{
currentStamina -= staminaDrain;
staminaBar.SetStamina(currentStamina);
if(regen != null)
{
StopCoroutine(regen);
}
regen = StartCoroutine(RegenStamina());
}
else
{
Debug.Log("Not enough stamina");
}
}
private IEnumerator RegenStamina()
{
if (currentStamina == 0)
{
staminaRegenStart = 4f;
}
else
{
staminaRegenStart = 2f;
}
yield return new WaitForSeconds(staminaRegenStart);
while (currentStamina < maxStamina)
{
currentStamina += maxStamina / 100;
staminaBar.SetStamina(currentStamina);
yield return staminaRegenTick;
}
regen = null;
}
private IEnumerator RunDrainStamina()
{
while (currentStamina - runCost >= 0 && isRunning)
{
if (currentStamina - runCost <= 1)
{
UseStamina(runCost);
Walking();
}
else
{
UseStamina(runCost);
yield return new WaitForSeconds(runDrainStaminaTick);
}
}
}
}