MrTGN / unlimaginmod

Advanced Killing Floor game modification and large-scale flexible modification platform
Other
4 stars 0 forks source link

Доработка BallisticCollision #490

Open MrTGN opened 7 years ago

MrTGN commented 7 years ago

Update: смотреть "выводы" в 3м комментарии. Разработчики Red Orchestra Ostfront 41-45 (RO далее) в свое время написали хорошую систему нанесения урона по частям тела человека. Она довольно простая, но эффективная. Из себя она представляет список цилиндров заданных размеров, прикрепленных к костям или сокетам монстра. Обнаружение попадания по тем или иным HitPoint производится через функцию HitPointTrace, которая возвращает массив с HitPoint, которые были задеты при трассировке.

В настройках HitPoint следующие параметры:

/*==========================================
* Red Orchestra hit detection system
*=========================================*/

// Hit point types
enum EPawnHitPointType
{
    PHP_None,
    PHP_Head,
    PHP_Torso,
    PHP_Arm,
    PHP_Leg,
    PHP_Hand,
    PHP_Foot,
};

// Information for each specific hit area
struct native PawnHitpoint
{
    var() float             PointRadius;        // Squared radius of the head of the pawn that is vulnerable to headshots
    var() float             PointHeight;        // Distance from base of neck to center of head - used for headshot calculation
    var() float             PointScale;
    var() name              PointBone;
    var() vector            PointOffset;        // Amount to offset the hitpoint from the bone
    var() float             DamageMultiplier;   // Amount to scale damage to the player if this point is hit
    var() EPawnHitPointType HitPointType;       // What type of hit point this is
};
  //PawnHitpoint
var()   array<PawnHitpoint>     Hitpoints;          // An array of possible small points that can be hit.

Из комментариев к переменным все вполне понятно. Но там есть неточности. Описание, видимо, копировалось из кода функции IsHeadShot. PointRadius - это радиус в квадрате зоны этого HitPoint. PointHeight - это расстояние от центра до края HitPoint, т.е. пол высоты цилиндра.

Подозреваю что и сама HitPoint система работает по тому же принципу что и логика функции IsHeadShot, с той лишь разницей, что она перебирает все возможные зоны на пути трейса и проверяет пересечет ли она их. Т.е. как таковых HitPoint "цилиндров" на модели нет. Просто идет перебор всех HitPoint от положения их кости и проверяется пересечение с траекторией трейса.

С одной стороны плюс этой системы в том, что она не спавнит никаких лишних объектов на Pawn, зато минус в том, что производит много расчетов. Видимо из-за этого они и убрали эти расчеты в нативный код на C++ и я не могу посмотреть что конкретно там считается.

MrTGN commented 7 years ago

Судя по-всему я правильно предположил причину того, почему убрали HitPoint в нативный код. Вот что из себя представляет функция IsHeadShot уже после моей оптимизации:

// Overridden so that anims don't get interrupted on the server if one is already playing
function bool IsHeadShot(vector Loc, vector Ray, float AdditionalScale)
{
    local   float   RayDotLoc, RayDotRay;

    if ( bDecapitated || HeadBone == '' )
        Return False;

    if ( Level.NetMode == NM_DedicatedServer )
        CheckDedicatedServerAnim();

    UpdateHeadLocation();
    if ( bUseOnlineHeadShotCheck )
        AdditionalScale = Square(HeadRadius * HeadScale * AdditionalScale * OnlineHeadshotScale);
    else
        AdditionalScale = Square(HeadRadius * HeadScale * AdditionalScale);

    // Express snipe trace line
    Ray *= 2.0 * CollisionHeight + 2.0 * CollisionRadius;
    // Find Point-Line Squared Distance
    Loc = HeadLocation - Loc;
    RayDotLoc = Ray dot Loc;
    if ( RayDotLoc > 0.0 )  {
        RayDotRay = Ray dot Ray;
        if ( RayDotLoc < RayDotRay )
            Loc = Loc - Ray * RayDotLoc / RayDotRay;
        else
            Loc -= Ray;
    }

    Return (Loc dot Loc) < AdditionalScale;
}

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

По той же причине у них PointRadius - это радиус в квадрате.

MrTGN commented 7 years ago

В общем, все это я к тому, что даже в нативном коде в HitPoint системе все равно довольно много вычислений. И большая нагрузка на сервер. Не зря ведь они это убрали в нативный код. А теперь представим что на сервере одновременно в тела 3-5 монстров влетает сразу 20-30 снарядов из дробовиков. Представляете сколько расчетов одновременно серверу придется произвести? Так что получается, что идея с HitPoint конечно хороша, но тоже далеко не идеальна из-за большой нагрузки расчетами.

Выводы

Получается, что мне нужно переработать BallisticCollision таким образом, что бы он не блокировал снаряды, а блокировал Trace. И при попадании снаряда в монстра производить TraceActors на расстояние его коллизии. Это позволит мне быстро получать список того, чему наносить урон и сколько энергии снаряд потратит. При этом, на это будет тратится значительно меньше ресурсов. Ведь цилиндры BallisticCollision прикреплены к костям и обновляются движком автоматически. Т.е. не нужно искать и рассчитывать их положение, а так же рассчитывать пересечения. Конечно это BallisticCollision - это спавн лишних объектов на монстре, зато избавление от расчетов. Думаю, это вполне стоит этого. Лучше один раз заспавнить несколько (пусть даже 6) цилиндров BallisticCollision и просто производить Trace через них, чем постоянно, пусть даже и нативно, рассчитывать все это.

MrTGN commented 7 years ago

Так. Закончил переработку логики BallisticCollision и провел хорошую оптимизацию логики базовых классов снарядов. Теперь все это нужно тестировать, ибо изменение кардинальные. Так же, надеюсь, оптимизация снарядов хорошо скажется на производительности, ибо я постарался максимально убрать лишнее использование переменных и лишние вызовы.