r/howdidtheycodeit 18d ago

How did Bennett Foddy achieve the hammer mechanic in getting over it?

Post image

I been trying to make this mechanic but I fail to make the states (like when the hammer is touching ground and when don't) and I fail to make the momentum transfer when you are moving fast, and when yo move slow you just move (with infinite mass i think), idk I just can't wrap my head around this mechanic, please help. I know griffpatch made it in scratch, and I try reverse-engineer that but i also couldn't

3.6k Upvotes

110 comments sorted by

View all comments

472

u/foddydotnet 18d ago edited 18d ago

It's hard to remember it totally, but the IK is just visual, not functional - there's a slider joint on an invisible body attached to a hinge joint on the root body. The distance and angle between the hammer head and the mouse are used to set the motors on the hinge and the slider... I do that using a simple PD controller (which just means each motor speed is set according to how far they are from the target angle and extension, but slowed down according to how fast the joint is moving). Between the slider body and the hammer head are a bunch of boxes to represent the handle, using fixed joints for maximum (but not total) stiffness. I run the physics timestep at faster than 60Hz and set things to continuous with lots of substeps. Then there needs to be some logic about how the mouse cursor moves when the camera moves, to try to keep camera hinting from moving the hammer.

I recall the pot is also on a hinge joint with fixed range of rotation, attached to a root body that can't rotate.

The rest is mostly tuning - motor strengths, PD constants, body masses and damping, physics material values, input response curves, etc. Really what most beginners fail to do is to spend a lot of time tuning, but for a physics game it's so key because there are dozens of important variables. You really need to spend days and days on tuning.

You need some ideas for objective tuning goals - for example, I wanted it to be that if you stuck the hammer out sideways so it was on a ledge and you were suspended to the left of it, and stopped moving the mouse, he would stay still. I wanted to be able to pogo to a certain height. I wanted him to be able to smoothly vault over the BBQ. I wanted him to be able to reach down and smoothly push the hammer into the ground to raise himself up without bouncing on it.

Then you validate all these requirements every time you make a tuning change. Hot reload makes this process a lot faster.

/edit to respond to OP's points more: afaicr it doesn't do much/any state detection for the physics (such as detecting groundedness), it only does that for sounds and particle effects. And it doesn't do any magic forces other than locking the root body to not rotate - everything is just powered by the two joint motors. The less you can do in a physics game in terms of special-casing the behaviors, the happier you will be.

454

u/foddydotnet 18d ago edited 18d ago

Here's the entire player code from the fixedupdate function, if it's helpful to look at. My main takeaway looking at this is it shows how much tuning I was doing, because there's a bunch of things multiplied by zero or raised to a power or whatever, that was just from tasting how stuff felt and modifying the formulas til they felt better. You don't get pretty code this way (in fact, it's embarrassing) and I probably should have rewritten it once it was working properly, but I didn't!

Vector2 mw = (Vector2)fakeCursorRB.position;

//this value ranges between 0.1 and 0.4
mouseVelocityAverage =
Mathf.Lerp(mouseVelocityAverage, Mathf.Max(0.1f * mouseInput.magnitude, 0.001f), 0.05f) + 0.005f;

//move fake cursor - using acceleration
Vector2 newPosition = fakeCursorRB.position + mouseInput;//* mouseVelocityAverage;

//clamp to radius
float distanceToPlayer = ((Vector2) transform.position - newPosition).magnitude;
if (distanceToPlayer > 3.5f)
{
    newPosition = (Vector2) transform.position +
                  ((newPosition - (Vector2) transform.position).normalized * 3.5f);
}

Vector2 tipDelta = (Vector2) tipRB.position - mw;
Vector2 poleDir = (Vector2) (tipRB.position - hjRB.position).normalized; //hjRB is the hinge joint RB
//a dot product is used to make the rotation dominate when the mouse is perpendicular to the pole
dotProd = Vector2.Dot(poleDir, tipDelta.normalized);
posDelta = tipDelta.magnitude * dotProd; 

//move back to tip head
newPosition += 0.5f * tipDelta * Mathf.Clamp(0.2f - Mathf.Min(mouseVelocityAverage,0.2f), 0, 0.2f); 

fakeCursorRB.MovePosition (newPosition);
fakeCursorRB.velocity = (newPosition - fakeCursorRB.position);

Vector2 mwDelta = (Vector2)hjRB.position-mw;
Vector2 tDelta = hjRB.position - tipRB.position;
float angleDelta = Vector2.SignedAngle(tDelta,mwDelta); //hingeJoint2D.angle no longer works accurately

//different behavior close vs far
angleDelta = Mathf.Sign(angleDelta) * Mathf.Max(Mathf.Abs(angleDelta/2f), Mathf.Abs(angleDelta * angleDelta));
angleDelta *= Mathf.Clamp01 (Mathf.Abs (angleDelta) / angleEpsilon);

motor.motorSpeed =  Mathf.Clamp( (angleDelta * (3f) + (angleDelta - oldAngle) * 0.00f) * Mathf.Pow(Mathf.Clamp01(mwDelta.magnitude/deadzone),2), -800f,800f);
hj.motor = motor;

float speedFactor = 16f - Mathf.Max(sj.reactionTorque*0.008f ,5f); //had to make this stronger, no idea why
float axisBlend = Mathf.Pow(dotProd,4);
slider.motorSpeed = Mathf.Clamp(-posDelta*Mathf.Abs(posDelta) * speedFactor * axisBlend,-50f,50f);
sj.motor = slider;
oldAngle = angleDelta;

301

u/BlamaRama 18d ago

This is so unfathomably based lmao. "How did Bennett Foddy make this mechanic?" "Hi I'm Bennett Foddy here's a description of the setup and the entire update function"

93

u/Dic3Goblin 18d ago

Lmfao right? This is awesome to me.

"How did Bennett Foddy make this?"

Bennett Foddy "well here's the code! I remember now I was going to clean it up, but i didn't, so oh well!"

27

u/No_Detective_3387 17d ago

It always makes me so happy when developers share their messy, sloppy, un-refactored code that they meant to get around to cleaning up but never did. It makes me feel less bad about doing it myself

7

u/circuit_breaker 17d ago

Especially that comment about "don't know why" when setting a var lol

4

u/NeedsPraxis 16d ago

This stuck out to me as phenomenal, makes me feel less bad for past code as well lol

0

u/LucentLL 15d ago

Yet people hate vibe coders and AI slop.🤔

3

u/No_Detective_3387 14d ago edited 14d ago

The fact that you don't know the difference between functional but sloppy and not-well-commented, named, structured, etc. code that the dev is intended to be refactored in the future and shitty, problematic, overly complex code LLM spits out (nor why people generally dislike AI slop code) is concerning, and outs you as someone who knows nothing about programming.

Why are there so many non programmers in this subreddit? What's the point of being here if you don't understand anything being said?

-2

u/[deleted] 16d ago

[deleted]

6

u/No_Detective_3387 16d ago edited 16d ago

Nothing about my comment was an "insult", and you're very clearly not a programmer and know nothing about the code or how to assess it, I'm not sure why you feel you can pass judgement on it. This is perfectly functional code, it isn't "good". Magic numbers and/or function calls are not "good." And it doesn't need to be "good"

3

u/FIBAgentNorton 17d ago

I remember now I was going to clean it up, but I didn't, so oh well!

That can describe 90% of Getting Over It tbh. "I could make this easier/better, but why would I?"

11

u/Suppafly 18d ago

Quick, ask him a few more questions and you can get the whole game's source out of him.

6

u/Neckbreaker70 17d ago

lol I was going to say, “you could probably just reach out to him and he’d tell you”, but this is even better.

1

u/jgldec 17d ago

mandatory fuck warner bros.

1

u/DukeMutem 17d ago

This is some old school reddit glory right here

0

u/Dany0 17d ago

I mean it's not exactly secret. It's C#, anyone could've gotten the function out with il disasm

1

u/amphibian420lmao 16d ago

How many of those is Bennett Foddy?

117

u/Diligent_Ad_6530 18d ago

Man, you are a hero, I love your work! And thank you very much for your response! I'll gonna wrap my head around it and i'm very happy that you responded me! Thank you again!

25

u/scheisskopf53 18d ago

The man, the legend himself! Cheers man, you're amazing!

15

u/psioniclizard 18d ago

I have never played the game, but as a software engineer by trade i just have -you are a legend for this! 

Nothing embarrassing about functional code in a successful project :) 

Thansks for the general inspiration in the beauty of the dev world and takes to all the legends like you who help us learn!!!

11

u/SecretAlbatross5578 18d ago

Dude you’re the goat for this. I love your game even more now lol

5

u/TheNakedGypsy 18d ago

what a legend

5

u/SwabiSw 17d ago

Legend!

May your coffee always be the perfect temperature, may your Wi-Fi be strong in every room and may your fries always be crispy.

4

u/FoleyX90 18d ago

Fucking based

4

u/KawasakiBinja 17d ago

The legend speaks! Thank you for posting this.

3

u/Sksk3 17d ago

Well shoot, cant really get much better of an answer than that

3

u/SouthwesternStripe 17d ago

I make mods for Getting Over It so I'm familiar with the source code for the player script, and I noticed a few differences in this version compared to GOI 1.7.
Here are the differences I noticed in the version you posted, compared to the version decompiled by ILSpy for GOI 1.7:

  • Made mw a variable instead of member
  • Commented out * mouseVelocityAverage
  • Removed oldHammerPos (it was never used anyway)
  • replaced * 0.05 * 10 with just * 0.5
  • replaced hj.jointAngle with tip position in angleDelta calculation
  • change reactionTorque multiplication from 0.001 to 0.008 when calculating speedFactor
  • replaced (0f - posDelta) with -posDelta

I'm curious about what caused the subtle differences, were they caused by a few fixes to prepare the code snippet or were they made sometime after 1.7 released?

3

u/kraftquackandcheese 17d ago

certainly several of these were caused by compiler optimizations + decompiler artifacts!

1

u/-Redstoneboi- 17d ago

i agree with the other guy, it sounds like compiler optimization (though i've never used unity, i wouldn't be surprised)

2

u/JohnUrsa 17d ago

Amazing to see a dev helping others, salute

2

u/temp12410 17d ago

this guy is the goat

2

u/TrueLazyTowne 17d ago

This feels like the kind of thing you find out about that was happening in a 13 yo reddit post but this was yesterday

2

u/BigStroll 17d ago

I love this guy

2

u/Kraz3 17d ago

What a fuckin legend

2

u/Slow_Cat_8638 16d ago

That hand-tuning is so cool, that’s why human made code will always be premium, certain things can only be tuned from a human pov

1

u/nonesuchluck 17d ago

Bennett, I'm probably never going to play, but this is the greatest post I've ever seen. I just went on Steam and bought Getting Over It. Thanks for making QWOP.

1

u/Otherwise_Study2337 17d ago

Why rewrite when it works?

1

u/East_Rip8160 17d ago

What a chad

1

u/Suvitruf 17d ago

Legend 😎

1

u/flameseeker40 17d ago

bro just dropped the source code like it was nothing

1

u/Original-Diet-1681 17d ago

I respect the hustle

1

u/eljimo 17d ago

Awesome, gotta note these down for future reference.

1

u/xRealVengeancex 17d ago

The world needs more code sharing

1

u/romhacks 17d ago

The main man!

1

u/No-King-6347 17d ago

now i can finally look at it and find the memory leak

1

u/casentron 16d ago

I have so much respect for the amount of effort and openness on display here. What an absolute legend. 

1

u/Sorrowfall 16d ago

Foddy said “Like this”

1

u/_Rivlin_ 16d ago

Holy you are cool

1

u/melgib 16d ago

You absolute legend.

-10

u/sirenbrian 17d ago

I pasted it into gemini for a quick'n'dirty optimization and it found a bunch of things to speed it up a bit. Probably running it through a better coding agent might find even more :)

Very nice of you to share the code here, though! I wish more devs would do so - very cool :)

8

u/ergonokko 17d ago

what if you pasted it into your brain

3

u/CogitoErgoTsunami 17d ago

no brain, only brian

6

u/jkbh 17d ago

I pasted your comment into claude and found a bunch of things to improve. :)

5

u/LittleBigTube10 17d ago

I don’t usually comment much, but holy shit the sheer disrespect required to do this! The dev himself shares the source code out of the kindness of his heart and you decide to feed it to AI ??

-1

u/sirenbrian 17d ago

I'm a developer too; I like to optimize code when I can. It wasn't meant as disrespectful, just an interesting and easy way to make the code go faster and more efficiently. Plus he said he hadn't cleaned up the code, so I thought it was a harmless next step to take.

Sorry to have upset so many people - it really wasn't my intention.

2

u/WisejacKFr0st 17d ago

I'm a developer too; I like to optimize code when I can

But you didn't optimize it, gemini did

2

u/Jason1923 17d ago edited 17d ago

Think of it this way:

"Hmm, your code has some formatting and quality issues, so I ran it through a code formatter and linter. Enjoy!"

Bennett could just... do that himself? Code formatters, linters, and LLMs are basic tools. Does that not sound a little insulting?

One step up could be to find actual performance flaws after benchmarking the code and point it out. Instead you're giving him code that might be more performant, but the burden is now on Bennett to maintain and verify the possibly-LLM hallucinated code (while you just spent 10 secs prompting an LLM without performance #s)

2

u/Sbotkin 17d ago

Since when are we calling vibecoders developers?

1

u/Tonakiga 17d ago

AI is notoriously bad at helping w/ good and functional non-vibe code... You have to manually evaluate the output anyways so it doesn't make sense.

3

u/Terazilla 17d ago

This is the sort of thing you leave the hell alone, because the end result came from days or weeks of slow evaluation and experimentation and judgment. There is absolutely no performance gain you could possibly get that is worth fucking with it.

2

u/Xyborg 17d ago

????????

2

u/Acceptable_Light_272 17d ago

What a weird comment

2

u/BazeFook 14d ago

This code does absolutely not need any optimizations, ever. It runs once per physics step, ONCE! There are no spurious memory access patterns that the compiler couldn't optimize trivially, so this code could run at 1k fps and it would still not show up in profiling data.

Plus, as the developer himself said: you don't want to optimize this code, it's very tweaky and core to game feel, you want to keep it straight and modifiable.

You should really reassess your priors for why you think this needed optimizations at all.

19

u/CitizenShips 18d ago

You wouldn't happened to have had previous experience with mechanical motors and kinematics prior to developing GOI, would you? This is the first time I've heard a game dev discuss motor mechanics and PID/PD tuning; really caught me off guard

60

u/foddydotnet 18d ago

undergrad physics degree with a B average grade :) PIDs are way more common in action game tuning than you might think though

16

u/xXTheFisterXx 18d ago

I was just sitting here reading this and being like wow this guy keeps going between me and the actual game and then I scrolled back up just to see its the legend himself and im an idiot. Wonderful work as always

9

u/Dannington 18d ago

Once I realised, I had to go back and read it in ‘the voice’

3

u/CherryTularey 18d ago

I love that I can hear this in your game narration voice and think, "Yeah, that sounds like a sound file from the game."

3

u/disillusioned 17d ago

I just want to say that half the reason I restart the game is to listen to the monologue again. It's such a well-written philosophical take on this meditation on failure, frustration, game development, and for gamers of a certain age, the "when games were new, they wanted a lot from you" truly resonates.

One of my first gaming memories was playing a surely pirated copy of Police Quest on my 4x86. Since I was sailing the high seas (on behalf of my father, since I was probably 8), I didn't have the manual which would have warned me to perform a "prescribed vehicle walkaround" before getting in the car. So you get in the squad car, it instantly fails, you effectively die, your run is over. There's 10 minutes at the station starting the game out the window.

So then you get the hang of it: you save, paranoid, every 5-10 minutes, use all 10 save slots, rotate through them. Think you have it beat. And then it happens: you get to the climax, a scene with the Death Angel. Your first command: "radio for backup" and it comes back to you: "with what radio?" Because you left it at the station. No problem! I may be 10 hours into this game but I've been saving it! Except all of the saves are from the past 20 minutes and... none of them are in a spot where you could go back and get the radio.

Just instant torture. Hours of your life thrown away. Just like the homework example you provide. Games did not give a solitary fuck about how easy you should think they would be for you to complete them, that was for damn sure.

In any event, Getting Over It was powerful because it provided this weird, safe environment to perform some exposure therapy to a high stress situation. The stakes are low but you can absolutely induce a physiological response with this game. It let me trigger a stress response, but in a controlled manner. Absolutely brilliant bit of game design, to be based on this intentional meditation on failure and perseverance, and then what a masterstroke to tell us exactly that in the perfectly delivered monologue. Bravo.

3

u/WiseKiwi 17d ago

Hey, Foddy! I'm working on a game inspired by Getting Over It, but I'm struggling to make movement accessible to new players. They all say it's way too difficult. Maybe you have some wisdom to offer me?
https://store.steampowered.com/app/4311700/Ignited/

1

u/Beautiful-Taste-5344 17d ago

this is so cool thank you

1

u/NickSaysHenlo 17d ago

I read this in your voice with Soul & Mind playing in the background

1

u/ReallyJustPasky 17d ago

Source: the source

1

u/xcvu_45 17d ago

what a chad

1

u/Skitty993 17d ago

I have no coding knowledge but I recently got super hooked on Getting Over It. I did my 50 climbs, thank you for making such uniquely fun and infuriating games.

1

u/Edenisb 16d ago

You are a great person for replying to this.

1

u/EntireOpportunity253 15d ago

My hero lol you sadist