Player swinging slowly with Distance joint in 2D

I’m making a grappling hook with a distance joint in my 2D platformer game. However, the player is supposed to swing fast when they hook onto an object, but instead, they are swinging slowly.

I tried creating another sprite and adding the same Grappling Hook script to it. When I pressed play, the sprite swung fast as intended. The player’s and the sprite’s components, such as Rigidbody, distance joint, and line renderer, are the same, except I didn’t include the PlayerController script in the other sprite. Therefore, I think something is wrong with my PlayerController script that prevents the player from swinging fast, because I removed PlayerController script from the player and It worked, but PlayerController script is very useful.

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

public class PlayerMovement : MonoBehaviour
{
    // Status
    [Header("Status")]
    [SerializeField] public float hp;
    [SerializeField] private int hearts;
    [SerializeField] public float speed;
    [SerializeField] public float regularSpeed;
    [SerializeField] private float jumpForce;

    // Movement Mechanics
    [Header("Movement Mechanics")]
    public Rigidbody2D rb;
    private float move;
    [SerializeField] private bool isOnGround;
    [SerializeField] private Transform groundCheck;
    [SerializeField] private LayerMask groundLayer;

    [SerializeField] private bool isMovingLeft;
    [SerializeField] private bool isMovingRight;

    public bool canMove;

    // Attack Mechanics
    [Header("Attack Mechanics")]
    public float shootDmg;
    [SerializeField] private float attackDmg;
    [SerializeField] private Transform shootingPoint;
    [SerializeField] private GameObject bullet;
    
    // Raycast
    [Header("Raycast")]
    [SerializeField] private float obstacleRayDistance;
    [SerializeField] private GameObject obstacleRayObject;

    // UI
    [Header("UI")]
    public Slider healthBar;
    public TMP_Text heartsCount;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        speed = regularSpeed;
    }

    void Update()
    {
        // Health
        if (hp <= 0)
        {
            Death();
        }

        healthBar.value = hp;
        heartsCount.text = hearts.ToString();

        if (!canMove)
        {
            Debug.Log("You can't move");
            return;
        }

        // Check for keyboard inputs
        if (Input.GetKey(KeyCode.A))
        {
            MoveLeft();
        }
        else if (Input.GetKey(KeyCode.D))
        {
            MoveRight();
        }
        else if (!isMovingLeft && !isMovingRight)
        {
            StopMoving();
        }

        // Set direction based on button states
        if (isMovingLeft)
        {
            MoveLeft();
        }
        else if (isMovingRight)
        {
            MoveRight();
        }

        // Jump when the Jump button is pressed and player is grounded
        if (Input.GetButtonDown("Jump") && isGrounded() && canMove)
        {
            rb.AddForce(new Vector2(rb.velocity.x, jumpForce));
        }

        if (Input.GetButtonUp("Jump") && rb.velocity.y > 10)
        {
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
        }

        // Shooting function (commented out)
        // if(Input.GetButtonDown("Shoot") && canMove)
        // {
        //     Instantiate(bullet, shootingPoint.position, transform.rotation);
        // }
    }

    void FixedUpdate()
    {
        // Update Rigidbody velocity based on move direction
        rb.velocity = new Vector3(speed * move, rb.velocity.y, 0);
    }

    // Button press handlers
    public void OnLeftButtonDown() => isMovingLeft = true;
    public void OnLeftButtonUp() => isMovingLeft = false;
    public void OnRightButtonDown() => isMovingRight = true;
    public void OnRightButtonUp() => isMovingRight = false;

    public void MoveLeft()
    {
        transform.eulerAngles = new Vector3(0, 180, 0);
        move = -1;
    }

    public void MoveRight()
    {
        transform.eulerAngles = new Vector3(0, 0, 0);
        move = 1;
    }

    public void Jump()
    {
        if (isGrounded() && canMove)
        {
            rb.AddForce(new Vector2(rb.velocity.x, jumpForce));
        }
    }

    public void StopMoving()
    {
        move = 0;
    }

    private bool isGrounded()
    {
        return Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer);
    }

    public void Death()
    {
        hp = 100f;
        hearts -= 1;
    }
}

Grappler script:

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

public class Grappler : MonoBehaviour
{
    public Camera mainCamera;
    public LineRenderer _lineRenderer;
    public DistanceJoint2D _distanceJoint;

    public GameObject[] grapplingObjects;
    public GameObject nearestObject;
    float distance;
    float nearestDistance = 10000;

    // Start is called before the first frame update
    void Start()
    {
        grapplingObjects = GameObject.FindGameObjectsWithTag("GrapplingObject");
        _distanceJoint.enabled = false;

        
    }

    // Update is called once per frame
    void Update()
    {
        for (int i = 0; i < grapplingObjects.Length; i++)
        {
            distance = Vector2.Distance(this.transform.position, grapplingObjects[i].transform.position);

            if(distance < nearestDistance)
            {
                nearestObject = grapplingObjects[i];
                nearestDistance = distance;
            }
        }

        if (Input.GetKeyDown(KeyCode.Mouse0))
        {
            Vector2 mousePos = (Vector2)mainCamera.ScreenToWorldPoint(Input.mousePosition);
            _lineRenderer.SetPosition(0, nearestObject.transform.position);
            _lineRenderer.SetPosition(1, transform.position);
            _distanceJoint.connectedAnchor = nearestObject.transform.position;
            _distanceJoint.enabled = true;
            _lineRenderer.enabled = true;
        }
        else if (Input.GetKeyUp(KeyCode.Mouse0))
        {
            _distanceJoint.enabled = false;
            _lineRenderer.enabled = false;
        }
        if (_distanceJoint.enabled) 
        {
            _lineRenderer.SetPosition(1, transform.position);
        }
    }

}

Here’s the video of player:
https://youtu.be/LKtuEjaY_KM

And here’s the video of another sprite:
https://youtube.com/shorts/CguW2uXhBv8?feature=share

The player controller above is always driving velocity every FixedUpdate() (review line 115) so of course it interferes with any rope / grapple physics.

The typical approach is to make a separate “when grappling” player controller.

If you want to keep doing the same controller for both then the player controller needs to know you’re grappling and behave differently, such as by not calling all the code above that drives velocity.