How to make Vector3 gravitate towards zero?

I am moving my characterController based on a vector3, and I am trying to get it to gravitate towards zero, while still being able to accelerate freely. I am going to attempt to explain what I am trying to achieve with 2D ideas.

Basically, if you were to press Left or Right in this theoretical 2D space, you would accelerate by 0.5 pixels per a second, maximum of 5 pixels per a second, left or right depending on which key you are holding. If you release either one, the game will check if the value is positive or negative by comparing it to 0. Assuming its positive, it would decelerate by 0.8 pixels per a second if the player is not touching right. Same for left.

I am trying to achieve this in 3D with this code so far:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class PlayerController : MonoBehaviour {

// Use this for initialization


public CharacterController controller;//The characterController
public Animator animator;//The animator

public float jumpForce;//How high the player will jump
public float fallForce;//How fast the player will fall
public float maxAcc;//The player's acceleration
public float minusAcc;//The player's acceleration loss
public float moveSpeed;//The player's maximum movespeed

public Transform poit;//The pivot point of player's rotation(camera)

private Vector3 moveDirection;//For when we actually apply movement to the character controller
private float velY;//Stores the 'Y' velocity that is to be applied to the character controller

void Start () {


controller = GetComponent<CharacterController>();//Grabbing the characterController
animator = GetComponent<Animator>();//Grabbing the animator
}



// Update is called once per frame

void Update () {


    if (controller.isGrounded) {
velY = -4;//Set the velY to -4 to fix some slope issues
if (Input.GetButtonDown("Jump")) {velY = jumpForce;}//normal jump
    }

if (Input.GetAxis("Jump") < 0.1 && velY > 7) {velY = 7;}//Player releasing jump button results in lower jump

moveDirection += poit.forward * maxAcc * Input.GetAxis("Vertical") * 1.5f * Time.deltaTime;
moveDirection += poit.right * maxAcc  * Input.GetAxis("Horizontal") * 1.5f * Time.deltaTime;

moveDirection.y = 0;

if (moveDirection.magnitude > moveSpeed) {
moveDirection = moveDirection.normalized * moveSpeed;//Keep player from strafing(x + z, not one for side somersault)
}

velY = velY - (fallForce * Time.deltaTime);//Making the player fall
moveDirection.y = moveDirection.y + velY;//prevent adding on y axis
controller.Move(moveDirection * Time.deltaTime);//move the player

}


}

So how can I make my vector3(moveDirection) gravitate towards zero?

There are several things not quite clear. Let me summarize how I see your situation. Your character actually moves in 3d in the x-z plane, y direction is up down for jumping. Since you use “poit.forward” and “poit.right” it seems that your character can actually rotate around the y axis. So “forward” can actually change. You essentially want to slowly eliminate any momentum against the current principal axis (forward and right) if you’re not pressing those buttons.

If that’s the case there are several possible approaches. First of all since you compose the current acceleration direction from the two principal axes, in order to determine the current velocity along those axis you have to reverse this process. Essentially you have to project the current movement vector onto the principal axes. This can be done either seperately by using the Dot product between the movement vector and the normalized direction (forward and right). This gives you a signed local velocity for each axis. You get the same result by simply use InverseTransformDirection with your movement vector which will give you the movement in local space of the object. So the x component is the signed speed along the right axis and the z component is the signed speed along the forward axis.

Once you have splitted the movement vector into it’s two components all you have to do to decelerate is to check if the absolute value of the corresponding component is larger than your decelerate amount per frame. If it is, just subtract the axis (forward or right) multiplied by the sign of the movement component multiplied by your decelerate amount multiplied by deltaTime. If the remaining amount is smaller than your decelerate amount per frame you just eliminate the remaining amount by subtracting the whole remaining amount

In code it would look something like this:

var localVelocity = poit.InverseTransformDirection(moveDirection);
if (Input.GetAxis("Vertical") == 0)
{
    float decelerate = 0.8f * Time.deltaTime;
    if (Mathf.Abs(localVelocity.z) > decelerate)
        moveDirection -= poit.forward * Mathf.Sign(localVelocity.z) * decelerate;
    else
        moveDirection -= poit.forward * localVelocity.z;
}

if (Input.GetAxis("Horizontal") == 0)
{
    float decelerate = 0.8f * Time.deltaTime;
    if (Mathf.Abs(localVelocity.x) > decelerate)
        moveDirection -= poit.right * Mathf.Sign(localVelocity.x) * decelerate;
    else
        moveDirection -= poit.right * localVelocity.x;
}

Note that the condition of the input value equals 0 highly depends on your input axis settings. The input axis already have sensitivity and gravity values. If you want a fast reaction you want to use a high gravity value so the input value returns to 0 quickly.

Instead of subtracting the deceleration from the worldspace direction vector you could manipulate the localVelocity vector and when done convert it back to worldspace. This might be a bit simpler:

var localVelocity = poit.InverseTransformDirection(moveDirection);
if (Input.GetAxis("Vertical") == 0)
{
    float decelerate = 0.8f * Time.deltaTime;
    if (Mathf.Abs(localVelocity.z) > decelerate)
        localVelocity.z -= Mathf.Sign(localVelocity.z) * decelerate;
    else
        localVelocity.z = 0;
}

if (Input.GetAxis("Horizontal") == 0)
{
    float decelerate = 0.8f * Time.deltaTime;
    if (Mathf.Abs(localVelocity.x) > decelerate)
        localVelocity.x -= Mathf.Sign(localVelocity.x) * decelerate;
    else
        localVelocity.x = 0;
}
moveDirection = poit.TransformDirection(localVelocity);

This does exactly the same thing.

You take your direction vector and reverse it, normalize it and multiply by that until velocity in each direction is zeroed out. First multiply by a scalar to find the deceleration that feels good.

Apply to movement:

moveDirection = moveDirection * (-moveDirection.normalized * decelerationSpeed);
 controller.Move(moveDirection * Time.deltaTime);//move the player