Make a 2D Grappling Hook Game in Unity – Part 2

How to make a 2D grappling hook in Unity – part 2. In this part you will learn how to unwrap your rope as you swing back past the pivot points. By Sean Duffy.

Leave a rating/review
Save for later
Share

Contents

Hide contents
Note: This tutorial is intended for an intermediate to advanced audience, and won’t cover things such as adding components, creating new GameObjects scripts or the syntax of C#. If you need to level up your Unity skills, work through our tutorials on Getting Started with Unity and Introduction to Unity Scripting first.

Welcome to the second and final part of this two-part series on how to make a 2D grappling hook game in Unity!

In Part 1 of this series, you learned how to hook up a fairly nifty grappling hook with a rope wrapping mechanic. However, you were left wanting for more. The rope could wrap around objects in the level, but didn’t unravel when you swung back past them again.

By the end of this tutorial, you’ll be unwrapping that rope like a professional!

Getting Started

In Unity, open your completed project from Part 1 in this tutorial series, or download the starter project for this part of the series and open 2DGrapplingHook-Part2-Starter. As with Part 1, you should be using Unity 2017.1 or newer.

Open the Game scene under the Scenes project folder in the editor.

Run the Game scene and try your grappling hook on the rocks above you, then swing around so that the rope wraps over a couple of edges on the rocks.

When swinging back again, you’ll notice that the points on the rocks where the rope had previously wrapped around don’t unwrap again.

Think about the point at which the rope should unwrap. To make this easier, it might be best to think about the case where you have the rope wrapping over edges.

If the slug swings to the right while grappled to a rock above, the rope will wrap at the threshold where it passes the 180 degree point with the edge the slug is currently grappled to, as indicated by the circled green point in the image below.

When the slug swings back around again in the other direction, the rope should unwrap at that same point again (the point highlighted in red below):

Unwrapping Logic

To calculate when to unwrap the rope from points it has wrapped around, you’ll need to employ the use of some geometry mathematics. Specifically, you’ll need to use angle comparison to work out when the rope should unwrap.

Thinking about this problem can be a little daunting. Math can invoke feelings of terror and despair even in those with the strongest of fortitude.

Luckily, Unity has some excellent math helper functions that should make your life a little bit easier.

Open RopeSystem in your IDE, and create a new method named HandleRopeUnwrap().

private void HandleRopeUnwrap()
{

}

Locate Update() and add a call to your shiny new method at the very end.

HandleRopeUnwrap();

Right now, HandleRopeUnwrap() doesn’t do anything, but you now have a handle on the logic that deals with this whole unwrapping business.

You may recall from part 1 of this series that you stored rope wrap positions in a collection named ropePositions, which is a List collection. Every time the rope wraps around an edge, you store the position of that wrap point in this collection.

In order to keep things more efficient, you won’t worry about running any of the logic in HandleRopeUnwrap() if this collection’s count of stored positions is 1 or less.

In other words, when the slug is grappled to a starting point, and its rope has not wrapped around any edges yet, the ropePositions count will be 1, and you won’t worry about handling unwrapping logic.

Add this simple return statement at the top of HandleRopeUnwrap() to save precious CPU cycles for these cases, as this method is being called from Update() many times a second.

if (ropePositions.Count <= 1)
{
    return;
}

Adding Extra Variables

Below this newly added check, you'll want some measurements and references to the various angles required to do the bulk of the unwrap logic. Add the following code to HandleRopeUnwrap():

// Hinge = next point up from the player position
// Anchor = next point up from the Hinge
// Hinge Angle = Angle between anchor and hinge
// Player Angle = Angle between anchor and player

// 1
var anchorIndex = ropePositions.Count - 2;
// 2
var hingeIndex = ropePositions.Count - 1;
// 3
var anchorPosition = ropePositions[anchorIndex];
// 4
var hingePosition = ropePositions[hingeIndex];
// 5
var hingeDir = hingePosition - anchorPosition;
// 6
var hingeAngle = Vector2.Angle(anchorPosition, hingeDir);
// 7
var playerDir = playerPosition - anchorPosition;
// 8
var playerAngle = Vector2.Angle(anchorPosition, playerDir);

That's a lot of variables, so here is some explanation around each one, along with a handy illustration that will help you match up each one to its purpose.

  1. anchorIndex is the index in the ropePositions collection two positions from the end of the collection. You can look at this as two positions in the rope back from the slug's position. In the image below, this happens to be the grappling hook's first hook point into the terrain. As the ropePositions collection fills with more wrap points, this point will always be the wrap point two positions away from the slug.
  2. hingeIndex is the index in the collection where the current hinge point is stored; in other words, the position where the rope is currently wrapping around a point closest to the 'slug' end of the rope. It’s always one position away from the slug, which is why you use ropePositions.Count - 1.
  3. anchorPosition is calculated by referencing the anchorIndex location in the ropePositions collection, and is simply a Vector2 value of that position.
  4. hingePosition is calculated by referencing the hingeIndex location in the ropePositions collection, and is simply a Vector2 value of that position.
  5. hingeDir a vector that points from the anchorPosition to the hingePosition. It is used in the next variable to work out an angle.
  6. hingeAngle is where the ever useful Vector2.Angle() helper function is used to calculate the angle between anchorPosition and the hinge point.
  7. playerDir is the vector that points from anchorPosition to the current position of the slug (playerPosition)
  8. playerAngle is then calculated by getting the angle between the anchor point and the player (slug).

These variables are all being calculated by looking at positions stored as Vector2 values in the ropePositions collection, and comparing these positions to other positions, or the current position of the player (slug).

The two important variables you now have stored for comparison are hingeAngle and playerAngle.

The value stored in hingeAngle should stay static, as it is always a fixed angle between the point two 'rope bends' away from the slug, and the current 'rope bend' closest to the slug which doesn't move until it unwraps or a new wrap point is added after this.

The playerAngle value is what changes while the slug is swinging. By comparing this angle to the hingeAngle, as well as whether the slug was last left or right of this angle, you can determine if the current wrap point closest to the slug should unwrap or not.

In part 1 of this tutorial, you stored wrap positions in a Dictionary collection named wrapPointsLookup. Each time you stored a wrap point, you added it to the dictionary with the position as the key, and 0 as the value. That 0 value was pretty mysterious though right?

This value is what you'll use to store the slug's position, relative to its angle to the hinge point (the current closest wrap point to the slug).

You'll set this to a value of -1 when the slug's angle (playerAngle) is less than the hinge's angle (hingeAngle), and a value of 1, when playerAngle is greater than hingeAngle.

By storing this in the dictionary, every time you check playerAngle against hingeAngle, you'll be able to tell if the slug has just passed the threshold at which the rope should unwrap.

Another way to put this is if the slug's angle has just been checked, and is less than the hinge's angle, but the last time it was stored in the wrap point dictionary it was marked with a value indicating it was on the other side of this angle, then the point should be immediately unwrapped!

Sean Duffy

Contributors

Sean Duffy

Author

Gijs Bannenberg

Tech Editor

Chris Belanger

Editor

Eric Van de Kerckhove

Final Pass Editor and Team Lead

Over 300 content creators. Join our team.