Hello, I need help with a simple script that disables jumping if the stamina falls below 0, this is to both limit the amount of jumping the players can do and the time in which they can do it (Stamina reduces whilst button is held, causing it to fall to 0). The problem is that even after fulling regenerating I the player should be able to jump as high (Stays enabled for same time) but instead of doing so is only able to jump about 80% of the original height. Upon logging the value of my “stamina” variable i found that even though fully generated, the stamina number would go more and more negative when pressed and then instantly go back up to 0 and regenerate once release. Any help would be great, thanks <3 (Script Below)
using System.Collections.Generic;
using UnityEngine;
public class ForceMovementTest : MonoBehaviour {
public Rigidbody rb;
public float thrust;
public float jump;
private bool isjumpenabled;
private double stamina;
private bool isjumping;
public bool touchleft;
public bool touchright;
public bool touchup;
public Collider player;
public Collider cube;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody>();
isjumpenabled = true;
}
// Update is called once per frame
void Update () {
// Checks if player is currently jumping
if (Input.GetKey (KeyCode.W)) {
isjumping = true;
} else {
isjumping = false;
}
{
// Disables jumping if stamina equals zero
if (stamina <= 0) {
isjumpenabled = false;
} else
isjumpenabled = true;
}
//Moves player
{
if (Input.GetKey (KeyCode.D)) {
rb.AddForce (thrust, 0, 0, ForceMode.Acceleration);
}
if (Input.GetKey (KeyCode.A)) {
rb.AddForce (-thrust, 0, 0, ForceMode.Acceleration);
}
{
if (isjumpenabled == true) {
if (Input.GetKey (KeyCode.W)) {
rb.AddForce (0, jump, 0, ForceMode.VelocityChange);
stamina = stamina - 2 * Time.time;
}
}
}
// Phone Controls
if (touchright) {
rb.AddForce (thrust, 0, 0, ForceMode.Acceleration);
}
if (touchleft) {
rb.AddForce (-thrust, 0, 0, ForceMode.Acceleration);
}
{
if (isjumpenabled == true) {
if (touchup) {
rb.AddForce (0, jump, 0, ForceMode.VelocityChange);
stamina = stamina - 1 * Time.time;
}
}
}
}
if (isjumping == false && (stamina <30)){
stamina = stamina + 0.05 * Time.time;
Debug.Log (stamina);
jump = 3;
}
}
}
Let us follow your code through the scenario that is an issue:
Player has zero stamina but is pressing the button anyway.
The player is holding W so we will enter this block and isjumping will be set to true (Line 31)
Is our stamina <= 0 ? yes so isjumpenabled will be set to false. (Line 39)
We know that isjumpenabled is false so we will not enter this block this update (Line 40)
isjumping is true so we will not enter this block either (Line 80)
So far logic is sound, maybe the problem isn’t logical? Maybe we’ve mistaken a functions functionality? Lets have a look at the stamina drain equation used in Line 59, stamina = stamina - 2 * Time.time. I picked this to look at as stamina is being all wacky! So let’s review the documentation on Time.time since we don’t think it could possibly be our part of the equation causing this mess… and wait the documentation tells us that Time.time returns "The time at the beginning of this frame (Read Only). This is the time in seconds since the start of the game. "
I don’t think that’s what is required in this situation do you? There must be another time we can use we aren’t looking for the elapsed time since the start of the game running, it’s just going to make our drain increase as the game progresses. Well what else can we use? Lets take a look at where time comes from the Time class, what could be better suited to what we need. We are looking for a time which indicates the time since the last frame. deltaTime looks like a much better candidate for what we are looking for! Lets replace all our Time.time references with Time.deltaTime this will get us the intended behaviour.
While the script will do what it is supposed to do now it is very messy and does not account for say what if the player doesn’t actually have the full amount of stamina to jump? Should the force that is applied be a fraction? Or is it not something we care about? If you wished to account for such a scenario and also not allow negative stamina it requires a little more logic such as:
using System.Collections.Generic;
using UnityEngine;
public class ForceMovementTest : MonoBehaviour
{
public Rigidbody rb;
//Amount of force to apply
public float jump;
//Amount of stamina that is drained per second while jumping
public float jumpdrain;
//Amount of stamina that is regenerated per second
public float staminaRegenRate;
//Maximum amount of stamina that can be held at once
public float staminaMax;
//Remaining amount of stamina
private float stamina;
void Start()
{
stamina = staminaMax;
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
if (Input.GetKey(KeyCode.W))
{
Jump();
}
else
{
stamina += staminaRegenRate * Time.deltaTime;
stamina = stamina < staminaMax ? stamina : staminaMax;
}
}
void Jump()
{
float forceFactor = 1f;
float drainAmount = jumpdrain * Time.deltaTime;
if (drainAmount > stamina)
{
//If the drain is greater then the amount available only apply a fraction of the force
forceFactor = stamina / drainAmount;
stamina = 0.0f;
}
else
{
stamina -= drainAmount;
}
rb.AddForce(0, (jump * forceFactor) * Time.deltaTime, 0, ForceMode.VelocityChange);
}
}
This modified script would give you extended behaviour, such as stamina not being 0, applying only a fraction of force based on how much stamina is left when there is not enough and it is also moved into the FixedUpdate message rather then Update, which is a better place to place physics related code when possible.
Hopefully this will help you not only with your problem currently but also how to approach troubleshooting yourself as well best of luck in your Unity Adventures!