Trace 함수

엔진 내 다양한 버전의 Trace 함수들을 살펴본다.

종류

Trace

함수 원형

// Actor.uc
native(277) noexport final function Actor Trace
(
    out vector                       HitLocation,        // Hit 한 위치
    out vector                       HitNormal,          // Hit 한 Normal 벡터
    vector                           TraceEnd,           // Trace 검사할 종료 위치
    optional vector                  TraceStart,         // Trace 검사할 시작 위치
    optional bool                    bTraceActors,       // Trace 과정 중 Actor 를 체크할지 여부
    optional vector                  Extent,             // Trace 할 ray 의 크기
    optional out TraceHitInfo        HitInfo,            // Hit 정보
    optional int                     ExtraTraceFlags     // 각종 플래그
);
 
// UnScript.cpp
void AActor::execTrace( FFrame& Stack, RESULT_DECL )
{
    ...
 
    // 파라메터로 들어온 ExtraTraceFlags 를 조회하여 SingleLineCheck 에 사용할 Flag 를 조합한다.
    DWORD TraceFlags;
 
    ...
 
    if ( pHitInfo )
    {
        TraceFlags |= TRACE_Material;
    }
 
    ...
 
    if ( ExtraTraceFlags & UCONST_TRACEFLAG_Bullet )
    {
        TraceFlags |= TRACE_ComplexCollision;          // bullet 을 위한 trace 라면 per poly 수준의 line check 를 할 것이다.
    }
 
    ...                     // 뭐 대충 이런 내용들
 
    // 조합된 TraceFlags 로 line check 한 후, output 파라메터의 정보를 채운다.
    FCheckResult Hit( 1.0f );
    GWorld->SingleLineCheck( Hit, TraceActor, TraceEnd, TraceStart, TraceFlags, TraceExtent );
 
    *(AActor**)Result   = Hit.Actor;     // 첫번째로 hit 한 Actor
    HitLocation         = Hit.Location;
    HitNormal           = Hit.Normal;
 
    if ( pHitInfo )
    {
        HitInfo.PhysMaterial = DetermineCorrentPhysicalMaterial( Hit );
        HitInfo.Material     = Hit.Material ? HitMaterial->GetMaterial() : NULL;
        HitInfo.Item         = Hit.Item;
        HitInfo.LevelIndex   = Hit.LevelIndex;
        HitInfo.BoneName     = Hit.BoneName;
        HitInfo.HitComponent = Hit.Component;
    }
}

내부적으로 UWorld::SingleLineCheck 함수를 사용하며, Hit 정보를 토대로 PhysicalMaterial 을 설정하는 부분이 보인다.

SingleLineCheck

함수 원형

// UnWorld.h
class UWorld : public UObject, public FNetworkNotify
{
    ...
    UBOOL SingleLineCheck( FCheckResult& Hit, AActor* SourceActor, const FVector& End, const FVector& Start, DWORD TraceFlags, const FVector& Extent=FVector(0,0,0), ULightComponent* SourceLight = NULL );
    ...
};
 
// UnLevAct.cpp
UBOOL UWorld::SingleLineCheck
(
    FCheckResult&     Hit,             // SingleLineCheck 의 결과를 담은 구조체
    AActor*           SourceActor,     // 체크를 수행하는 Actor
    const FVector&    End,             // 체크 종료 위치
    const FVector&    Start,           // 체크 시작 위치
    DWORD             TraceFlags,      // 체크 플래그
    const FVector&    Extent,          // 체크할 ray 크기
    ULightComponent*  SourceLight      // 
)
{
    ...
 
    // 현재 메모리 스택을 마킹
    FMemMark Mark( GMainThreadMemStack );
 
    // MultiLineCheck
    TraceFlags = TraceFlags | TRACE_SingleResult;
    FCheckResult* FirstHit = MultiLineCheck(
        GMainThreadMemStack,
        End,
        Start,
        Extent,
        TraceFlags,
        SourceActor,
        SourceLight );
 
    ...
 
    // 마킹된 스택으로 원복
    Mark.Pop();
 
    ...
}

SingleLineCheck 는 내부적으로 MultiLineCheck 를 사용하고 있다.

MultiLineCheck

함수 원형

// UnWorld.h
class UWorld : public UObject, public FNetworkNotify
{
    ...
    FCheckResult* MultiLineCheck( FMemStack& Mem, const FVector& End, const FVector& Start, const FVector& Size, DWORD TraceFlags, AActor* SourceActor, ULightComponent* SourceLight = NULL );
    ...
};
 
// UnLevAct.cpp
FCheckResult* UWorld::MultiLineCheck
(
    FMemStack&          Mem,
    const FVector&      End,
    const FVector&      Start,
    const FVector&      Extent,
    DWORD               TraceFlags,
    AActor*             SourceActor,
    ULightComponent*    SourceLight
)
{
    ...
 
    // 결과들을 저장해 둘 배열
    INT NumHits = 0;
    FCheckResult Hits[64];
 
    // 레벨 line check
    if (
        ( TraceFlags & TRACE_Level ) &&                                              // 레벨체크하라는 플래그
        BSPLineCheck( Hits[NumHits], NULL, End, Start, Extent, TraceFlags ) == 0 )   // BSP 에 line check 시도
    )
    {
        ...
        Hits[NumHits++] = ...  // 배열에 결과를 추가
        ...
    }
 
    ...
 
    // 액터 line check
    for (
        FCheckResult* Link = Hash->ActorLineCheck( ... );
        ... ;
        Link = Link->GetNext()
    )
    {
        Hits[NumHits++] = ...  // 배열에 결과를 추가
        ...
    }
 
    ...
 
    // 결과들을 소팅
    ...
    appQsort( Hits, NumHits, sizeof(Hits[0]), (QSORT_COMPARE)FCheckResult::CompareHits );
    ...
 
 
    return Result;
}

BSP 혹은 Actor 의 Primitive 에 대한 line check 를 수행하여 결과들을 배열에 담아 리턴한다.

FCheckResult

상기 2개 함수의 리턴형으로 FCheckResult 란 구조체를 사용한다. 각 변수의 의미를 살펴본다.

UnPrim.h
struct FIteratorActorList : public FIteratorList
{
    AActor*               Actor;         // 충돌된 Actor. BSP에 충돌된 경우 WorldInfo 객체. 캐릭터에 충돌할 경우 Pawn 객체의 포인터가 넘어온다. StaticMeshActor 도 마찬가지
    ...
};
 
struct FCheckResult : public FIteratorActorList
{
    FVector                      Location;        // 충돌한 위치
    FVector                      Normal;          // 충돌한 노멀 방향
    FLOAT                        Time;            // 라인체크일 경우 충돌이 발생한 시간. Start 와 End 구간에서 0~1 사이의 값
    INT                          Item;            // BSP 충돌인 경우 Level.Model 에서 충돌된 BSP 인덱스를 뜻하며, SkeletalMeshComponent 의 경우 PhysicAsset 의 BodySetup 인덱스를 뜻한다. (있다면)
    UMaterialInsterface*         Material;        // 충돌된 Material 인스턴스. TRACE_Material 플래그를 사용한 경우 유효하다.
    UPhysicalMaterial*           PhysMaterial;    // 충돌된 물리 재질
    UPrimitiveComponent*         Component;       // 충돌한 PrimitiveComponent
    UPrimitiveComponent*         SourceComponent; // 바로 위 Component 와 충돌한 Actor 의 Component
    FName                        BoneName;        // 충돌한 것이 SkeletalMeshComponent 인 경우 충돌한 Bone 이름
    ULevel*                      Level;           // BSP 충돌인 경우 BSP 를 소유한 Level 객체
    INT                          LevelIndex;      // BSP 충돌인 경우 BSP 인덱스. 이 인덱스에 해당하는 객체가 바로 위 ULevel 변수에 캐싱된다.
};

디버그

Trace 정보를 실시간으로 확인 가능하다.

SHOWLINECHECK, SHOWEXTENTLINECHECK, SHOWPOINTCHECK 커맨드를 입력하면 라인체크를 실시간으로 확인할 수 있다.

아래 코드에서 플래그가 바뀌고

UnWorld.cpp
UBOOL UWorld::Exec( const TCHAR* Cmd, FOutputDevice& Ar )
{
    ...
    else if ( ParseCommand( &Cmd, TEXT("SHOWEXTENTLINECHECK") ) )
    {
        bShowExtentLineChecks = !bShowExtentLineChecks;
        return 1;
    }
    else if ( ParseCommand( &Cmd, TEXT("SHOWLINECHECK") ) )
    {
        bShowLineChecks = !bShowLineChecks;
        return 1;
    }
    else if ( ParseCommand( &Cmd, TEXT("SHOWPOINTCHECK") ) )
    {
        bShowPointChecks = !bShowPointChecks;
        return 1;
    }
    ...
}

여기서 그려진다.

UnLevAct.cpp
FCheckResult* UWorld::MultiLineCheck
(
    FMemStack&          Mem,
    const FVector&      End,
    const FVecotr&      Start,
    const FVector&      Extent,
    DWORD               TraceFlags,
    AActor*             SourceActor,
    ULightComponent*    SourceLight
)
{
    ...
#if !FINAL_RELEASE
    if ( this->bShowLineChecks && Extent.IsZero() )
    {
        LineBatcher->DrawLine( Start, End, FColor( 0, 255, 128 ), SDPG_World );
    }
    else if ( this->bShowExtentLineChecks && !Extent.IsZero() )
    {
        LineBatcher->DrawLine( Start, End, FColor( 0, 255, 255 ), SDPG_World );
        DrawWireBox( LineBatcher, FBox( End - Extent, End + Extent ), FColor( 0, 255, 255 ), SDPG_World );
    }
#endif
    ...
}

참조

  • 소스코드