"Обратное" к TraceHull

Dragokas

Меценат
Сообщения
178
Реакции
143
Здравствуйте!

Подскажите, пожалуйста, алгоритм - как получить первую позицию не-коллизии / или иное решение такой задачи.

Обрисую, как выглядит задача в целом (см. картинку).

ray.png

Т.е. цель - получить позицию для спауна игрока (ближайшую к точке B) в направлении взгляда другого игрока (точка A). Он может смотреть в стену, в предмет, куда угодно.
В целом, это решается простой функцией TR_TraceHullFilterEx.

Но, в моей задаче со * позиция клиента A уже изначально находится в состоянии коллизии для TraceHull, т.е. эта функция мне сразу же вернёт как результат - точку A.
Кроме того, где-угодно посреди комнаты могут быть другие предметы, поэтому нельзя просто так взять например, средину обычного луча (TR_TraceRayFilterEx) и затем начать TraceHull оттуда, т.к. средина также может оказаться внутри какой-нибудь коллизии.

Было бы очень круто, если б была возможность как-то провести от B до A "обратный" TraceHull таким образом, чтобы он возвращал не позицию первой коллизии, а наоборот, первую позицию, где нет коллизии.

Как вы бы решали такую задачу?

PS. Если бы в комнате не было других преград, мой код выглядел бы так:
C-подобный:
/*
    Returns the position for respawn which is located at the end of client's eyes view angle direction.
*/
bool GetSpawnEndPoint(int client, float vSpawnVec[3])
{
    if( !client )
    {
        return false;
    }
    float vEnd[3], vEye[3];
    if( GetDirectionEndPoint(client, vEnd) )
    {
        GetClientEyePosition(client, vEye);
        ScaleVectorDirection(vEye, vEnd, 0.1); // get a point which is a little deeper to allow next collision to be happen
 
        if( GetNonCollideEndPoint(client, vEnd, vSpawnVec) ) // get position in respect to the player's size
        {
            return true;
        }
    }
    return false;
}

void ScaleVectorDirection(float vStart[3], float vEnd[3], float fMultiple)
{
    float dir[3];
    SubtractVectors(vEnd, vStart, dir);
    ScaleVector(dir, fMultiple);
    AddVectors(vEnd, dir, vEnd);
}

stock bool GetDirectionEndPoint(int client, float vEndPos[3])
{
    float vDir[3], vPos[3];
    GetClientEyePosition(client, vPos);
    GetClientEyeAngles(client, vDir);

    Handle hTrace = TR_TraceRayFilterEx(vPos, vDir, MASK_PLAYERSOLID, RayType_Infinite, TraceRay_NoPlayers);
    if( hTrace )
    {
        if( TR_DidHit(hTrace) )
        {
            TR_GetEndPosition(vEndPos, hTrace);
            delete hTrace;
            return true;
        }
        delete hTrace;
    }
    return false;
}

stock bool GetNonCollideEndPoint(int client, float vEnd[3], float vEndNonCol[3], bool bEyeOrigin = true)
{
    float vMin[3], vMax[3], vStart[3];
    if( bEyeOrigin )
    {
        GetClientEyePosition(client, vStart);
 
        /* If we attempting to spawn from stucked position, let's start our hull trace from the middle of the ray */
        if( IsClientStuckPos(client, vStart) )
        {
            float vMiddle[3];
            AddVectors(vStart, vEnd, vMiddle);
            ScaleVector(vMiddle, 0.5);
            vStart = vMiddle;
        }
    }
    else {
        GetClientAbsOrigin(client, vStart);
    }
    GetClientMins(client, vMin);
    GetClientMaxs(client, vMax);
    //vMax[2] -= DUCK_HEIGHT_DELTA;

    Handle hTrace = TR_TraceHullFilterEx(vStart, vEnd, vMin, vMax, MASK_SHOT, TraceRay_NoPlayers);
    if( hTrace != INVALID_HANDLE )
    {
        if( TR_DidHit(hTrace) )
        {
            TR_GetEndPosition(vEndNonCol, hTrace);
            delete hTrace;
            if( bEyeOrigin && IsClientStuckPos(client, vEndNonCol) ) // if eyes position doesn't allow to build reliable TraceHull, repeat from the origin's
            {
                GetNonCollideEndPoint(client, vEnd, vEndNonCol, false);
            }
            return true;
        }
        delete hTrace;
    }
    return false;
}

bool IsClientStuckPos(int iClient, float vPos[3])
{
    float vMin[3], vMax[3];
    Handle hTrace;
    bool bHit;
    GetClientMins(iClient, vMin);
    GetClientMaxs(iClient, vMax);
    hTrace = TR_TraceHullFilterEx(vPos, vPos, vMin, vMax, MASK_PLAYERSOLID, TraceRay_NoPlayers);
    if( hTrace ) {
        bHit = TR_DidHit(hTrace);
        delete hTrace;
    }
    return bHit;
}

public bool TraceRay_NoPlayers(int entity, int contentsMask)
{
    return (entity > MaxClients);
}
 
Последнее редактирование:

tonline_kms65_1

Участник
Сообщения
439
Реакции
173
У тебя луч в бесконечность(RayType_Infinite), почему он должен остановиться? Он не остановится. Остановишь его в конце коробки(скайбокса).
Не совсем понятен вопрос, луч прошел одну стену, вторую, около 3 стены должен остановиться?
По идее, сам луч не должен тогда останавливаться вообще, просто по ходу движения луча, все что по пути встречается писать в массив, и потом уже работать с массивом. Зачем еще одна трассировка?
 

Dragokas

Меценат
Сообщения
178
Реакции
143
@tonline_kms65_1, не-не, RayType_Infinite у меня у обычного луча.
#1 (оранжевый) и #2 (оранжевый) - это не стены, а преграды для коробки (TraceHull-a). Обычный луч через них проходит нормально. Он остановится у #3 (желтый) - это реально стена.
Дальше у меня идёт луч TraceHull, там уже не RayType_Infinite, а координата, хотя в моей схеме вы правы, можно было бы ограничиться одним TraceHull с RayType_Infinite.

Зачем еще одна трассировка?
Первая трассировка (обычным лучём), чтобы найти координату стены.
Вторая - собственно TraceHull-ом, чтобы найти координату рядом со стеной, где для игрока не будет коллизии.
все что по пути встречается писать в массив, и потом уже работать с массивом
Да, это идея. Спасибо.
Жаль, что в фильтр вместе с entity сразу не попадает заодно и координата. Ведь нужная мне будет == 0 (World).
Получается, мне потребуется запомнить сколько раз вызывался фильтр, потом выполнить идентичную трассировку и вернуть в фильтр true в момент, когда произойдёт этот самый последний вызов, тогда луч остановится на самом последнем препятствии, а не на самом первом.
 

tonline_kms65_1

Участник
Сообщения
439
Реакции
173
Кстати, в обновлённом sourcemod'е есть много интересного, я то работаю со старым(у меня DM, а он с новым не работает), я как-то проверял, можно даже материал в месте прицела получить, и отлично работает.

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

Dragokas

Меценат
Сообщения
178
Реакции
143
Кстати, в обновлённом sourcemod'е есть много интересного, я то работаю со старым(у меня DM, а он с новым не работает), я как-то проверял, можно даже материал в месте прицела получить, и отлично работает.
Да, есть такое, но без понимания геометрии уровня "профи", тяжело понять, как работают некоторые из них.
Например, TR_GetFractionLeftSolid
C-подобный:
float TR_GetFractionLeftSolid(Handle hndl)

Returns the time fraction from a trace result when it left a solid. Only valid if trace started in solid
Очень уж смахивает на мой начальній вопрос. Только, что значит фракция времени, как и где её можно далее применить, не имею понятия.
Сообщения автоматически склеены:

Интересный вопрос, проще говоря, тебе нужно получить расстояние между препятствиями.
Я как-то делал нечто похожее, получить нужно было высоту стены(браша, т.е. мира) и её ширины, что бы определить как с этим препятствием поступить(обойти, перепрыгнуть, и т.д.), так же всё собирал в массив и потом уже с данными из массива работал.
Зачётный жук ))
 

tonline_kms65_1

Участник
Сообщения
439
Реакции
173
Только, что значит фракция времени, как и где её можно далее применить, не имею понятия.

Предполагаю что это время прохождения трассировки от начала твердости до конца этой-же твердости(возможно браша), т.е. размеров препятствия на пути трассировочного луча.
Если, к примеру, габариты любых ENT можно получить при помощи:

C-подобный:
GetEntPropVector(ent, Prop_Send, "m_vecMins", min); //'здесь тоже не пойму, зачем клиенту отправка, мне кажется вполне можно получить из _Data на сервере
GetEntPropVector(ent, Prop_Send, "m_vecMaxs", max);
реально раздражает тупое форматирование кода на этом сайте
такое к брашам(миру) не представляется возможным, поэтому нужны другие варианты.

Если к примеру, взять игровое время(от начала трассировки) - время прохождения = то время, которое можно использовать для определения дистанции до объекта(или браша).
Ну это так, навскидку, вариантов то много. Жалко что DM не поддерживается больше Баллопаном, приходится сидеть на старом соурсмоде.
 
Последнее редактирование:

Dragokas

Меценат
Сообщения
178
Реакции
143
Как-то очень странно, что в геометрии здесь появилось такое понятие как время, вместо расстояния.
Следовательно, напрашивается, очевидный вопрос, как измерить скорость луча, для получения расстояния и соответственно самой координаты выхода из solid поверхности.
Но что-то мне подсказывает, что этот луч уложиться за один тик.
Сообщения автоматически склеены:

PS. То чувство, когда тебе дали в руки линейку и циркуль, но ты не знаешь как ими пользоваться, чтобы нарисовать диаметр:DDD
 

tonline_kms65_1

Участник
Сообщения
439
Реакции
173
Как-то очень странно, что в геометрии здесь появилось такое понятие как время, вместо расстояния.
Следовательно, напрашивается, очевидный вопрос, как измерить скорость луча, для получения расстояния и соответственно самой координаты выхода из solid поверхности.
Но что-то мне подсказывает, что этот луч уложиться за один тик.
Сообщения автоматически склеены:

PS. То чувство, когда тебе дали в руки линейку и циркуль, но ты не знаешь как ими пользоваться, чтобы нарисовать диаметр:DDD
Это моё предположение, по принципу зависимости время, расстояние, скорость. Ты на этом не зацикливайся.
Я поэтому стараюсь не пользоваться стандартными ф-ми sm, если не знаю как они работают, лучше сделать свои, пусть и медленнее - зато понятно.
А уж если очень заинтересовало - аналоги поискать, сишные библиотеки, их много, примеры всегда можно найти.
 
Сверху