Paste
Of Code


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
using UnityEngine;
using UnityEngine.InputSystem;


public class HeavyBallMovement : MonoBehaviour
{
    [Header("References")]
    [SerializeField] private PlayerInput _playerInput;
    [SerializeField] private Rigidbody rb;
    [SerializeField] private Camera cam;
    [Space]
    [Header("Player Parameters")]
    [SerializeField, Range(-100f, 100f)] private float moveSpeed;
    [SerializeField, Range(-100f, 100f)] private float airSpeed;
    [SerializeField, Range(-100f, 100f)] private float airMoveSpeed;
    [SerializeField, Range(-100f, 100f)] private float accelMaxSpeed;
    [SerializeField, Range(-100f, 100f)] private float jumpForce;
    [SerializeField, Range(-100f, 100)] private float doubleJump;
    [SerializeField, Range(0f, 100f)] public float holdThreshold;
    [SerializeField, Range(-100f, 100f)] private float reverseMultiplier;
    [Space]
    [Header("Grounded Settings")]
    [SerializeField] private LayerMask groundLayer;
    [SerializeField, Range(-100f, 100f)] private float radius;
    [SerializeField, Range(-100f, 100f)] private float maxDistance;
    [SerializeField, Range(-100f, 100f)] private float groundDamping;
    [SerializeField, Range(-100f, 100f)] private float airDamping;
    [SerializeField, Range(-100f, 100f)] private float slopeStickForce;
    [Space]
    [Header("Custom Gravity Parameters")]
    [SerializeField, Range(-100f, 100f)] private float playerGravity;
    [SerializeField, Range(-100f, 100f)] private float upwardMultiplier;
    [SerializeField, Range(-100f, 100f)] private float downwardMultiplier;
    [Space]
    [Header("Bool Parameters")]
    [SerializeField] public bool isGrounded;
    [SerializeField] public bool jumpPressed;
    [SerializeField] public bool isFullJump;
    [SerializeField] public bool doubleJumpUsed;
    [SerializeField] public bool jumpButton;

    private float jumpHoldTime = 0f;
    public float finalJump;

    private bool jumpBuffered;
    private bool wasGrounded;
 


    RaycastHit hit;
    Vector2 currentInput;
    Vector3 moveDirection;


    private void Update()
    {
        HandleInput();

        jumpButton = _playerInput.jumpAction.action.WasPressedThisFrame();

        if (_playerInput.jumpAction.action.WasPressedThisFrame())
        {
            
            jumpBuffered = true;
        }
    }

    private void FixedUpdate()
    {
        HandleMovement();
        HandleGroundCheck();
        HandleJump();
        CustomGravity();
        jumpBuffered = false;

    }



    private void HandleMovement()
    {
        Vector2 input = Vector2.ClampMagnitude(currentInput, 1f);

        Vector3 camForward = cam.transform.forward;
        Vector3 camRight = cam.transform.right;

        camForward.y = 0f;
        camRight.y = 0f;

        camForward.Normalize();
        camRight.Normalize();

        moveDirection = camForward * input.y + camRight * input.x;
        moveDirection = moveDirection.normalized;


        if (isGrounded)
        {
            moveDirection = Vector3.ProjectOnPlane(moveDirection, hit.normal).normalized;

            Vector3 gravityAlongSlope = Vector3.ProjectOnPlane(Physics.gravity, hit.normal);
            rb.AddForce(-gravityAlongSlope, ForceMode.Acceleration);

            rb.AddForce(-hit.normal * slopeStickForce, ForceMode.Acceleration);
        }


        Vector3 flatVel = new Vector3(rb.linearVelocity.x, 0, rb.linearVelocity.z);

        Vector3 velDir = flatVel.magnitude > 0.01f ? flatVel.normalized : moveDirection;

        float alignment = Vector3.Dot(velDir, moveDirection);

        if (alignment < 0f)
            rb.AddForce(-velDir * moveSpeed * reverseMultiplier, ForceMode.Acceleration);


        float currentSpeed = isGrounded ? moveSpeed : airSpeed;

        if (moveDirection != Vector3.zero)
            rb.AddForce(moveDirection * currentSpeed, ForceMode.Force);


    }

    private void HandleJump()
    {
        if (_playerInput.jumpAction.action.IsPressed() && isGrounded && !jumpPressed)
        {
            jumpHoldTime += Time.deltaTime; 
        }
        else
        {
            if (jumpHoldTime > 0f && isGrounded)
            {


               
                if (jumpHoldTime >= holdThreshold)
                {
                    finalJump = 1.5f; 
                    isFullJump = true;
                }
                else
                {
                    finalJump = 1f; 
                    isFullJump = false;
                }

                Vector3 v = rb.linearVelocity;
                v.y = 0f;
                rb.linearVelocity = v;

                rb.AddForce(new(0, finalJump * jumpForce, 0), ForceMode.Impulse);
                jumpHoldTime = 0f;
                jumpPressed = true;

            }
        }

        if (!isGrounded && isFullJump && !doubleJumpUsed && jumpBuffered)
        {
            Vector3 v = rb.linearVelocity;

            v.y = 0f;
            rb.linearVelocity = v;

            rb.AddForce(1f * doubleJump * Vector3.up, ForceMode.Impulse);
            doubleJumpUsed = true;
        }
      
    }

    private void CustomGravity()
    {
        if (!isGrounded)
        {
            if (rb.linearVelocity.y > 0f)
            {
                rb.AddForce(Physics.gravity * upwardMultiplier, ForceMode.Acceleration);
            }
            else if (rb.linearVelocity.y < 0f)
            {
                rb.AddForce(Physics.gravity * downwardMultiplier, ForceMode.Acceleration);
            }
        }
    }

    private void HandleGroundCheck()
    {
        isGrounded = wasGrounded;

        isGrounded = (Physics.SphereCast(transform.position, radius, Vector3.down, out hit, maxDistance, groundLayer));


        if (isGrounded && !wasGrounded)
        {
            rb.linearDamping = groundDamping;
            rb.mass = 2f;
            jumpPressed = false;
            finalJump = 0f;
            isFullJump = false;
            doubleJumpUsed = false;
           
        }
        else
        {
            rb.linearDamping = airDamping;
            rb.mass = 0.5f;
            rb.AddForce(moveDirection * airMoveSpeed, ForceMode.Acceleration);
        }

        

    }


    private void HandleInput()
    {
        currentInput = _playerInput.moveAction.action.ReadValue<Vector2>();

    }
}

Toggle: theme, font