Grappling Hook

First of all, I’d like to thank you for reading and attempting to help me.

So I’m in a bit of a pickle trying to create a grapple system for my game. It has quite the amount of specific needs, and it’s broken down into multiple parts. Luckily, that’s a good thing, as I can tackle each part one by one.

What I am looking for:

  • An idea of how I should go about tackling this system
  • The components I should try to use to achieve the right outcome for each part
  • (If possible) The right settings for these components

The system
The player is made using rigidbody, so I’m applying force to get it to move where I want it to.

The first part of the grappling system is for the player to choose a location of where they want to grapple and shoot the hook out towards the location.
This part I have finished. What I did was, simply shoot a raycast towards the intended position, and used Vector3.MoveTowards() to move the hook towards the hit point.

The second part is what has me hanging. When a button is pressed, the player needs to move at a constant speed towards the target without overshooting it. In other words, it moves directly towards the target, and as it reaches the point, it just hangs there.
I’ve tried a number of things, one that I can remember is getting the difference between the hook position and player position, normalizing it, and adding force to the rigidbody to move towards that normalized value as velocity changed.
This almost worked, but it didn’t seem to move the player at a constant speed, and it kept overshooting him.
Another one I tried was movetowards, then after it came close to the intended position, apply the velocity after, so when I let go of the button, it continues in the direction. Although, this felt clunky, but I should have probably tested it more.

The third part is the part I couldn’t even begin on. I have literally no idea how to go about this. If the player is grappling, and let go of the button that moves it towards the position, it should keep the velocity, but move around the object. It’s hard to explain, but say you have a rope that can extend and contract on will, and the player is attached to that rope, and the other end is attached to a static object. The player can reel in the rope to move towards, but as he stops reeling in, he keeps his velocity, but at some point, he turns and starts to swing around that static object.

This is what I’m looking to achieve. Sorry if it seems like I’m asking you to make a full system for me, it’s not my intention. I’m just looking for an idea as to how I should go about doing this, or what I should use to go about doing this.

Thank you very much for reading my wall of text and attempting to assist me!

EDIT:
So thanks to @CheetahSpeedLion and @NorthStar79 I’ve come up with this script for the first part and the second part of my problem

if (input.ReelIn)
                {
                    if (Vector3.Distance(transform.position, grapple.position) > 2.0f)
                    {
                        Vector3 normalized = (grapple.position - transform.position).normalized;
                        rigidbody.velocity = Vector3.MoveTowards(rigidbody.velocity, normalized * grappleSpeed, 1.0f);
                    }
                    else
                    {
                        rigidbody.velocity = Vector3.zero;
                    }
                }

But as for the third part of my problem, I believe I haven’t explained it well enough.

well, thanks to well-explained question.
you already did very well, i just want to remind one thing that could help you for achieving your desired result.

when adding force to a rigidbody , there is an option: “ForceMode.VelocityChange” .

you can make a variable for velocity, and lerp it from “0” towards your desired velocity, and when the player reached stopping distance you can instantly make it “0” again, this prevent overshooting.

@ForceGaming2013 I came up with a solution that might work for you.

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

public class grapplingHook : MonoBehaviour {

    Camera cam;
    Rigidbody rb;

    RaycastHit grapplePoint;


    bool isGrappling = false;

    //keeps track of the lenght of your "rope"
    float distance;

    //lets you control how fast you want your grappling hook to be
    public float grappleSpeed = 5f;

	void Start () {

        // Get Camera and Rigidbody
        cam = Camera.main;
        rb = GetComponent<Rigidbody>();
	}
	
	void Update () {
        // ray from camera into the scene
        Ray ray = cam.ScreenPointToRay(Input.mousePosition);


        // Check if a button is pressed and if the Raycast hits something
        if (Input.GetButtonDown("Fire1") && Physics.Raycast(ray, out grapplePoint))
        {
            isGrappling = true;
            Vector3 grappleDirection = (grapplePoint.point - transform.position);
            rb.velocity = grappleDirection.normalized * grappleSpeed;//This will instantly accelerate the player towards the grappling point
        }

        //turn grappling mode off when the button is released
        if (Input.GetButtonUp("Fire1"))
            isGrappling = false;


        //when in grappling mode (Thats when the magic happens :D)
        if (isGrappling)
        {
            //Look at the object you are currently grappling. Not really necessary but it looks cool
            transform.LookAt(grapplePoint.point);

            //Get Vector between player and grappling point
            Vector3 grappleDirection = (grapplePoint.point - transform.position);


            if (distance < grappleDirection.magnitude)// With this you can determine if you are overshooting your target. You are basically checking, if you are further away from your target then during the last frame
            {
                float velocity = rb.velocity.magnitude;//How fast you are currently

                Vector3 newDirection = Vector3.ProjectOnPlane(rb.velocity, grappleDirection);//So this is a bit more complicated
                //basically I am using the grappleDirection Vector as a normal vector of a plane.
                //I am really bad at explaining it. Partly due to my bad english but it is best if you just look up what Vector3.ProjectOnPlane does.

                rb.velocity = newDirection.normalized * velocity;//Now I just have to redirect the velocity

            }
            else//So this part is executed when you are grappling towards the grappling point
                rb.AddForce(grappleDirection.normalized * grappleSpeed);//I use addforce just to keep the velocity rather constant. You can fiddle around with the forcemodes a bit to get better results

            //Calculate distance between player and grappling point
            distance = grappleDirection.magnitude;

        }
        else
            transform.rotation = Quaternion.LookRotation(Vector3.forward, Vector3.up); //resets the rotation. Only necessary if you used transform.LookAt() before
	}
}

The script needs to be on a rigidbody to work. Just hit me up if you encounter any problems.
99249-1tm9wv.gif

I think you can achieve #2 and #3 using addforce. The trick is that if you keep adding force in one direction, the player will keep accelerating. So if you want to have a constant velocity, you need to stop applying the force once the player reaches your desired velocity. Then if you let go of the hook, the player has momentum and it will keep moving in the direction it was.

You can use something like:

if (rigidbody.velocity.magnitude < maxSpeed)
rigidbody.AddForce(transform.forward * force);

Hope that helps,
Areleli Games

@ZhavShaw
Have you thought about a spring joint?, with setting the connected anchor to hit.point?