
Фундаментальный элемент механики игры Getting Over It with Bennett Foddy — это молот главного героя. Именно этот инструмент служит основным средством взаимодействия с миром: с его помощью персонаж перемещается, преодолевая горы и абстрактные нагромождения объектов. Недавно разработчик проекта решил приоткрыть завесу тайны и поделился техническими деталями реализации этой системы.
Reddit представляет собой экосистему сообществ, именуемых подреддитами. Платформа придерживается идеологии пользовательского контента, позволяя каждому не только создавать публикации, но и запускать собственные тематические площадки для обсуждений. С момента введения открытой регистрации сообществ в начале 2008 года пользователи активировали более 100 тысяч подреддитов, охватывающих всё — от массовых интересов до узкопрофильных ниш.
Типичный пример узкоспециализированного сообщества — /r/howdidtheycodeit. Судя по названию, этот форум создан для начинающих разработчиков, которые стремятся разобраться, как реализовать ту или иную «магическую» программную механику. Участники совместно анализируют алгоритмы, предлагают варианты прототипирования и дискутируют о возможных архитектурных решениях.
Популярность сообщества лучше всего иллюстрируется его топовыми постами. До недавнего времени лидерство удерживала публикация о нестандартном эффекте WebGL, позволяющем отслеживать положение окон браузера на рабочем столе. Эксперты в комментариях сошлись на том, что это результат синергии window.getScreenDetails() и localStorage, обеспечивающего синхронизацию данных между окнами, либо комбинация получения системных координат окна и событий storage.
Однако лидеры рейтинга сменились: на вершину поднялся вопрос реддитора о механике молота в Getting Over It. Неожиданно для всех в дискуссию вступил сам создатель игры, Беннетт Фодди.
Фодди известен как автор QWOP — проекта, где управление ногами бегуна превращалось в издевательски сложный процесс. Браузерная версия 2008 года обрела феноменальную популярность: к 2011 году сайт разработчика насчитал 30 миллионов посещений. Позже Фодди экспериментировал с похожими концепциями в CLOP (симулятор единорога) и GIRP (скалолазание), пока не довёл эту идею до абсолюта в Getting Over It, где игрок управляет обнажённым мужчиной по имени Диоген, запертым в котле.
В то время как ранние игры требовали филигранной работы с клавиатурой, Getting Over It полностью полагается на мышь или тачпад. Управление положением молота через мышь позволяет совершать резкие махи, но из-за высокой чувствительности и физической модели развитие мышечной памяти требует колоссального терпения.
Беннетт Фодди не просто дал совет — он выложил фрагмент исходного кода функции fixedupdate, подробно описав внутреннее устройство системы. По словам автора, он сам уже не помнит всех нюансов реализации, но суть такова: обратная кинематика рук не влияет на физику напрямую; молот приводится в движение вращательными и поступательными сочленениями, а всё управление базируется на принципах простого пропорционально-дифференциального регулятора.
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.reactionTorque0.008f ,5f); //had to make this stronger, no idea why
float axisBlend = Mathf.Pow(dotProd,4);
slider.motorSpeed = Mathf.Clamp(-posDeltaMathf.Abs(posDelta) speedFactor axisBlend,-50f,50f);
sj.motor = slider;
oldAngle = angleDelta;
В игре не используется системный курсор: вместо него введено «фиктивное» тело fakeCursorRB, которое накапливает относительные движения мыши и ограничено радиусом в 3,5 единицы вокруг игрока. Это решение нивелирует влияние перемещения камеры на управление персонажем. Фодди также предусмотрел «мёртвую зону» в центре вращения, где чувствительность динамически регулируется, чтобы избежать резких рывков при малых движениях.
Интересно, что физика игры работает на более высоких частотах, чем стандартная частота обновления монитора. Фодди использовал FixedUpdate с активным continuous collision detection и дробными временными шагами, так как контроллер рассчитан на очень высокие скорости вращения и перемещения. Корневой объект персонажа перемещается, но его вращение жестко ограничено, иначе реактивный момент от молота сделал бы геймплей неуправляемым.
Рукоять молота — это набор физических тел, соединённых через fixed joint 2D для обеспечения минимальной гибкости. В то же время руки персонажа реализованы исключительно через обратную кинематику: они «прилипают» к молоту постфактум, не оказывая силового воздействия на физическую модель. Моторы вращения и скольжения постоянно стремятся привести молот к цели, заданной мышью, но благодаря физическому движку и столкновениям с окружением именно эти попытки «вытягивают» котел с персонажем вверх.
На вопрос о глубоких познаниях в теории автоматического управления (ПИД-регуляторах) Беннетт ответил со скромностью: все знания были почерпнуты из университетского курса физики, который он в своё время закрыл на средние оценки, а многие параметры в итоговом коде были подобраны исключительно эмпирическим путём в процессе бесконечных экспериментов.

