Rope math: deconstruction of DETOUR's main mechanic
Hello! As last idea for a post about our puzzle-game DETOUR, I wanted to make an overview of how I implemented rope physics (or should I say "wire physics"?)
What do we want? - We want to draw a line between the player and the charging station, and this line to update on player's movement. - When this line touches pillar, we want to "bend" the line around the corner, adding a new turning point for the line. - Repeat for each corner we meet. - Also, if we start to "unwind" the wire, detach from the pillar when necessary.
example from our final build:

So, how do we implement it?
One way to do this is use some physics engine: pillars are collidable objects, rope is a chain of collidable objects, they collide with each other. This method was scrapped early on because of one big reason: physics is wonky, rope will wiggle around, glitch at corners, and in general behave not 100% predictable. Not what we want for a puzzle game.
So we choose the "nerdy" way: maths. It actually turned out to be easier than I expected, but still required lots of steps to be implemented. I'll try to explain step by step. I am using Godot and its internal objects like Line2D and Area2D, but the concept should work for any game engine (or even extrapolated to no engine at all, if needed).
1) For visuals, I use Line2D. In its primitive form it's just a set of lines connecting your points in order.
2) Points are stored in an array somewhere in code. First point is always charger (which is immovable), last point is always player (so on player's movement we update last point's position and redraw the line).
3) Also, on player's movement we trigger a special "update detection" function. For each pair of points (at first it's just 2 points in line), we add a rectangular CollisionShape to the Area2D detection of the line, with the length of distance between lines and the width of the line (more or less). It looks something like this:

It’s arguably the only use of build-in “physics”, but in our case it’s just for detecting intersections between rectangles, which technically could also be done manually by calculating intersection of two rectangle’s edges :)
4) Add small collision rectangles to each of the 4 corners in each pillar. When some of the corners detects an intersection with one of the line’s collider, it sends its position to the line. Line adds a new point before the last one, basically splitting the last edge in two new edges (not forgetting to add a new collider for newly emerged edge). Corner remembers which two parts of wire it is already touching, so we don’t create new points for them. The result already looks promising:

There is only one caveat: we can’t unwind the rope for now. For that, we’ll need more math!
5) How do we check if we should be still touching the rope? The secret is just to compare angles! Let’s assume that line between player and second to last point is angled at Y degrees to the horizon, and second to last + third to last points form an X angle:

Now we can see that if Y>X, then player is still “around the corner” and the wire should still touch the corner. And if the player backtracks and Y becomes lesser than X, then we should “unsnap” from the corner by deleting the second to last point. Sounds good? Let’s test:
They got us in the first half, not gonna lie.
But we missed just one last calculation:
6) If we wrap around the corner “counterclockwise” (in comparison to “clockwise in step 5”), we should actually wait for Y to become greater than X, because everything is mirrored. There is an easy fix: when our line collides with the corner, we also check which side from the edge is the corner situated. It’s actually also just a math comparison between two angles:

We store this side alongside line’s points and use it to determine whether we should use “>” or “<” in step 5.
Well, seems like that's it! How does it look?
Awesome, it works now!
Of course, this is a generalisation of the whole idea. I had to implement some “usual game hacks” to make it work as expected: - Corner’s point is a bit outside corner’s collider - I needed some “small negative delta angle” in unsnapping function so I don’t unsnap immediately after snapping, - Comparison between angles behaves funnily at +-180 degrees
Also, this logic has limitations. I can’t have moving blocks (but should be easy enough to implement, just moving points inside line and more angle comparisons). Also I tried to do a maze level and failed miserably because line touches dozens of corners at the same time (and I was too lazy to fix that for only one level):

What is the moral of the story? Idk, maybe that some complex looking ideas can become simple enough if you can destructure them into simple steps.
If you liked this post (I genuinely don’t know if this was an interesting read), please leave a like and play our little rope-based arcade-puzzle DETOUR :)
https://ldjam.com/events/ludum-dare/53/detour














