Круг LineSegment Collision C ++ (UE4) - PullRequest
       33

Круг LineSegment Collision C ++ (UE4)

0 голосов
/ 10 января 2019

Здравствуйте, я пытаюсь вычислить столкновение Circle - LineSegment (в 2D координатном пространстве).

Я хочу обнаружить столкновение, когда мой агент движется, поэтому я делаю шаг за шагом -> agentPosition - это то место, где сейчас находится агент, а agentPosition + agentDelta - то, где он хочет находиться на этом шаге.

Линия определена FVertex (Да, я знаю дурное имя) testEdge (у нее 2 точки A и B).

Круг определяется как (agentPosition + agentDelta) как центр и agentRadius как его радиус.

Я пытаюсь найти точку столкновения (в среднем это будет 2 возможных точки пересечения) и вычислить необходимые параметры:

Время до столкновения ({0.0f, 1.0f}) CollisionPoint (где положение круга при столкновении) ImpactPoint (средняя точка столкновения)

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

Буду признателен, если вы поможете мне понять, что я здесь делаю неправильно.

static bool CheckAgentEdgeCollision(FVertex testEdge, FVector agentPosition, FVector agentDelta, float agentRadius, FCollisionResult2Dplease& outCollisionResult, UWorld* world = nullptr)
{
    agentPosition.Z = 0.0f;
    agentDelta.Z = 0.0f;
    testEdge.B.Z = 0.0f; testEdge.A.Z = 0.0f;

    FVector D = testEdge.B - testEdge.A;
    FVector d = testEdge.A - (agentPosition + agentDelta);

    float a = D | D;            // Operator | is Dot Product
    float b = (d | D) * 2.0f;   
    float c = (d | d) - FMath::Square(agentRadius);

    float disc = b * b - 4.0f * a * c;
    if (disc < KINDA_SMALL_NUMBER) 
    {
        return false;
    }

    float sqrtDisc = FastSQRoot(disc);

    float invA = 1.0f / ( a * 2.0f );

    float t0 = (-b - sqrtDisc) * invA;
    float t1 = (-b + sqrtDisc) * invA;

    FVector poin1 = FVector::ZeroVector;
    FVector poin2 = FVector::ZeroVector;

    poin1 = testEdge.A + t0 * D;
    poin2 = testEdge.A + t1 * D;

    bool p1 = true;
    bool p2 = true;

    if(t0 > 1.0f || t0 < 0.0f)
    {
        //disregard
        p1 = false;
    }

    if (t1 > 1.0f || t1 < 0.0f)
    {
        p2 = false;
    }

    if(!p1 && !p2)
    {
        return false;
    }
    else if(!p1)
    {
        poin1 = poin2;
    }
    else if (!p2)
    {
        poin2 = poin1;
    }

    float invRadius = 1.0f / agentRadius;

    agentRadius += 5.0f; 

    //Average the points:
    FVector impactPoint = (poin1 + poin2) / 2.0f;


    FVector directionToCircle = agentPosition - impactPoint;  
    FastNormalize(directionToCircle);

    FVector collisionPoint = directionToCircle * agentRadius + impactPoint;

    float distToCollision = FastSQRoot(agentPosition.DistSquared2D(agentPosition, collisionPoint));
    float speed = FastSQRoot(agentDelta.SizeSquared2D());

    float outTime = 0.0f;
    if (speed != 0.0f)
    {
        outTime = distToCollision / speed;
    }

    outCollisionResult.m_bIsPawn = false;
    outCollisionResult.m_edge = testEdge;
    outCollisionResult.m_normal = directionToCircle;
    outCollisionResult.m_collisionPoint = collisionPoint;
    outCollisionResult.m_time = outTime;
    outCollisionResult.m_bHit = true;
    outCollisionResult.m_impactPoint = impactPoint;

    outCollisionResult.m_binPenetration = outTime < 0.0f;
    return true;
}

1 Ответ

0 голосов
/ 15 января 2019

Подсказка:

Если сегмент зафиксирован, вы можете исправить проблему, «сдавив» круг, одновременно надувая сегмент, что дает вам точку смещения в сравнении с «капсулой». Как вы видите, столкновение происходит в точке пересечения между траекторией центра и контуром капсулы, что может происходить вдоль прямого или круглого края.

enter image description here

Вычисление будет упрощено вращением сцены так, чтобы сегмент доходил до оси X.

...