Итак, предстоит длинный заумный пост, рассказывающий об устройстве физики в игре, актуальной к версии 068R2. Ленивым просьба не тратить время Остальным посоветую вооружиться чашечкой кофе, кружечкой чая или бутылочкой пива по выбору. Пристегнулись. Поехали.
Часть Первая
редакция 3
Пожалй начну с того, что перечислю основные переменные и константы, учавствующие в процессах, о которых далее пойдет речь.
Переменные:
TPlayer - класс, в котором прописываются все основные свойства игрока
(
inertiaX, inertiaY - импульсы влияющие на движение модельки игрока в пространтсве. Далее - инерции.
x, y - координаты игрока в пространстве
speedjump - счеткик прыжков распрыга игрока.
injump - микрозадержка для того, чтобы не было двойного срабатывания за один прыжок.
speed - тут я просто храню текущую скорость игрока, для справки.
dir - (от direction) показатель стороны куда мы смотрим и как мы это делаем. 0 или 1 - бежим влево или вправо соответственно. 2 или 3 - стоим и пялимся влево или вправо соответственно.
)
players - массив игроков типа TPlayer.
players[0]. - обращение к конкретному игроку.
id - в данном случае индекс игрока, к которому мы обращаемся при переборе.
Другими словами players[id]. - это игрок, которого мы сейчас обсчитываем.
Константы:
GRAVITY = 0.02 - гравитация.
PLAYERINITSPEED = 3 - начальная скорость игрока.
PLAYERMAXSPEED = 5 - максимальная скорость игрока.
Дебажные константы:
DEBUG_SPEEDJUMP_Y = 0.012 - множитель распрыга, влияющий на вертикальное смещение.
DEBUG_SPEEDJUMP_X = 0.33 - множитель распрыга, влияющий на горизонтальное смещение.
DEBUG_SPEEDJUMP_MAX = 7 - Тут меня верно поправил Rh. Поэтому я опишу другими словами Эта константа определяет максимальное количество прыжков распрыга, при которых идет набор скорости. При его достижении скорость более не увеличивается, а остается постоянной.
Пару слов об инерциях.
Для удобства, буду называть их ниерцияУ и инерцияХ. ИнерцияУ это как пинок под зад Точнее сила этого пинка. Пока она больше ноля, мы летим вверх, но она постепенно уменьшается и, уходя в минуса, тянет нас к земле. ИнерцияХ толкает нас вправо, если она положительна и влево, если она меньше ноля. Получать инерции игрок может как от зажатия кнопок движения, так и от внешних факторов (попадание пули, взрывная волна, джамппад,...).
Забегая вперед скажу, что именно инерции определяют величину смещения модельки игрока между тиками. Инерция в игре максимально может быть близкой к 5. Все что больше пяти обрезется. В случаее инерции равной 6 и больше, начинаются системные ошибки.
Переходим к водным процедурам.
Самой главной процедурой нфк является DXTimerTimer, этот кусок кода срабатывает при каждом "тике" игры, а это несколько раз в секунду. Почти весь игровой процесс протекает либо в ней самой, либо в вызываемых из нее процедурах.
В алгоритмах обсчета физики игрока участвуют несколько процедур, та что пересматривает смещение игрока в пространстве имеет имя "playerphysic". Она вызывается из PredictNetworkPlayerPos при прогнозировании координат игрока в сетевой игре. Но главным местом ее вызова является процедура playermove.
Но расскажу для начала про playerphysic, а уж потом вернусь к playermove, чтобы картина выглядела понятнее.
playerphysic
В самом начале playerphysic в переменные defx и defy запоминаются текущие координаты игрока. Что бы иметь под рукой значения предшествующие влиянию процедуры. Далее на инерциюУ влияет прежде всего гравитация :
Code
players[id].InertiaY := players[id].InertiaY + (Gravity*2.80);
2.80 - величина постоянная. Gravity, как уже было упомянуто = 0.02 и тоже во время выполнения программы не меняется. После этого идет проверка, мол если вертикальная инерция между -1 и 0, то она делится на 1.11
Code
if (players[id].inertiay > -1) and (players[id].inertiay < 0) then players[id].inertiay := players[id].inertiay/1.11 // progressive inertia
В другом случае, при условии, что инерцияУ больше ноля но меньше максимальной скорости, то она умножается на 1.1
Code
else if (players[id].inertiay > 0) and (players[id].inertiay < PLAYERMAXSPEED) then players[id].inertiay := players[id].inertiay*1.1; // progressive inertia
Эти действия позволяют сделать иллюзию ускорениие импульса прыжка, его затухание и ускорение свободного падения. При положительной инерции мы резво взлетаем вверх (*1.1). Когда инерция иссякает, мы зависаем на пике(/1.11) и, в падении отрицательная инерция с каждым тиком ускоряется (+Gravity*2.80).
Так работает изменение инерции по вертикали, но есть еще и горизонталь. ИнерцияХ принимается во внимание, если она по модулю больше 0.2 (т.е. больше 0.2, при движении вправо и меньше -0.2 при левостороннем движении). Если инерцияХ меньше, она обнуляется, считается, что игрок больше не перемещается по горизонтали. Вместе с тем же впервые дает о себе знать наличие распрыжки, тем что также обнуляются,ведь мы отановили свое вдижение.
Вернемся к существенной инерцииХ. Если мы больше не жмем движение (dir = 2 или 3) тогда высчитывается торможение.
Code
если мы стоим на земле - players[id].InertiaX := players[id].InertiaX / 1.14
в противном случае, мы в воздухе - players[id].InertiaX := players[id].InertiaX / 1.025
Т.е. коеффициент трения в нфк равно 1.14 , а сопротивления воздуха 1.025 Тут же почему-то выссчитывается скорость торможения трупа
Code
if(players[id].dead > 0 ) and (isonground(players[id])) then players[id].InertiaX := players[id].InertiaX / 1.03;
Трупы в нфк - существа особые, трение на них влияет иначе, а сопротивление воздуха и подавно уже не колышит. Это и понятно, им уже действительно все равно. Внимание! Здесь мы подошли к очень важному моменту, т.к. именно тут параметры распрыга влияют на игрока. Если количество зачисленных как распрыг прыжков больше одного (players[id].speedjump > 1), тогда физика претерпевает изменения.
Code
players[id].inertiay := players[id].inertiay + (players[id].speedjump * DEBUG_SPEEDJUMP_Y);
и в зависимости от того, в какую сторону мы прыгали, либо налево
Code
players[id].x := players[id].x - (players[id].speedjump * DEBUG_SPEEDJUMP_X)
либо направо
Code
players[id].x := players[id].x + (players[id].speedjump * DEBUG_SPEEDJUMP_X)
И только после этого инерции влияют на новые кординаты игрока
Code
players[id].y := players[id].y + players[id].inertiay;
players[id].x := players[id].x + players[id].inertiax;
Еще раз подчеркну одну особенность. DEBUG_SPEEDJUMP_Y влияет на инерциюУ, а DEBUG_SPEEDJUMP_X напрямую на players[id].x
Тут видимо необходима историческая справка.
Изначально я делал распрыг, основываясь только на модификации инерции. Но результаты тех дней были настолько разительно неправдоподобны, что я придумал, как отделить распрыг от влияния торможения и ускорения. Придавая большую инерцию, я получал большой рывок, но практически мгновенное его затухание, что приводило к ущербным подергиваниям модельки.
Дальше идут не столь важные в нашем случае проверки на препядствия и нахождение в жидкостях. Упомяну лишь, что во всех случаях кроме столкновения с потолком, счетчик распрыгов обнуляется, что в свою очеред обнуляет и влияние распрыжки. Обнуляются также и инерции в зависимости от того было ли столконовение с горизонтальной плоскостью или вертикальной.
Сделаем паузу, скушаем твикс и хлебнем топлива, если кто зачитался и забыл это сделать. Дальше.
Playermove
Вернемся к playermove. Напомню, что playermove "главнее", чем playerphysic так как она вызывает последнюю, а не наоборот. Поэтому действия перечисленные тут, по времени выполнения будут предшествовать всяческим смещениям, происходяжим в playerphysic.
Итак, вначале там идет всякое бла-бла-бла, а потом дикое условие (тут надо поднапрячься):
Если зажат "moveup первого игрока" И мы - первый игрок ИЛИ зажат "moveup второго игрока" И мы второй игрок, ТОГДА при условии что мы НЕ В ПРЫЖКЕ, ровно как если мы тупо прыгающий бот, следующий код будет выполнен. Фухх. Говоря простым языком, дальше кусок кода выполнится только для того, кто прыгает.
А если мы в воде или лаве - обнулить распрыжку нафиг, ибо как это. о_О
Потом идет второе дикое условие, что б уж наверняка. Если при всем вышеупомянутом зажата кнопка движения, соответствующая направлению нашего прыжка И мы не в присядке, то в конце концов, счетчик распрыга увеличиться. Наконец-то.
А если вдруг все вышеперечисленные условия не удовлетворены, распрыжка также уходит в ноль, т.к. это значит, что мы явно делаем что-то не то.
Так, ага, счетчик распрыга мы крутанули. Потом, мы вызываем уже известную нам playerphysic и идем дальше.
Ниже, в числе все прочего бла-бла-бла встречается еще и такая строка как
Code
if (isonground(players[i])) then players[i].inertiay := 0; // really nice thing :)
Как бе намекающая, что если мы на земле, вниз нас уже тянуть не надо. Я пробовал ставить -1. Эффект примерно такой, как если бы пол подбрасывал нас.. землятрясение и только.