팀프로젝트 - 자율주행 위험 구간 분석 시뮬레이터 

역할 : 에이전트 로직 담당


클래스 구조

[월드]
│
├── ARoadActor               (도로 경로 정의)
│   └── USplineComponent    (스플라인 데이터)
│
└── APawn                    (차량)
    └── USplineFollowerComponent  (자율주행 로직)
        └── 매 프레임 ARoadActor를 찾아 따라감
ARoadActor 도로의 형태와 속성 정의
USplineFollowerComponent 도로를 따라가는 운전 판단
APawn 실제로 움직이는 차량 객체

 

이렇게 분리하면 한 차량이 여러 도로를 갈아타거나, 여러 차량이 같은 도로를 공유하는 시나리오를 쉽게 구현할 수 있다

 

폴더 구조

Source/Team24Unreal/
├── Actor/
│   ├── RoadActor.h
│   └── RoadActor.cpp
└── Component/
    ├── SplineFollowerComponent.h
    └── SplineFollowerComponent.cpp

 

팀 협업을 고려해 클래스 종류별로 폴더를 분리했다


 

USplineComponent 공식 문서

  • 곡선을 정의하는 컴포넌트
  • 점을 여러 개 찍으면 자동으로 곡선이 됨
  • 도로나 강 같은 거 만들 때 씀

스플라인 = 점들을 이어서 만든 곡선

 

사람이 운전할 때:
1. 도로를 본다 (눈)
2. 어디로 가야 할지 안다 (판단)
3. 핸들을 돌린다 (조향)
4. 페달을 밟는다 (가속/감속)
5. 커브가 보이면 미리 속도 줄인다
6. 도로 가운데에서 벗어나면 핸들로 보정한다

 

  • 도로를 본다 → 어떻게든 도로 데이터를 가져와야 함
  • 어디로 가야 할지 안다 → "앞쪽 어딘가 한 점"을 정해야 함
  • 핸들을 돌린다 → 차의 방향을 바꿔야 함
  • 페달을 밟는다 → 차의 속도를 바꿔야 함
 
1. 도로 — 월드에 놓여있고, 모양 정보를 가짐
2. 자동차 — 움직이는 물체
3. 자동 운전 두뇌 — 도로 보고 자동차 조작

 

클래스 3개로 나누자.

근데 자동 운전 두뇌를 클래스로 따로 만들지, 아니면 자동차 안에 넣을지 고민됨.

 

언리얼 공식 문서: Components

  • Actor: 월드에 직접 놓이는 것 (예: 자동차, 도로)
  • Component: Actor에 붙여서 기능을 추가하는 것 (예: 메시, 충돌체, 카메라)

자동 운전 로직을 Component로 만들면:

  • 자동차에 붙여서 자율주행 기능 추가
  • 다른 차에도 똑같이 붙일 수 있음
  • 자동차 클래스가 너무 커지지 않음
 
ARoadActor              ← 도로 (Actor)
APawn (기본 사용)        ← 자동차 (Pawn = 조종 가능한 Actor)
USplineFollowerComponent ← 자율주행 두뇌 (Component)

 

클래스 이름 앞 글자 A는 Actor, U는 UObject(Component 포함)라는 언리얼 컨벤션

 


RoadActor

 

Unreal Forums: SplineMeshComponent vs SplineComponent 

  • USplineComponent = 곡선 경로 정의 (보이지 않음)
  • USplineMeshComponent = 두 점 사이에 메시를 휘어서 표시

내가 필요한 건 경로 데이터 → USplineComponent

 

AActor를 직접 상속받자

  1. ASplineActor는 그냥 AActor + USplineComponent를 묶어놓은 것
  2. 내가 직접 묶으면 더 자유로움 (나중에 위험 영역, 신호등 정보 같은 거 추가하기 쉬움)
  3. 디지털 트윈 프로젝트라 도로에 메타데이터가 많이 붙을 예정

→ 부모 클래스: AActor, 안에 USplineComponent를 직접 추가.

 


도로 위에 차 놓기

 

USplineComponent API 레퍼런스

  • GetLocationAtTime — 시간 기반? 안 쓸 듯
  • GetLocationAtSplinePoint — 점 인덱스 기반
  • GetLocationAtDistanceAlongSpline ← 거리로 위치를 얻는다
  • FindLocationClosestToWorldLocation ← 가장 가까운 위치 찾기
  • GetSplineLength — 전체 길이

차를 도로 시작점에 텔레포트

void BeginPlay()
{
    ARoadActor* Road = ...;  
    USplineComponent* Spline = Road->GetSplineComponent();

    FVector StartPos = Spline->GetLocationAtDistanceAlongSpline(0.f, ESplineCoordinateSpace::World);
    OwnerPawn->SetActorLocation(StartPos);
}

 

 

"월드에서 Actor 어떻게 찾지?" 검색

Unreal 공식 문서: TActorIterator

for (TActorIterator<ARoadActor> It(GetWorld()); It; ++It)
{
    ARoadActor* Road = *It;
    // 이걸로 사용
}

 

 

SplineFollowerComponent를 만들기 시작

부모는 UActorComponent.

// SplineFollowerComponent.h (첫 버전)
UCLASS(meta=(BlueprintSpawnableComponent))
class TEAM24UNREAL_API USplineFollowerComponent : public UActorComponent
{
    GENERATED_BODY()

public:
    USplineFollowerComponent();

protected:
    virtual void BeginPlay() override;
    virtual void TickComponent(float DeltaTime, ELevelTick TickType,
                               FActorComponentTickFunction* ThisTickFunction) override;

private:
    UPROPERTY()
    TWeakObjectPtr<ARoadActor> TargetRoad;

    UPROPERTY()
    TWeakObjectPtr<APawn> OwnerPawn;
};

 

TWeakObjectPtr?

Tom Looman의 UE5 C++ 가이드

  • 그냥 ARoadActor* (raw pointer): 객체가 삭제되면 댕글링 포인터 → 크래시
  • TWeakObjectPtr<ARoadActor>: 객체가 삭제되면 자동으로 nullptr 처리

 

BeginPlay 첫 구현

void USplineFollowerComponent::BeginPlay()
{
    Super::BeginPlay();

    // 이 컴포넌트가 붙어있는 액터를 가져옴
    OwnerPawn = Cast<APawn>(GetOwner());

    // 월드에서 첫 번째 RoadActor 찾기
    for (TActorIterator<ARoadActor> It(GetWorld()); It; ++It)
    {
        TargetRoad = *It;
        break;
    }
}

 

Cast<>는 다이나믹 캐스트 같은 것. 실패하면 nullptr.

 

 


위치 확인 및 이동시키기 (GetActorLocation / SetActorLocation)

// 1. 내 현재 위치 가져오기 (FVector는 X, Y, Z 좌표 상자)
FVector MyLocation = GetActorLocation();

// 2. 새로운 위치로 순간이동 시키기 (예: 하늘 위로 500만큼 띄우기)
FVector NewLocation = MyLocation + FVector(0.f, 0.f, 500.f);
SetActorLocation(NewLocation);

 

형변환, Cast

OwnerPawn = Cast<APawn>(GetOwner());

 

-> GetOwner()가 Pawn이면 이 코드에서 그거 이름을 OwnerPawn이라고 부르겠다.

아니면 nullptr이 반환됨

 

 

 

+ Recent posts