Closed MrTGN closed 7 years ago
Записываю, что бы не забыть. Похоже, по-умолчанию отсчет положения цилиндра коллизии идет от его центра. Т.е. вокруг определенной точки равноудаленно вверх и вниз добавляется половина высоты и по радиусу описывается окружность, что вкупе дает цилиндр вокруг центральной точки отсчета. Нужно в коде балистической коллизии учесть это и автоматически при спавне, после установки размера коллизии делать отступ на половину высоты, дабы цилиндр крепился основанием к кости. Или все же не делать этого. В общем, нужно подумать.
Записываю, что бы не забыть.
Теперь осталось в базовые классы вписать два элемента BallisticCollision для головы и туловища и подобрать для них размеры, отступы и т.д, а так же высчитать плотность.
Не забыть разобраться и поправить в базовых классах мобов значения OnlineHeadshotScale.
Записываю информацию по поводу HeadHeight, дабы не забыть. HeadHeight - это параметр, указывающий половину "высоту головы" человечка, т.е. дистанцию от шеи до центра головы. Сразу помечаю, что нам этот параметр уже не нужен (возможно нужен только для старого оружия). Этот параметр используется в bool функции IsHeadShot().
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
{
local coords C;
local vector HeadLoc, B, M, diff;
local float t, DotMM, Distance;
local int look;
if (HeadBone == '')
return False;
// If we are a dedicated server estimate what animation is most likely playing on the client
if (Level.NetMode == NM_DedicatedServer)
{
if (Physics == PHYS_Falling)
PlayAnim(AirAnims[0], 1.0, 0.0);
else if (Physics == PHYS_Walking)
{
if (bIsCrouched)
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
else
PlayAnim(IdleWeaponAnim, 1.0, 0.0);
if ( bDoTorsoTwist )
{
SmoothViewYaw = Rotation.Yaw;
SmoothViewPitch = ViewPitch;
look = (256 * ViewPitch) & 65535;
if (look > 32768)
look -= 65536;
SetTwistLook(0, look);
}
}
else if (Physics == PHYS_Swimming)
PlayAnim(SwimAnims[0], 1.0, 0.0);
SetAnimFrame(0.5);
}
C = GetBoneCoords(HeadBone);
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis);
// Express snipe trace line in terms of B + tM
B = loc;
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
// Find Point-Line Squared Distance
diff = HeadLoc - B;
t = M Dot diff;
if (t > 0)
{
DotMM = M dot M;
if (t < DotMM)
{
t = t / DotMM;
diff = diff - (t * M);
}
else
{
t = 1;
diff -= M;
}
}
else
t = 0;
Distance = Sqrt(diff Dot diff);
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
}
Логика у функции следующая.
Похоже, придется для совместимости переписать функцию IsHeadShot. Благо у меня в ней уже будет меньше расчетов, теоретически просто трейсить цилиндр головы.
В дальнейшем доработать TakeDamage в BallisticCollision!
Так, записываю важную информацию по BallisticCollision, её креплению к модели и возможностям движка, которые удалось выяснить.
Объекты класса Actor можно крепить не только непосредственно к кости модели, но и к так называемым "Сокетам", которые задаются через AnimationBrowser в UnrealEditor. Эти сокеты представляют из себя условные точки для крепления чего либо, которые находятся на заданном расстоянии и развороте от указанной кости. Эти скоеты как раз очень удобно использовать для крепления BallisticCollision.
Так. Есть очень неприятный аспект, который выяснился. По логике мода BallisticCollision должен прикрепляться к костям или сокетам. Дабы BallisticCollision не застревал и не залипал от окружающих объектов, ему желательно выставлять bool переменную bHardAttach в True. Но из-за неё выключаются компоненты коллизии bBlockActor и bBlockPlayer. Из чего следует, что снаряды перестают реагировать на соприкосновение с этими объектами. Получается что в этом случае эти объекты можно обнаружить лишь через Trace, да и то я не уверен в этом. Исходя из этого вся эта история получается ничем не лучше написания своего окружения для HitPoint системы. Ибо там при соприкосновении с цилиндром монстра через HitPointTrace можно узнать какие зоны затронет снаряд по траектории полета. Но нужно уточнить можно ли эти HitPoint крепить не к костям, а к сокетам, ибо с костями в модели беда - они все в разные стороны направлены, все оси перепутаны.
Судя по всему HitPoint'ы можно крепить к соектам. Я предполагаю, что при вызове HitPointTrace на самом деле нет никаких HitPoint физически. Думаю, что в этой функции просто идет итерация через список всех указанных в HitPoint костей или сокетов, и проверяется пересекает ли вектор Trace эти зоны. А потом возвращается затронутые сокеты/кости. Думаю, что привязка HitPoint к сокетам возможна, ибо функция GetClosestBone, например, возвращает не название кости, а как раз название ближайшего сокета в UT2004. Значит теперь в каждой модели монстра нужно создать набор HitPoint сокетов для нормальной работы этой системы и привязки к ним.
Дополнение. Все правильно я тогда понял. Даже в самом Killing Floor работают не только с самими костями модели, но и с сокетами. Например, проверка на попадание в голову производится через поиск положения сокета head модели монстра. Насчет этого сокета как раз и хотел записать что делать дальше. Сокет head сам находится в том же месте, где и сама кость головы CHR_Head. У неё перепутаны координаты, видимо так для анимации нужно было. Суть в том, что мне нужно создать в моделях монстров новые скоеты для головы HitPoint_Head, правильно их расположить и направить относительно положения модели. Так же затем нужно создать HitPoint и для других частей тела, но это уже после того, как протестирую работу на сокете головы.
Во время прочтения заметки промелькнула мысль про отстрел рук и ног у монстров, но это слишком сложно для реализации, да и на практике едва ли будет полезным.
Этим я дальше и хотел заниматься. С руками все не сложно. А вот с ногами уже сложно из-за анимации. Если ты отстрелил руки, то там просто прячется часть модели и убирается урон в ближнем бою, или он вообще отключается, а анимации остаются старые. А вот если отстрелил ноги, уже нужна анимация ползущего монстра, а их нет. Так то сделать дамаг по частям тела можно, но только по верхним частям тела оно будет работать нормально.
А, ну тогда хорошо. Но в принципе, отстрел ног не так и критичен для геймплея. А вот машущие культями Мясники это и забавно и гораздо меньше дамага от них одновременно.
Так, записываю, что бы не забыть. Проверил на модели толстяка свой сокет HitPoint_Head, вроде бы работает нормально и попадания в голову засчитываются весьма точно. Но как всегда вылезли велосипеды от разрабов KF. Почему-то в модели смещение по оси Y это на самом деле смещение по оси X в глобальной системе координат игры. Т.е. при выставлении смещения OnlineHeadShotOffset вместо смещения по Y всегда я должен указывать смещение по X.
Подключил Дебаг на положение области хэдшотов. У толстяка после моей настройки модели попадания в голову весьма точно обрабатываются. Самый косой - это клот. В общем я пока думаю имеет ли смысл вообще заморачиваться над продвинутой коллизией монстров и обратит ли кто-нибудь на это внимание. Так ли это нужно в игре, ибо учитывая кривизну моделей да и всей системы в целом, на разработку подобной вещи уйдет уйма времени.
Проставил во всех моделях сокет HitPoint_Head. Отпозиционировал и отладил коллизию у Толстяка и Босса, остальные нужно доделать. Пока ковырялся с кодом и моделями, наконец понял смысл переменной OnlineHeadShotOffset в Killing Floor. В этой переменной не просто положение центра головы выставляется, а предполагаемое положение головы во время проигрывания анимации ходьбы монстра. Такого велосипеда я точно не ожидал. Причем, велосипед это еще и потому, что в анимации ходьбы положение головы постоянно меняется, а этот OnlineHeadShotOffset всегда один и тот же.
В общем, разработчики хотел повысить производительность игры путем сомнительной экономии трафика и расчетов, а получили проблему с положением головы из-за периодически не проигрывающейся анимации на сервере, и написали такой вот велосипед.
Было бы интересно, если бы ты таким же образом покопался досконально в коде второй части, и узнал, научились ли чему-то разработчики за эти годы. Можно было бы и статью написать по этой теме на профильный ресурс.
Такс. Записываю мысли по поводу баллистической коллизии. По ходу тестирование становится ясно, что это не самое лучшее решение. Слишком уж лагает эта коллизия. Вся проблема в том, что позиции частей тела монстра на сервере и на клиенте часто отличаются. На сервере, как я понял из комментариев в коде, анимация монстров не проигрывается. И она воспроизводится лишь при попадании по нему и проверки попадания в голову. Причем, там выставляется примерная анимация, которая сейчас "наверное" проигрывается на клиентах. Это опять велосипед от разработчиков. Варианта два:
Первый вариант весьма тупой и очень неточный. Это то, что и так есть в оригинальном Killing Floor.
Как реализовать второй вариант я понимаю, но придется переработать все функции, связанные с проигрыванием анимации у монстров таким образом, что бы все события обрабатывались на сервере и отправлялись клиентам. При этом на сервер сохранялась название анимации и время начала её проигрывания. Таким образом при попадании по монстру можно будет мгновенно выставить нужный кадр нужной анимации и все будет посчитано весьма точно.
При этом, если я займусь реализацией 2-го варианта, то от баллистической коллизации, думаю, все равно нужно будет отказываться в пользу HitPoint системы. Я разобрался как она работает и если синхронизировать анимацию, то HitPoint система позволит весьма точно обрабатывать попадания снарядов не только в голову, но и в другие части тела.
Записываю релевантную информацию по теме. В коде снарядов функции Touch и HitWall сингулярные, т.е. не могу выполняться одновременно. Единовременно вообще может выполнять только одна сингулярная функция. Это сделано специально для снижения нагрузки на движок. Из-за этого иногда у снарядов, летящих с большой скоростью могу возникать "проскакивания" через баллистическую коллизию. Т.е. если они коснулись коллизии туловища, то проигнорируют коллизию головы. Эта еще одна из важны причин отказаться от баллистической коллизии в пользу HitPoint системы.
Так же по поводу анимации. Как я выяснил, анимация на сервер проигрывается только в случае, если Pawn рендерится, т.е. если есть кому на него смотреть на конкретно этой машине. Но есть специальный хак, который заставляет обновлять скелет, даже если модель не рендерится. Я его применил ко всем монстрам, если они становятся реливантны игрокам. Конечно, это может существенно увеличить нагрузку на сервер, но это уже нужно тестировать. Зато таким образом скелет модели монстра всегда теперь будет иметь примерно одинаковое состояние на всех компьютерах.
Закрываю в пользу #490.
Создать в моде набор BallisticCollision объектов. В них должно храниться значение количества энергии на 1 мм2, необходимой для прострела этой части тела снарядом. Снаряд будет брать это значение, умножать на площадь своего сечение и высчитывать сколько он потратит энергии и прострел этой части тела, и прострелит ли её вообще.
Так же в этих объектах BallisticCollision будет присутствовать функция event TakeDamage, которая будет принимать урон от столкновения со снарядами. Но пока только от столкновения, урон от взрывов радиусом и т.п. продумаем уже потом. event TakeDamage соответствующего объекта будет накладывать какой-то эффект от получения урона, т.е. вызывать какую-то функцию из кода владельца-монстра, а затем вызывать функцию получение урона уже из кода монстра. Таким образом реализовываются различные "нокауты" от попаданий в голову, снижение скорости передвижения от попадания в ноги, снижение урона от попадание в руки или оружие и т.д.
Баллистические цилиндры блокируют снаряды, являются для них целью, т.е. снаряды должны реагировать на соприкосновение с этими цилиндрами, и блокируют trace, но никак не взаимодействуют с внешним миром и другими актерами, т.е. не делают все, что делает основной габаритный цилиндр, но выполняют все остальное, что касается детальной обработки взаимодействия с моделью монстра.