what does this script even mean?

Please help to understand what is velocity.y

 public Transform groundCheck;
    public float groundDistance = 0.4f;
    public LayerMask groundMask;

    Vector3 velocity;
    bool isGrounded;

    // Update is called once per frame
    void Update()
    {
        isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);

        if (isGrounded && velocity.y < 0)
        {
            velocity.y = -2f;
        }
}

velocity.y is the y component of the velocity.

velocity itself is a 3D vector that has 3 components, x, y, and z.

this particular code cannot work because there’s a brace missing, and also you need to use this velocity somehow. I presume this is an excerpt from a character controller you stumbled upon.

okay, it is a character controller.

so what this code does is going through a list of all the things that can happen to a velocity, and modify it, then finally apply it to the character. because Update happens every frame, this is essentially a per-frame recipe for how the character will behave based on dynamic input as well as gravity and obstacles.

the code I’m looking at has comments in place.
this particular line you’ve shown says (my source is in bad English)

if(isGrounded && velocity.y < 0) {
  //if the ground, the gravity down to 2
  velocity.y = -2f;
}

now the point of this is to introduce a little bit of gravity when there was none and when in contact with the ground.
admittedly, that’s not particularly intuitive and not how you implement these things, but I’m guessing this is added because the jumping which comes later in the recipe, tries to fight against this imaginary force.

//Jump
if(Input.GetButtonDown("Jump")) {
  velocity.y = Mathf.Sqrt(jumpHeight * -2f * Gravity);
}

idk, this would overwrite it, and it’s weird anyways.
it would be more helpful if you posted the full code.

all in all, that code doesn’t make much sense. if that’s what you asked.
but post it in entirety if you can, maybe there is something worthwhile going on.

It is to reduce gravity from a higher value to a lower value (-2) when you are on the ground, and your velocity is already moving you downwards (aka you aren’t jumping or being pushed upward).

the more I look at this code the less sense it makes.

you should see the rest of the code. in the end this velocity gets assigned to a controller, as if it was an input from a player. horrible piece of code. I don’t know if this was official at some point, but it’s dirty and confusing with too much magic going on. I get that it’s likely an arcade FPS controller, but still, it’s too archaic to make any proper sense of it.

this has no meaning on its own. why not -9.8? why not 0? why not just cap it?
just weird.

All velocities should end up going through the same pipeline and being treated the same because they all have to be summed into a single velocity, because you are only supposed to call the Move function on the character controller once a frame.

I’m probably the last person who doesn’t understand character controllers, you didn’t understand the point I was making.

Here is the full code-

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

public class PlayerMovementScript : MonoBehaviour
{

    public CharacterController controller;

    public float speed = 12f;
    public float gravity = -9.81f;

    Vector3 velocity;

    // Update is called once per frame
    void Update()
    {
        float x = Input.GetAxis("Horizontal");
        float z = Input.GetAxis("Vertical");

        Vector3 move = transform.right * x + transform.forward * z;

        controller.Move(move * speed * Time.deltaTime);

        velocity.y += gravity * Time.deltaTime;

        controller.Move(velocity * Time.deltaTime);
    }
}

Funny you said that ^^. This is a common solution for character controllers. The reason is simple. Yes, there is gravity, however gravity is an acceleration, not a constant velocity. Since a CharacterController does not react to any external forces and also does not handle collision responses, you have to handle the case when you actually “sit” on the ground manually.

When you are on the ground you could deactivate gravity by setting the y component of the velocity to 0. If you don’t, the y velocity would build up without bounds, even though your character sits on the ground. This would sooner or later cause issues. You either fall through the floor once the y velocity gets large enough, or when you step off a cliff you would insta-drop to the floor with a huge velocity.

You usually don’t want to set the vertical velocity to 0 because this will result in an odd movement behaviour when you walk down a slope. You would constantly loose ground contact, start falling and hitting the floor again. When you have a constant downwards velocity you can properly move up and down a slope without loosing ground contact.

How much velocity you apply is up to you. That value has nothing to do with the amount of acceleration due to gravity. You only want to set the “ground velocity” when you are already moving downwards and when you hit the ground. That’s exactly what the code does. It ensures a certain amount of downward movement when on ground to stay on the ground. If course when you jump the y component of the velocity would be positive, so the if statement is ignored in order to be able to lift off the ground.

I remember that Half-life did not do this. They let the player start accelerating due to gravity once he lift off the ground. Though the gravity was quite high. However when you walked down a slope you would constantly loose the ground connection. This effect could be seen in multiplayer where falldamage was clamped to 10hp and just requires a minimum impact force. When you manually set gravity to a really high value, any slight drop would cause damage (even jumping caused damage ^^). The fun thing was, walking down a staircase with 9 steps would cause 90 damage. Walking down a slope would essentially insta-kill you because you would drop each frame a tiny amount, hit the ground hard and get 10hp damage.

So using a constant velocty of 2 m/s at a normal gravity acceleration of 9.81m/s² (or roughtly 10) means you retain a velocity roughly equal to 200ms (0.2s) of free-falling.

1 Like

Aha RIGHT! It’s to prevent accumulation overflow. Now that’s something! Thanks. Couldn’t figure this one out, and I still think it’s a bad code. you would insta-drop oh boy Neutron star :smile:

This I figured. But I fixated on this too much.

Thanks for the explanation!

Well, I’m absolutely not an expert in character controllers, but according to many tutorials you are supposed to call controller.Move a maximum of once a frame like I mentioned previously otherwise it “might cause issues”, whatever that means.

The official docs also call Move twice in a frame. So can it actually cause issues, or is summing the velocity and calling Move only once just about efficiency?

When people said it can cause issues, I imagined scenarios like if you press W and Move forward and press S and Move backward in a single frame, the controller might collide with something in front of you and not be able to move forward, but then it would be able to move you backwards, even though you really shouldn’t have hit the collider or moved at all, because your movements should have cancelled each other out, except that you registered them separately. That’s the kind of weird stuff I imagined happening. I didn’t test anything, I just followed the advice.

Anyone know what the issues are supposed to be?

I just couldn’t resist starting up Halflife and try it out once more ^^. Too much nostalgia :slight_smile: Crossfire always was our favourite DM map (and it gave me my nickname almost 20y ago). Occationally I can be found on this TF2 server (Lazytown only server).

You are indeed only supposed to do this, starting sometime in the 2017 series or so. Calling .Move() twice makes your grounded checks invalid and inconsistent.

Here’s my blurb:

I wrote about this before: the Unity example code in the API no longer jumps reliably.

I reported it to Unity via their docs feedback in October 2020. Apparently it is still broken:

Here is a work-around:

https://discussions.unity.com/t/811250/2

I recommend you also go to that same documentation page and ALSO report that the code is broken.

When you report it, you are welcome to link the above workaround. One day the docs might get fixed.

1 Like

Can you please explain me what is velocity.y

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

public class PlayerMovement : MonoBehaviour
{
    //VARIABLES
   
    [SerializeField] private float moveSpeed;
    [SerializeField] private float walkSpeed;
    [SerializeField] private float runSpeed;

    private Vector3 moveDirection;
    private Vector3 velocity;

    [SerializeField] private bool isGrounded;
    [SerializeField] private float groundCheckDistance;
    [SerializeField] private LayerMask groundMask;
    [SerializeField] private float gravity;

    [SerializeField] private float jumpHeight;

    //REFFERENCES

    private CharacterController controller;

    private void Start()
    {
        controller = GetComponent<CharacterController>();
    }

    private void Update()
    {
        Move();
      
    }

    private void Move()
    {

        isGrounded = Physics.CheckSphere(transform.position, groundCheckDistance, groundMask);

        if (isGrounded && velocity.y < 0)
        {
            velocity.y = -2f;
        }

        //W and S Movement
        float moveZ = Input.GetAxis("Vertical");
        moveDirection = new Vector3(0, 0, moveZ);
        
        //Speed Determision
        if (isGrounded)
        {
            if (moveDirection != Vector3.zero && !Input.GetKey(KeyCode.LeftShift))
            {
                walk();
            }
            else if (moveDirection != Vector3.zero && Input.GetKey(KeyCode.LeftShift))
            {
                run();
            }
            else if (moveDirection == Vector3.zero)
            {
                idle();
        
            }
            moveDirection *= moveSpeed;

            if (Input.GetKeyDown(KeyCode.Space))
            {
                jump();
            }
        }
       
       
        controller.Move(moveDirection * Time.deltaTime);


        velocity.y += gravity * Time.deltaTime;
        controller.Move(velocity * Time.deltaTime);
    }

    private void walk()
    {
        moveSpeed = walkSpeed;
    }

    private void run()
    {
        moveSpeed = runSpeed;
    }

    private void idle()
    {

    }

    private void jump()
    {
        velocity.y = Mathf.Sqrt(jumpHeight * -2 * gravity);
    }
}

As has already been pointed out multiple times above, velocity is a Vector3 variable. See line 15.

Go look here and you can see what ALL the parts of a Vector3 are:

If any of that does not make instant sense to you, which it clearly appears not to, go work through a few hours of C# tutorials, otherwise you are just wasting your time.