Using velocity.magnitude for stamina regen

Hey all. I’m trying to use velocity.magnitude to either decrease or increase a stamina meter. I can get it to decrease just fine. But if I want to increase stamina, I’m checking if the rigidbody’s velocity.magnitude is < 0.1. This isn’t working, though. Even when not moving, the stamina continues to fall.

I’m not sure what I’m doing wrong. Any ideas? Here’s the code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerVitals : MonoBehaviour
{

    public Slider healthSlider;
    public int maxHealth = 100;
    public int healthFallRate;

    public Slider staminaSlider;
    public int maxStamina = 100;
    public int staminaFallRate;
    public int staminaRegen;

    PlayerMovement2 playerMovement;

    void Start()
    {
        healthSlider.maxValue = maxHealth;
        healthSlider.value = maxHealth;
        staminaSlider.maxValue = maxStamina;
        staminaSlider.value = maxStamina;

        playerMovement = GetComponent<PlayerMovement2>();
    }

    void Update()
    {
        Stamina();
        Debug.Log(playerMovement.rb.velocity.magnitude);
    }
  
    void Stamina()
    {
     

        if(playerMovement.rb.velocity.magnitude > 0)
        {
            staminaFallRate = 5;

            staminaSlider.value -= Time.deltaTime * staminaFallRate;
        }
        else if (playerMovement.rb.velocity.magnitude < 0.1)
        {
            staminaRegen = 5;

            staminaSlider.value += Time.deltaTime * staminaRegen;
        }

    }
}

Actually, I may have figured out the issue. Stupid of me. Both if statements pass the check, so I’ve increased the check on the first if statement to 0.2.

This didn’t work. I can’t find a suitable magnitude to check for, since it goes up and down constantly and I don’t understand the pattern. If the player (rigidbody) is sprinting, the magnitude is sometimes smaller than if it’s stationary.

I’m clearly missing something. Any help is greatly appreciated.

And sorry for the multiple posts.

That’s odd that you see a smaller magnitude when sprinting compared to standing still.

Are there a lot of different speeds the player can be at? If so, do you want many different variations of the gain/loss in stamina? I ask because perhaps you could just have 2 or 3 states (I see you’re only using 2 now), and the other script could set the state; inside your Update function, you could use a switch statement to calculate the loss/gain based on this state.

Small question/note: If the rigidbody is on the same game object, you could (have) just looked it up there, rather than getting it from the script :slight_smile:

I’ll have to do a refresher on switch statements! I only started learning c# a few months ago, so I’m constantly having to refamiliarise myself with concepts as and when I need them.

Switch statement aside, I suppose the easiest way would be to create a bool for when the player is sprinting (based on a key press) and use that in the if statement.

I’m just confused about the rigidbody having a magnitude greater than 0 when it’s stationary. It also changes dramatically depending on where I move the object to. At one location (while stationary) the magnitude was jumping between 4 and 8. In other locations, it stayed around 0.0046. Is that normal? I assume I’m missing something about the concept.

And yeah, I completely forgot the scripts were on the same object.

Okay… I ran a small test for this, and my results are always showing the correct magnitude. It’s what I imagined, but when in doubt, I test. :wink:
So, perhaps you could show your code in case something is odd there? I mean, I don’t know what but…

As for the switch, that’s not really that important if you only have the 2 states, it was more of an idea if you had 3-5+ , then I find switch statements a lot easier than a bunch of bools :slight_smile:

This is the super simple code I wrote to test :slight_smile:

public class RbMagTest : MonoBehaviour {

    Rigidbody rb;
    int cnt = 0;
    [SerializeField]
    float speed;
    [SerializeField]
    bool useCount = false;
   void Start () {
        rb = GetComponent<Rigidbody>();
   }
   
   void Update () {
        float v = Input.GetAxis("Vertical");
        float h = Input.GetAxis("Horizontal");
        Vector3 move = new Vector3(h, v, 0);
        move.Normalize();
        rb.velocity = move * speed;
       
        if (useCount)
        {
            ++cnt;
            if (cnt == 15)
            {
                print("mag = " + rb.velocity.magnitude);
                cnt = 0;
            }
        }
        else print("mag = " + rb.velocity.magnitude);
   }
}

Sure. Here’s the code from the movement script. There’s some stuff in there I haven’t really used yet, but maybe you can spot something that’s messing with the magnitude?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement2 : MonoBehaviour
{
    public float speed =10;
  

    public Rigidbody rb;
    Vector3 movement;
    LayerMask floorMask;

    public bool sprint;
    public bool moving = false;

    private void Awake()
    {
        floorMask = LayerMask.GetMask("Floor");
        rb = GetComponent<Rigidbody>();
    }

    private void FixedUpdate()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");

        Move(h, v);
        Turning();

        Sprint();
  
    }


   void Move(float h, float v)
    {
        movement.Set(h, 0, v);
        movement = movement.normalized * speed*Time.deltaTime;

        rb.MovePosition(transform.position + movement);

        if(rb.velocity.magnitude > 0)
        {
            moving = true;
        }
        else
        {
            moving = false;
        }
    }


    void Turning()
    {
        Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);

        RaycastHit floorHit;

        if(Physics.Raycast (camRay, out floorHit, 100f, floorMask))
        {
            Vector3 playerToMouse = floorHit.point - transform.position;
            playerToMouse.y = 0f;
            Quaternion newRotation = Quaternion.LookRotation(playerToMouse);
            rb.MoveRotation(newRotation);
        }
    }
    void Sprint()
    {
        if (Input.GetKeyUp(KeyCode.Space))
        {
            sprint = !sprint;
        }

        if (sprint)
        {
            speed = 30;
        }
        else
            speed = 10;
    }

  
}

omgoodness, finally I was able to reproduce something that might, maybe be related to your issue lol.

I almost gave up or said it was working for me a few times, but I kept looking…

So, if I tried using move position, like you, and I had gravity on and my rigidbody was not kinematic, I would not getting any value for magnitude going side to side, only going up (gravity, I believe).

But when i changed to kinematic I got values printing, again.

To be honest, I did more tests than that, but have lost my head a little bit here in doing so, so I cannot tell you how everything went :slight_smile:

I do not really know if you want kinematic or not, but if you don’t, I would suggest you move your character with adding force or setting the velocity. (not entirely related, just thought I’d mention it).

Hope that helps. If for some reason, what I wrote is not relevant to you, I am …out of ideas, I think?! :slight_smile:

Thanks for taking so much time to help. It’s very much appreciated.

I’m actually not sure if I’ll want my rb to be kinematic. However, when I make it kinematic I do get the correct values. So that would work if need be. :slight_smile:

When it’s not kinematic my rb is moving slightly even when it’s supposed to be stationary, so the values are correct. I can see that the x and y position is changing ever so slightly. Some sort of force is being applied to it, I guess. I just don’t have a clue what it could be.

Ah okay. not sure what forces, but I understand what you’re saying in general.

Well, hopefully from here you can work out what you want to do next :slight_smile: heh.

OK, here’s some more information. If I freeze the y position in the inspector(the rb is just above the ground, so it drops slightly when I hit play), the console says the magnitude is 0. With the y frozen, the magnitude remains 0 even when I move along the x and z.

Second piece of possibly relevant information. As soon as the rb touches the ground, the magnitude becomes greater than 0 (x and z positions starting changing in the inspector). If the rb is simply in the air, the magnitude remains 0.

Hopefully! I’ve included some more info in the post above that might help you solve it. I can’t let this go. :stuck_out_tongue:

Is that still with MovePosition and non-kinematic rb ?
Because that sounds very similar to what I was experiencing.

Yes, the rb is non-kinematic. It’s something to do with the rb landing on the ground that makes it start changing position. If I try to move the object in the air in any direction, the magnitude is always 0.

Well, we could try to figure out the ground portion, but honestly it doesn’t much matter, since you can’t get a velocity reading while moving in the air. Kinda makes sense, right?

So, back to the same options:

  1. use your bool to determine stamina
  2. move the character with force/velocity (honestly this is better I think for physics, anyways; whether you use the bool or magnitude for your stamina calculations).

Alright, I’ll do that. Thanks again for all the help. :slight_smile:

The velocity shouldn’t be consuming stamina, if it’s for sprinting etc. Time should consume stamina while sprinting is active.

No problem. Good luck & have fun :slight_smile: