Последние рекорды серверов
Pro Nub

Физика LongJump

Опубликовано Kpoluk 13 Мая 2023 в 11:26
В статье про физику стрейфов фигурировали следующие понятия: вектор текущей скорости, направление взгляда игрока, направление wishdir, угол u между направлением wishdir и вектором текущей скорости. Поскольку статичными картинками довольно сложно описывать такой динамичный процесс, как lj, то давайте посмотрим на взаимосвязь этих понятий в движении:

GIF 31.01MB

Анимация условно показывает, как на блоке kz_longjumps2 выполняется lj, состоящий из престрейфа и пяти стрейфов. Траектория движения обозначена оранжевой кривой на земле и жёлтой в воздухе. Зелёный вектор задаёт направление wishdir, белый - направление взгляда, красный – скорость. Угол u – это угол между зелёным и красным векторами.

Проследим за тем, как меняется угол u. Пока мы разбегаемся на W, он равен нулю. Затем, когда мы набрали скорость порядка 250 юнитов/сек, мы нажимаем A. Как было выяснено в статье про физику стрейфов, в первый фрейм после нажатия A вектор wishdir повернётся на 26.565 градуса, а затем станет равным 45 градусам. Далее угол u в течение престрейфа уменьшается с 45 до некоторой величины, при которой в момент перед прыжком мы бы не потеряли скорость. Для 275-277 юнитов/сек этот угол равен примерно 27 градусам. Следовательно, угол между скоростью и направлением взгляда перед прыжком будет составлять около 45 - 27 = 18 градусов.

В момент прыжка отпускаем W, и wishdir с этого момента составляет 90 градусов с направлением взгляда. Физика стрейфов подсказывает нам, что оптимальный угол u, который даст нам максимальный прирост скорости, в начале прыжка должен быть как можно ближе к значению 88.96. Однако, если мы старались выполнить престрейф идеально, то угол u в первый фрейм в воздухе составит примерно 90 - 18 = 72 градуса, то есть нам нужно будет как можно быстрее подвернуть мышь в сторону вектора скорости, чтобы она начала расти.


Далее, в момент переключения с одного стрейфа на другой, всё тоже не так гладко, как хотелось бы. Вектор wishdir перескакивает по другую сторону от направления взгляда, и если поддерживаемый нами угол u до этого был равен 88.96, то после переключения он составит 90 + (90 - 88.96) = 91.04 градуса. Чтобы снова попасть в оптимальный угол, нам нужно, чтобы за один фрейм направление взгляда оказалось по другую сторону от вектора скорости, а мышь резко поменяла направление движения на противоположное.

Выполнить такой финт физиологически крайне сложно, поэтому на практике происходит примерно так, как показано на анимации в начале статьи: белый вектор направления взгляда догоняет красный вектор скорости (то есть мы заранее увеличиваем угол u), а после смены стрейфа замедление мыши как бы даёт время красному вектору скорости повернуться, после чего мы ускоряем мышь уже в другую сторону, и белый вектор вновь начинает погоню за красным.

А теперь давайте объединим трёхмерные графики из статьи про физику стрейфов в один. При этом нас будет интересовать скорость от 250 до 340 юнитов/сек, а угол u пусть меняется от 110 до -110 (положительный для левого стрейфа и отрицательный для правого, для отрицательных значений u полученные ранее графики достаточно симметрично отразить):


При разбеге на скорости 250 юнитов/сек угол u близок к 45 градусам, и прирост скорости оказывается где-то возле нуля (жёлтая область). Затем угол уменьшается, мы поднимаемся на графике в оранжевую зону и, поскольку скорость растёт, начинаем двигаться в сторону плато. При подходе к плато мы следим за тем, чтобы u не уменьшился слишком сильно, иначе провалимся в яму, где резко потеряем скорость. Затем в воздухе мы попеременно оказываемся то на одном оранжевом холмике, то на другом, продолжая двигаться в сторону увеличения скорости. При неудачной смене стрейфа мы можем оказаться как на жёлтом плато, где прироста скорости нет, так и в яме, где скорость теряется.

И тут появляется интересная идея – что если во время разбега на W перед нажатием A повернуться вправо? Ведь тогда после нажатия клавиши A угол u будет сразу меньше 45, то есть на графике мы с самого начала окажемся в оранжево-красной зоне! В таком случае скорость наберётся быстрее, а дуга, по которой происходит разбег во время престрейфа, станет меньше. Это удобно на таких картах, как prochallenge_longjump, где блоки идут один за другим, так что чем меньше времени потратится на разбег, тем быстрее будет рекорд. Впрочем многие поклонники lj применяют эту технику и на kz_longjumps2.

Мировой рекорд


Перейдём от теории к реальным прыжкам и посмотрим на анимацию, построенную по данным из демки, где DeathClaw взял с помощью lj блок в 257 юнитов (что на момент написания статьи является мировым рекордом, скачать демку можно здесь):

GIF 6.81MB

В левом верхнем углу v – это скорость в юнитах/сек, u – это угол в градусах между зелёным и красным векторами (цвета здесь те же, что и на анимации в начале статьи). Также слева видно, какие кнопки нажимает DeathClaw и как при этом меняется скорость (чем ярче зелёный цвет, тем больше прирост, чем ярче красный, тем больше потери, в начале и в конце прыжка можно заметить серый цвет – там скорость не менялась). Воспроизведение lj замедлено в 20 раз.

Хорошо видно, как при разбеге DeathClaw уводит мышь вправо, после чего нажимает A и, поскольку угол u сразу оказался меньше 45 градусов, быстро достигает 265 юнитов/сек. Затем угол u растёт (иначе DeathClaw попал бы в яму, которую мы видели на трёхмерном графике). В какой-то момент скорость превышает 276 юнитов/сек, однако угол u оказывается слишком большим (порядка 33 градусов), и перед прыжком скорость немного теряется. После же прыжка DeathClaw не сразу отпускает W, из-за чего в самый первый фрейм в воздухе скорость не меняется, и на анимации фрейм имеет серый цвет. Что интересно, даже если бы DeathClaw отпустил W раньше, то в первый фрейм прироста скорости всё равно бы не было, так как угол u ещё не успел достичь того холмика на трёхмерном графике, где происходит прирост скорости. А если бы DeathClaw не начал увеличивать u перед прыжком, то серых фреймов после прыжка могло быть два или даже больше. Так что престрейф, больший 275 юнитов/с, не обязательно даёт лучший результат. К примеру, FreestyleR на том же 257 блоке имел перед прыжком скорость 276.28 юнитов/с и угол u = 30.85 градуса, а после прыжка u был равен сначала 33.32 (FreestyleR тоже не сразу отпустил W), а затем 82.72, немного не дотянув до диапазона, где начинается прирост скорости - итого два серых фрейма.

Смена стрейфа также не сильно отличается от нашего прогноза - пока мышь замедляется, красный вектор скорости оказывается по другую сторону от белого вектора, вдоль которого смотрит DeathClaw. Затем мышь разгоняется, белый вектор догоняет красный, а прирост скорости постепенно повышается, изредка срываясь в потери. На трёхмерном графике эти потери выглядят как скатывание с оранжевого холмика в синюю яму.

Максимальная дистанция


До сих пор мы отталкивались от оптимального угла u просто потому, что его поддержание давало в каждый момент наибольший прирост скорости. Поскольку скорость в каждый момент максимальна, то максимальной будет и длина траектории. Однако дистанция lj – это не длина кривой, а расстояние от точки, в которой мы оттолкнулись от земли, до точки приземления. Насколько оптимальный u подходит для получения максимальной дистанции? Чтобы понять это, нам нужно научиться оценивать дистанцию прыжка при заданном u. Начнём с того, что вспомним, как происходит векторное сложение при наборе скорости:

Здесь V - текущая скорость, dV - прибавка к скорости в данном фрейме, Vnew - результирующая скорость. Обобщая результаты, полученные в статье про физику стрейфов для скорости V = 275, мы можем утверждать, что прирост скорости начнётся при u > arccos(30 / V), достигая максимума при оптимальном u = arccos(5 / V). Для этого диапазона углов можем записать:

dV = wishspeed - currentspeed = 30 - V * cos(u)

По теореме косинусов

Vnew^2 = V^2 + dV^2 - 2* V * dV * cos(180 - u)

Подставляя сюда dV, получаем

Vnew^2 = V^2 - V^2 * cos(u)^2 + 900
Vnew = sqrt(V^2 * sin(u)^2 + 900)

Этот результат нам ещё пригодится в статье про физику слайда. А пока что из него мы можем увидеть, что максимум Vnew действительно приходится именно на u = arccos(5 / V): при фиксированной скорости V с ростом u увеличивается sin(u), а значит растёт Vnew, достигая значения

Vnew = sqrt(V^2 + 875)

Если же взять u > arccos(5 / V), то, как мы помним из функции PM_AirAccelerate, dV станет равным 25, и мы будем продолжать иметь прирост скорости вплоть до значения u, при котором Vnew станет равным V. Из теоремы косинусов V^2 = V^2 + 25^2 - 2 * V * 25 * cos(180 - u), откуда максимальный u = arccos(-12.5 / V), то есть чуть больше 90 градусов при V > 250 юнитов/с.

Для наглядности давайте теперь получим конкретные значения u для набора скоростей V:


Примечание: в kreedz встречаются сервера, на которых при прочих легальных настройках значение sv_airaccelerate изменено с 10 на 100. Для функции PM_AirAccelerate это означает, что при wishspd = 250 получается accelspeed = 250 > 30, поэтому прирост скорости продолжает расти вплоть до u = 90°, а максимальный u = arccos(-15 / V). Таким образом, диапазон угла u, дающий прирост, оказывается шире, да и максимум прироста при этом выше. Это может дать несколько дополнительных юнитов для lj, а для slide эффект просто потрясающий (о том, как скорость набирается на слайде, поговорим в отдельной статье). Интересно также то, что по сути для такого эффекта достаточно sv_airaccelerate 12 (при этом значении accelspeed = 30), дальнейшее увеличение на физику не повлияет.

Отлично, теперь мы знаем как будет меняться скорость в течение прыжка. Физика стрейфов указала нам, что для поддержания оптимального u в воздухе нужно поворачивать мышь примерно на 5 градусов за фрейм. При этом длительность стандартного lj составляет 73 фрейма. Задавшись престрейфом и количеством стрейфов, мы можем отсюда определить, куда был направлен вектор скорости в каждый момент времени. Спроецировав её на направление прыжка (спроецированную скорость будем обозначать Vx) и проинтегрировав результат, мы получим не что иное, как дистанцию прыжка.

Примечание. В изложенных далее примерах учтено, что во фрейм, когда происходит нажатие клавиши, wishspeed равен не 250, а 200 (см. статью про физику стрейфов), оптимальный угол u = arccos(10 / V), а скорость вычисляется как Vnew = sqrt(V^2 + 800). Угол же порота скорости за один фрейм составит не 5, а 4 градуса.

1) Возьмём престрейф 275 юнитов/с и 4 одинаковых стрейфа, каждый займёт примерно 18 фреймов, ширина стрейфа составит около 90 градусов (45 влево и 45 вправо), то есть траектория прыжка будет очень извилистой. Рассчитаем скорость Vx по приведённым формулам и отобразим её на графике:


Здесь для сравнения я также вывел Vx для 257 блока DeathClaw. На графике видно, что большую часть стрейфа Vx растёт, а затем падает (причём и у DeathClaw тоже, то есть это явление нормальное и неизбежное). При этом несколько раз наша Vx даже обгоняет DeathClaw по величине. Это происходит в те моменты, когда направление скорости совпадает с направлением прыжка, то есть когда Vx = V. Ничего удивительного в этом нет, ведь мы поддерживали угол u, максимизирующий V. А вот смогли ли мы обогнать DeathClaw по дистанции? Для её расчёта мы интегрируем Vx, то есть по сути просто считаем площадь под графиком. Для нашего прыжка в 4 стрейфа в результате получим 240.88 юнита. Тут уже становится понятно, что для четырёх стрейфов поддержание оптимального u не лучшая идея. Справедливости ради нужно отметить, что мы могли бы начать с Vx такой же, как у DeathClaw, и получить 241.47 юнита (правда тогда первый стрейф стал бы короче, и в конце пришлось бы начать делать пятый стрейф), что впрочем тоже не впечатляет. Ещё один хитрый приём - в момент, когда Vx на последнем стрейфе достигает максимума, мы просто отпустим все клавиши стрейфа, сохраняя значение Vx неизменным до конца прыжка (график закончится как горизонтальная линия). Так дистанция увеличится до 244.10 юнита, что уже лучше, но всё ещё слабовато.

2) А что если попытаться повторить стрейфы DeathClaw, но с оптимальным u? Берём его престрейф 275.23 юнита/с, 8 стрейфов, каждый подгоняем по длительности (9-10 фреймов), плюс первый стрейф начинается так, что спроецированная скорость Vx будет максимально похожа на начальную Vx DeathClaw (271.09 юнита/c). Расчётная ширина наших стрейфов составит 40 градусов (а точнее 38, так как крайние фреймы дают по 4 градуса), в то время как у DeathClaw она уменьшается с 30 до 22 градусов в течение полёта. Сравним Vx по графику:


На этот раз наша дистанция составит 263.45 юнита (а если отпустить последний стрейф в пике Vx, то 263.83). В то же время для lj DeathClaw расчёт по Vx даёт 257.234 юнита. Это значение отличается от настоящей дистанции главным образом потому, что DeathClaw был в воздухе не ровно 73 фрейма, а ещё и часть 74 фрейма, добрав 0.7795 юнита. В том, как ему это удалось, мы сможем разобраться только после статьи про EdgeBug и JumpBug.

Итак, наши 8 нечеловеческих стрейфов смогли превзойти мировой рекорд, причём с большим запасом. Мы могли бы получить ещё более впечатляющие результаты для 18 стрейфов, но вместо этого давайте будем двигаться в сторону более реалистичной ситуации.

3) Посмотрим, как отличался угол u в предыдущем примере:


Для своего lj возьмём первый стрейф в точности как у DeathClaw, а ещё 8 стрейфов сделаем одинаковыми по 8 фреймов и шириной в 28 градусов каждый. При этом изменение u для них позаимствуем у какого-нибудь наиболее удачного стрейфа DeathClaw, например у пятого. Найдя Vx в каждом фрейме и посчитав на его основе дистанцию, получим 258.226 юнита. Если учесть ещё и ту прибавку, что можно получить за часть 74 фрейма, то результат составит чуть больше 259 юнитов. Таким образом, можно предположить, что блок 259 как раз находится где-то на пределе человеческих возможностей. Как говорят в kreedz, impossible but very very hard.

Практика


При расчётах дистанции мы брали примерно одинаковые по длительности симметричные стрейфы. Однако никто не обязывает нас делать именно так - мы могли бы начать с быстрых стрейфов по 8 фреймов, а затем перейти на стрейфы по 12, или же вообще чередовать их, нарушив симметричность. Что касается прохождения карт, то там далеко не всегда нужно выжимать максимум из дистанции, порой важнее просто приземлиться в нужной точке, чтобы начать из неё следующий разбег или сделать бхоп. И тогда вариативность в исполнении стрейфов становится просто огромной, что дарит нам большое разнообразие в стилях игроков, и в этом заключается одна из изюминок kreedz.

Несмотря на большое количество возможностей, на практике делать lj может оказаться не так просто. Как мы видели выше, смена направления движения мыши должна происходить на 1-2 фрейма позже, чем смена клавиши стрейфа. В этот момент может случиться одна из нескольких неприятных ситуаций:

1) Если мышь будет опаздывать сильнее, то в начале стрейфа появятся фреймы, когда красный вектор скорости простаивает, не меняясь, как бы ожидая когда белый вектор наконец подойдёт к нему ближе и они вместе продолжат поворачиваться. На каком-то стрейфе может оказаться, что красный вектор скорости просто не успеет достаточно повернуться, и белый вектор обгонит его настолько, что к концу стрейфа мы свалимся в потерю скорости.

2) Похожий эффект может произойти, если мы нажмём новую клавишу стрейфа, не отпустив старую. Если подобное перекрытие длится дольше одного фрейма, то мы получаем нулевую прибавку скорости, то есть опять же красный вектор просто замрёт на месте. И в каком бы направлении мы при этом не смотрели, пока старый стрейф ещё зажат, повлиять на скорость мы не сможем.

3) Если же мышь наоборот начинает движение в противоположную сторону слишком рано либо просто двигается недостаточно быстро, то после переключения стрейфа красному вектору скорости понадобится несколько фреймов для того, чтобы обогнать белый, и в итоге мы получим потери скорости в начале нового стрейфа.

Все эти ошибки накладываются друг на друга и влияют на последующие стрейфы. Зачастую игрок, который умеет прыгать 250-253 юнита, недоумевает, почему же так сложно дотянуться хотя бы до 255. При детальном анализе оказывается, что он постоянно допускает упомянутые ошибки, которые в сумме отнимают у него эти несколько юнитов. Для наглядности посмотрим на обычный прыжок в моём исполнении. Я прыгнул 250 блок в 6 стрейфов (демку можно взять здесь):

GIF 5.90MB

Здесь я собрал всю коллекцию: первый со вторым стрейфом имеют перекрытия по нажатиям клавиш, получаем серый участок, где скорость не меняется; на третьем стрейфе я вёл мышь слишком медленно и в итоге свалился в потери скорости в начале четвёртого; клавишу пятого стрейфа я нажал слишком рано, красный вектор замер и половину стрейфа просто ждал, пока белый вектор вновь приблизится к нему. Последний стрейф получился совсем узким и невнятным, с мизерным приростом.

Таким образом мы приходим к пониманию того, что чем лучше будет отработано переключение с одного стрейфа на другой, тем проще будет проходить карты, и тем меньше шанс, что стрейфы «рассыпятся» после непродолжительного перерыва в тренировках. Именно поэтому новичкам часто советуют начинать с небольшого количества стрейфов, постепенно увеличивая его. Для примера посмотрим на один из вариантов того, как это можно реализовать. Пусть игрок может делать 4 стрейфа по 18 фреймов каждый. Со временем он делает первые три стрейфа всё быстрее, пока они не станут занимать 14-15 фреймов, оставляя длинный последний стрейф. А затем ему остаётся этот последний стрейф просто подробить на два, и получится 5 одинаковых стрейфов.

В следующей статье мы разберёмся с техникой HighJump и подробнее познакомимся с трением в CS.