Grapple: Show Your Solutions Here - Spoiler Alert - Dont look till you are done!

Here’s my solution

Summary

One challenge is missing as was removed as it caused errors further down the line - pun intended :wink:

Code
using System.Collections;
using UnityEngine;

public class GrapplingHook : MonoBehaviour
{
    [SerializeField] float playerSpeed = 1f;
    [SerializeField] float ropeSpeed = 5f;
    [SerializeField] private float distance = 10f;
    [SerializeField] private LayerMask mask;
    [SerializeField] private LineRenderer line;
    [SerializeField] private float grappleSpeed = 3f;
    [SerializeField] private GameObject playerHand;

    DistanceJoint2D joint;
    Vector3 targetPos;
    RaycastHit2D hit;
    Rigidbody2D connectedRigidbody;

    private void Start()
    {
        joint = GetComponent<DistanceJoint2D>();
        joint.enabled = false;
        line.enabled = false;
    }

    void Update()
    {
       PullPlayer();

        if (Input.GetMouseButtonDown(0))
        {
            targetPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            targetPos.z = 0;

            hit = Physics2D.Raycast(playerHand.transform.position, targetPos - playerHand.transform.position, distance, mask);

            if (hit.collider != null && hit.collider.gameObject.GetComponent<Rigidbody2D>() != null)
            {
                joint.enabled = true;

                connectedRigidbody = hit.collider.attachedRigidbody;

                joint.connectedAnchor = hit.point;

                joint.distance = Vector2.Distance(playerHand.transform.position, hit.point);

                StartCoroutine(FireLine(hit.point));
            }
        }

        if (Input.GetMouseButton(0))
        {
            line.SetPosition(0, playerHand.transform.position);
        }

        if (Input.GetMouseButtonUp(0))
        {
            joint.enabled = false;
            line.enabled = false;
            StopAllCoroutines();
        }
    }

    IEnumerator FireLine(Vector2 point)
    {
        line.enabled = true;  

        float perc = 0;

        while (perc < 1 && line.enabled)
        {
            perc += Time.deltaTime * ropeSpeed;

            line.SetPosition(0, playerHand.transform.position);
            line.SetPosition(1, Vector3.Lerp(playerHand.transform.position, point, perc));

            yield return null;
        }
        if (line.enabled) line.SetPosition(1, point);
    }

    void PullPlayer()
    {
        if (!joint.enabled) return;

        joint.distance -= Time.deltaTime * playerSpeed;
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        if (connectedRigidbody == collision.rigidbody)
        {
            joint.enabled = false;
            line.enabled = false;
        }
    }
}

Here is mine @EddieRocks @Hazel_Ryan

// GameDev.tv Challenge Club. Got questions or want to share your nifty solution?
// Head over to - http://community.gamedev.tv

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

public class GrapplingHook : MonoBehaviour
{
    [SerializeField] private float distance = 10f;
    [SerializeField] private LayerMask mask;
    [SerializeField] private LineRenderer line;
    [SerializeField] private float grappleSpeed = 3f;
    [SerializeField] private GameObject playerHand;

    DistanceJoint2D joint;
    Vector3 targetPos;
    RaycastHit2D hit;

    private void Start()
    {
        joint = GetComponent<DistanceJoint2D>();
        joint.enabled = false;
        line.enabled = false;
        line.material = new Material(Shader.Find("Legacy Shaders/Particles/Alpha Blended Premultiply"));
        line.endColor = Color.red;
        line.startColor = Color.gray;
        line.endWidth = .03f;
        line.startWidth = .03f;
    }

    private void Update()
    {
        PullPlayer();

        if (Input.GetMouseButtonDown(0))
        {
            targetPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            targetPos.z = 0;

            hit = Physics2D.Raycast(playerHand.transform.position, targetPos - playerHand.transform.position, distance, mask);

            if (hit.collider != null && hit.collider.gameObject.GetComponent<Rigidbody2D>() != null)
            {

                joint.distance = Vector2.Distance(playerHand.transform.position, hit.point);
                joint.connectedBody = hit.collider.gameObject.GetComponent<Rigidbody2D>();

                StartCoroutine(fireGrapple());
            }
        }

        if (Input.GetMouseButton(0))
        {
            line.SetPosition(0, playerHand.transform.position);
        }

        if (Input.GetMouseButtonUp(0))
        {
            joint.enabled = false;
            line.enabled = false;
        }
    }

    IEnumerator fireGrapple()
    {
        line.enabled = true;
        line.SetPosition(0, playerHand.transform.position);
        for (float i = 0; i <= 1.0f; i = i + 0.1f)
        {
            line.SetPosition(1, Vector2.Lerp(playerHand.transform.position, hit.point, i));
            yield return new WaitForSeconds(.01f);
        }
        line.SetPosition(1, hit.point);
        joint.enabled = true;
    }

    private void PullPlayer()
    {
        //Challenge 2:
        if (joint.enabled)
        {
            Vector2 pos = playerHand.transform.position;
            Vector2 dir = (hit.point - (Vector2)playerHand.transform.position).normalized;
            GetComponent<Rigidbody2D>().AddForce(dir*5f);
        }
    }
}

Quick look and as it works looks OK to me.

Couple of notes ( if you want -dont look if you dont!!!):

A) Hardcoded numbers

I personally I dont hardcode numbers (unless a constistent 0 or 1) so I’d put those in
[SerializeField] and allows easier playing round with when testing to get variety and sometime odd but pleasing results.
It also self comments the code.
So for AddForce(dir*5f) I’d have to guess that 5 is may be speed and if you comeback to it in 6 months time you may be guessing too :wink:

B) GetComponent
GetComponent<Rigidbody2D>().AddForce(dir*5f);

This is work for Unity - and you are asking for it quite a bit.
Pull it out into Start or Awake and only do it once.

1 Like

Does adding force shorten the distance joint or just propel the Rigid Body? I would expect it to only propel but I did find that these distance joints did behave strangely sometimes.

If you yield return null it will essentially continue on the next frame

I like that we all have 3 quite different solutions to the same problem

2 Likes

Thanks for the feedback, really helpful :smiley:

Thanks @Hazel, I tried that tonight and it works well, I prefer to use that :slight_smile:
Kurt

Solution put into practice…