엔진 내 다양한 버전의 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 을 설정하는 부분이 보인다.
함수 원형
// 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 를 사용하고 있다.
함수 원형
// 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 를 수행하여 결과들을 배열에 담아 리턴한다.
상기 2개 함수의 리턴형으로 FCheckResult 란 구조체를 사용한다. 각 변수의 의미를 살펴본다.
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 커맨드를 입력하면 라인체크를 실시간으로 확인할 수 있다.
아래 코드에서 플래그가 바뀌고
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; } ... }
여기서 그려진다.
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 ... }