icon안동민 개발노트

액터 (Actor) 클래스 이해와 구현


 언리얼 엔진에서 액터(Actor)는 게임 월드에 배치하거나 스폰할 수 있는 모든 객체의 기본 클래스입니다.

 이 절에서는 AActor 클래스의 특징, 생명주기, 구현 방법 등을 살펴보겠습니다.

AActor 클래스의 역할과 특징

 AActor는 다음과 같은 주요 특징을 가집니다.

  1. 월드 내 위치, 회전, 스케일 정보 보유
  2. 틱(Tick) 기능을 통한 프레임마다의 업데이트
  3. 리플리케이션을 통한 네트워크 동기화
  4. 컴포넌트를 포함할 수 있는 컨테이너 역할
  5. 블루프린트로 확장 가능

액터의 생명주기

  1. 스폰(Spawning) : 월드에 생성됨
  2. 초기화(Initialization) : BeginPlay 호출
  3. 틱(Ticking) : 매 프레임마다 Tick 함수 호출
  4. 파괴(Destruction) : EndPlay 호출 후 메모리에서 제거

C++에서 커스텀 액터 클래스 생성 및 구현

 기본 클래스 선언

// MyActor.h
##pragma once
 
##include "CoreMinimal.h"
##include "GameFramework/Actor.h"
##include "MyActor.generated.h"
 
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
    GENERATED_BODY()
    
public:    
    AMyActor();
 
protected:
    virtual void BeginPlay() override;
 
public:    
    virtual void Tick(float DeltaTime) override;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MyActor")
    float MyFloat;
 
    UFUNCTION(BlueprintCallable, Category="MyActor")
    void MyFunction();
};

 클래스 구현

// MyActor.cpp
##include "MyActor.h"
 
AMyActor::AMyActor()
{
    PrimaryActorTick.bCanEverTick = true;
    MyFloat = 0.0f;
}
 
void AMyActor::BeginPlay()
{
    Super::BeginPlay();
    UE_LOG(LogTemp, Log, TEXT("MyActor BeginPlay called"));
}
 
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    MyFloat += DeltaTime;
}
 
void AMyActor::MyFunction()
{
    UE_LOG(LogTemp, Log, TEXT("MyFloat value: %f"), MyFloat);
}

주요 가상 함수 오버라이딩

  1. BeginPlay() : 액터가 게임에 배치되거나 스폰될 때 호출
  2. Tick(float DeltaTime) : 매 프레임마다 호출
  3. EndPlay(const EEndPlayReason::Type EndPlayReason) : 액터가 파괴될 때 호출

액터의 속성과 동작 정의

  1. UPROPERTY 매크로를 사용하여 에디터에서 편집 가능한 속성 정의
  2. UFUNCTION 매크로를 사용하여 블루프린트에서 호출 가능한 함수 정의
  3. 컴포넌트 추가로 기능 확장
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
    GENERATED_BODY()
    
public:    
    AMyActor();
 
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components")
    UStaticMeshComponent* MeshComponent;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MyActor")
    float Damage;
 
    UFUNCTION(BlueprintCallable, Category="MyActor")
    void ApplyDamage(AActor* OtherActor);
};
 
AMyActor::AMyActor()
{
    MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
    RootComponent = MeshComponent;
    Damage = 10.0f;
}
 
void AMyActor::ApplyDamage(AActor* OtherActor)
{
    // 데미지 적용 로직
}

액터 간 상호작용 구현

 액터 간 상호작용은 주로 다음과 같은 방법으로 구현합니다.

  1. 직접 참조
  2. 인터페이스 사용
  3. 이벤트 시스템 활용

 예시 (인터페이스 사용)

// IDamageable.h
##pragma once
 
##include "CoreMinimal.h"
##include "UObject/Interface.h"
##include "IDamageable.generated.h"
 
UINTERFACE(MinimalAPI)
class UDamageable : public UInterface
{
    GENERATED_BODY()
};
 
class MYPROJECT_API IDamageable
{
    GENERATED_BODY()
 
public:
    virtual void TakeDamage(float DamageAmount) = 0;
};
 
// MyDamageableActor.h
##include "IDamageable.h"
 
UCLASS()
class MYPROJECT_API AMyDamageableActor : public AActor, public IDamageable
{
    GENERATED_BODY()
    
public:
    virtual void TakeDamage(float DamageAmount) override;
};
 
// MyDamageableActor.cpp
void AMyDamageableActor::TakeDamage(float DamageAmount)
{
    // 데미지 처리 로직
}

레벨에 액터 배치 및 동적 스폰

  1. 레벨에 직접 배치
  • 언리얼 에디터에서 액터를 월드에 드래그 앤 드롭
  1. 런타임에 동적 스폰
AMyActor* SpawnedActor = GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass(), Location, Rotation);
  1. 액터 제거
Actor->Destroy();

성능 고려사항 및 Best Practices

  1. Tick 함수 최적화
  • 필요한 경우에만 Tick 활성화
PrimaryActorTick.bCanEverTick = false;
  • Tick 간격 조절
PrimaryActorTick.TickInterval = 0.1f;
  1. 컴포넌트 사용
  • 기능을 모듈화하고 재사용성을 높이기 위해 컴포넌트 활용
  1. 메모리 관리
  • 불필요한 액터는 제때 제거
  • 객체 풀링 기법 고려
  1. 네트워크 최적화
  • 필요한 속성만 리플리케이션 설정
UPROPERTY(Replicated)
float ReplicatedValue;
  1. 레벨 스트리밍 고려
  • 대규모 월드의 경우 레벨 스트리밍을 통해 메모리 사용 최적화
  1. 인터페이스 활용
  • 느슨한 결합을 위해 인터페이스 사용
  1. 에디터 작업 최적화
  • 에디터에서의 작업을 용이하게 하는 속성과 함수 제공
UPROPERTY(EditAnywhere, Category="Debug")
bool bShowDebugInfo;
 
UFUNCTION(CallInEditor, Category="Debug")
void ToggleDebugInfo();
  1. 액터 초기화 최적화
  • 생성자에서 필수적인 초기화만 수행
  • BeginPlay에서 게임 시작 시 필요한 초기화 수행
  1. 상속 구조 설계
  • 공통 기능을 기본 클래스로 추출하여 코드 중복 최소화
  1. 문서화
  • UPROPERTY와 UFUNCTION에 메타데이터를 활용한 문서화
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats", meta=(ClampMin="0.0", ClampMax="100.0", ToolTip="Health value between 0 and 100"))
float Health;

 액터 클래스는 언리얼 엔진 게임플레이의 기본 구성 요소입니다.

 적절한 설계와 구현을 통해 효율적이고 확장 가능한 게임 시스템을 구축할 수 있습니다. 액터의 생명주기를 이해하고, 필요에 따라 적절한 가상 함수를 오버라이딩하며, 컴포넌트와 인터페이스를 활용하여 모듈화된 설계를 추구하는 것이 중요합니다.

 또한, 성능을 고려한 최적화와 함께 에디터와의 통합을 통해 개발 효율성을 높일 수 있습니다. 지속적인 프로파일링과 최적화를 통해 게임의 전반적인 성능을 개선하고, 확장 가능하고 유지보수가 용이한 코드베이스를 구축할 수 있습니다.