액터 스폰/소멸 개요

이 안내서에서는 스폰할 자체 액터와, 버튼 클릭 시 액터를 스폰할 aActor 스포너를 만듭니다.

구현 방법 선택

블루프린트

C++

구현 방법 선택

블루프린트

C++

종종 비디오 게임에서 플레이어는 캐릭터에 맞는 새로운 아이템이나 무기를 획득하여 계속 참여하고 싶을 때가 있으며, 경우에 따라 새로운 적이 등장하기도 합니다.

게임에서 게임플레이 도중 레벨에 아이템이나 적을 동적으로 추가하고 싶을 때가 있습니다. 이 경우를 스폰이라고 합니다.

이 안내서에서는 버튼을 누르면 액터 스포너에서 스폰할 액터 클래스를 만든 다음 다른 버튼을 누르면 스폰된 액터를 제거하는 로직을 만들겠습니다.

image alt text

스폰할 액터 생성

이 섹션에서는 스피어 컴포넌트, 스태틱 메시 컴포넌트, 파티클 시스템 컴포넌트를 포함하는 액터를 생성합니다.

  1. 삼인칭(Third Person) 블루프린트(Blueprint) 게임 프로젝트를 생성합니다. 시작용 콘텐츠(Starter Content)활성화 합니다. 프로젝트 이름을 SpawnDestroy 로 지정합니다.

    image_1.png

  2. 콘텐츠 브라우저(Content Browser) 에서 추가/임포트(Add/Import) 버튼을 선택하고 BP_ActorToSpawn 이라는 이름의 새 블루프린트 액터(Blueprint Actor) 클래스를 생성합니다.

    image_2.png

  3. 클래스 디폴트(Class Defaults) 에서 컴포넌트(Components) 탭으로 이동하여 컴포넌트 추가(Add Component) 를 선택합니다. 그런 다음 구체(Sphere) 컴포넌트를 검색하고 선택한 뒤 DefaultSceneRoot 위로 드래그하여 새 루트 컴포넌트(Root Component) 로 만듭니다.

    image_3.png

  4. 구체 컴포넌트의 디테일(Details) 패널에서 셰이프(Shape) 카테고리로 이동하여 스피어 반경(Sphere Radius) 값을 16.0 으로 변경합니다.

    image_4.png

  5. 피직스(Physics) 카테고리로 이동하여 Simulate Physics 변수 옆의 박스에 체크합니다.

    image_5.png

  6. 컴포넌트(Components) 탭으로 이동하여 컴포넌트 추가(Add Component) 를 선택한 다음 Static Mesh 컴포넌트를 검색하고 선택합니다.

    image_6.png

  7. 디테일(Details) 패널에서 Static Mesh 카테고리를 찾고 Static Mesh 변수 옆의 드롭다운 화살표를 선택합니다. 그런 다음 Shape_Sphere Static Mesh 에셋을 검색하고 선택합니다.

    image_7.png

  8. 트랜스폼(Transform) 카테고리로 이동하여 스케일(Scale) 변수에서 X, Y, Z 값을 0.25 , 0.25 , 0.25 로 변경합니다. 그런 다음 위치(Location) 변수에서 Z 값을 -12.0 으로 변경합니다.

    image_8.png

  9. 컴포넌트(Components) 탭으로 이동하여 컴포넌트 추가(Add Component) 를 선택합니다. Particle System 컴포넌트를 검색하고 선택합니다.

    image_9.png

  10. 디테일(Details) 패널로 이동하여 파티클(Particles) 카테고리의 Template 변수 옆에 있는 드롭다운 화살표를 선택한 다음 P_Fire 를 검색하고 선택합니다.

    image_10.png

  11. 블루프린트를 컴파일(Compile) 하고 저장(Save) 합니다.

    image_11.png

완성된 블루프린트

image_12.png

액터 스포너 생성

이제 스폰할 액터를 생성했으니, 새 BP_ActorSpawn 블루프린트를 스폰할 커스텀 함수를 포함하는 액터 스포너를 생성하겠습니다.

  1. 콘텐츠 브라우저(Content Browser) 에서 추가/임포트(Add/Import) 버튼을 선택하고 BP_ActorSpawner 라는 이름의 새 블루프린트 액터(Blueprint Actor) 클래스를 생성합니다.

    image_13.png

  2. 클래스 디폴트(Class Defaults) 에서 컴포넌트(Components) 탭으로 이동하여 컴포넌트 추가(Add Component) 를 선택합니다. 큐브(Cube) 컴포넌트를 검색하고 선택한 뒤 이름을 SpawnVolume 로 변경합니다.

    image_14.png

  3. 이벤트 그래프(Event Graph) 를 우클릭하여 액션(Actions) 컨텍스트 메뉴에서 커스텀 이벤트 추가(Add Custom Event) 를 검색하고 선택한 다음 SpawnActor 로 명명합니다.

    image_15.png

  4. SpawnActor 커스텀 이벤트의 실행 핀에서 드래그하여 실행가능 액션(Executable actions) 드롭다운 메뉴에서 Spawn Actor From Class 를 검색하고 선택합니다.

    image_16.png

  5. SpawnActor 노드의 Class 핀에서 BP_ActorToSpawn 클래스를 선택합니다.

    image_17.png

  6. Spawn Actor from Class 노드의 Spawn Transform 핀을 클릭하고 드래그하여 액션(Actions) 드롭다운 메뉴에서 Get Actor Transform 을 검색하여 선택합니다.

    image_18.png

  7. 블루프린트를 컴파일(Compile) 하고 저장(Save) 합니다.

    image_19.png

완성된 블루프린트

image_20.png

추가 캐릭터 액션 명령 설정

두 액터 클래스 BP_ActorToSpawn과 BP_ActorSpawner를 생성했으니, ActorSpawner의 SpawnActor 함수를 호출하는 함수 기능이 있는 삼인칭 캐릭터를 구성해야 합니다.

  1. 편집(Edit) > 프로젝트 세팅(Project Settings) > 엔진(Engine) > 입력(Input) 으로 이동한 후 바인딩(Bindings) > 액션 매핑(Action Mappings) 카테고리에서 + 버튼 을 클릭하여 이름이 SpawnActorsDestroyActors 인 매핑 두 개를 추가합니다.

    image_21.png

  2. SpawnActors 액션 키 매핑을 숫자 1 키에 설정하고, DestroyActors 액션 키 매핑을 숫자 2 키에 설정합니다.

    image_22.png

  3. 콘텐츠 브라우저(Content Browser) 에서 삼인칭 캐릭터(Third Person Character) 블루프린트로 이동한 다음 더블클릭하여 클래스 디폴트를 열고 이벤트 그래프(Event Graph) 를 우클릭합니다. 이 블루프린트에 대한 모든 액션(All Actions for this Blueprint) 컨텍스트 메뉴에서 SpawnActors 입력 액션을 검색하고 선택합니다.

    image_23.png

  4. Pressed 실행 핀에서 드래그하여 실행가능 액션(Executable actions) 드롭다운 메뉴에서 Get Actor Of Class 노드를 검색하고 선택합니다. 그런 다음 Actor Class 드롭다운 화살표를 클릭하고 BP_ActorSpawner 를 선택합니다.

    image_24.png

  5. Get Actor of Class 노드의 Return Value 핀에서 드래그합니다. 액션(Actions) 메뉴에서 Spawn Actor 함수 호출을 검색하고 선택한 다음 Get Actor Of Class 노드의 실행 핀을 Spawn Actor 노드의 실행 핀에 연결합니다.

    image_25.png

  6. 이벤트 그래프(Event Graph) 를 우클릭하여 컨텍스트 메뉴에서 DestroyActors 입력 액션 이벤트를 검색하고 선택합니다.

    image_26.png

  7. Pressed 실행 핀에서 드래그하고 실행가능 액션(Executable actions) 드롭다운 메뉴에서 Get All Actors Of Class 노드를 검색하여 선택합니다. 그런 다음 액터 클래스(Actor Class) 의 드롭다운 화살표를 클릭하고 BP_ActorToSpawn 을 선택합니다.

    image_27.png

  8. Out Actors 반환 값 핀에서 드래그하여 드롭다운 메뉴에서 For Each Loop With Break 노드를 검색하고 선택합니다.

    image_28.png

  9. Array Element 핀에서 드래그하여 액션(Actions) 메뉴에서 Destroy Actor 함수를 검색하고 선택합니다. 그런 다음 Get All Actors of Class 노드의 출력 실행 핀을 For Each Loop with Break 노드실행 핀 에 연결합니다.

  10. 다음으로 For Each Loop with Break 노드의 Loop Body 실행 핀을 Destroy Actor 노드의 입력 실행 핀에 연결합니다.

    image_29.png

  11. 컴파일(Compile) 하고 저장(Save) 합니다.

    image_30.png

완성된 블루프린트

image_31.png

씬 설정

이제 플레이어 캐릭터가 BP_Spawner의 SpawnActor 함수를 호출하는 데 꼭 필요한 함수 기능 생성을 마쳤으니, 레벨에서 BP_Spawner의 위치를 선택해야 합니다.

  1. 콘텐츠 브라우저(Content Browser) 에서 BP_ActorSpawner 의 인스턴스를 레벨로 드래그합니다.

    image_32.png

  2. 에디터에서 플레이(Play-In Editor, PIE) 를 선택하여 BP_ActorSpawner를 테스트합니다.

최종 결과

에디터에서 테스트할 때 1 키를 누르면 ActorSpawner 클래스가 레벨에 액터를 스폰합니다. 2 키를 누르면 레벨 내 모든 ActorsToSpawn이 소멸됩니다.

image_33.gif

스폰할 액터 생성

  1. 활성화된 시작용 콘텐츠(Starter Content) 로 새로운 삼인칭 C++ 게임 프로젝트(Third Person C++ games project) 를 생성하는 것부터 시작합니다. 프로젝트 이름을 SpawnDestroy 로 지정합니다.

    image_34.png

  2. C++ 클래스 마법사(C++ class wizard) 에서 이름이 ActorToSpawn 인 새로운 액터(Actor) 클래스를 생성합니다.

    image_35.png

  3. ActorToSpawn.h의 클래스 디폴트에서 다음을 구현합니다.

    protected:
    
        UPROPERTY(EditAnywhere, BlueprintReadWrite)
        class USphereComponent* SphereComp;
    
        UPROPERTY(EditAnywhere, BlueprintReadWrite)
        UStaticMeshComponent* StaticMeshComp;
    
        UPROPERTY(EditAnywhere, BlueprintReadWrite)
        class UParticleSystemComponent* ParticleComp;
  4. ActorToSpawn.cpp로 이동하여 다음 클래스 라이브러리를 선언합니다.

    #include "Components/SphereComponent.h"
    #include "Particles/ParticleSystemComponent.h"
  5. AActorToSpawn::AActorToSpawn 생성자 안에서 다음 코드를 구현합니다.

    AActorToSpawn::AActorToSpawn()
    {
        // 이 액터가 프레임마다 Tick()을 호출하도록 설정합니다.  이 설정이 필요 없는 경우 비활성화하면 퍼포먼스가 향상됩니다.
        PrimaryActorTick.bCanEverTick = true;
    
        //자체 디폴트 컴포넌트 생성
        SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere"));
        StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
        ParticleComp = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ParticleComp"));
    
        //컴포넌트 어태치 및 피직스 설정
        SphereComp->SetupAttachment(RootComponent);
        SphereComp->SetSimulatePhysics(true);
        SphereComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    
        StaticMeshComp->AttachToComponent(SphereComp,FAttachmentTransformRules::KeepRelativeTransform);
        ParticleComp->AttachToComponent(StaticMeshComp,FAttachmentTransformRules::KeepRelativeTransform);
    
        //스태틱 메시에 맞추어 스피어 반경을 더 작은 크기로 설정
        SphereComp->SetSphereRadius(16.0f);
    
        //스피어 반경에 맞추어 스태틱 메시 스케일 및 위치 설정
        StaticMeshComp->SetRelativeLocation(FVector(0.0, 0.0, -12.0f));
        StaticMeshComp->SetRelativeScale3D(FVector(0.25, 0.25, 0.25));
    
        //생성자 헬퍼를 사용하여 자체 스태틱 메시 컴포넌트를 스피어 셰이프로 설정
        static ConstructorHelpers::FObjectFinder<UStaticMesh>SphereMeshAsset(TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere'"));
        StaticMeshComp->SetStaticMesh(SphereMeshAsset.Object);
    
        //생성자 헬퍼를 사용하여 자체 파티클 컴포넌트를 자체 불 파티클 컴포넌트로 설정
        static ConstructorHelpers::FObjectFinder<UParticleSystem>ParticleCompAsset(TEXT("ParticleSystem'/Game/StarterContent/Particles/P_Fire.P_Fire'"));
        ParticleComp->SetTemplate(ParticleCompAsset.Object);
    }
  6. 코드를 컴파일(Compile) 합니다.

  7. 에디터에서 C++ 클래스 폴더(C++ classes folder) 로 이동하여 ActorToSpawn 을 우클릭합니다. 컨텍스트 메뉴에서 ActorToSpawn 기반 블루프린트 클래스 생성(Create Blueprint class based on ActorToSpawn) 을 선택합니다.

    image_36.png

    이 부분을 완료하면 BP_ActorToSpawn은 아래 이미지와 비슷하게 보입니다.

    image_37.png

완성된 코드

ActorToSpawn.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ActorToSpawn.generated.h"

UCLASS()
class SPAWNDESTROYCPP_API AActorToSpawn : public AActor
{
    GENERATED_BODY()

public: 
    // 이 액터 프로퍼티의 디폴트값 설정
    AActorToSpawn();

protected:
    // 게임 시작 또는 스폰 시 호출
    virtual void BeginPlay() override;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    class USphereComponent* SphereComp;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    UStaticMeshComponent* StaticMeshComp;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    class UParticleSystemComponent* ParticleComp;

public: 
    // 프레임마다 호출
    virtual void Tick(float DeltaTime) override;
};

ActorToSpawn.cpp

#include "ActorToSpawn.h"
#include "Components/SphereComponent.h"
#include "Particles/ParticleSystemComponent.h"

// 디폴트값 설정
AActorToSpawn::AActorToSpawn()
{
    // 이 액터가 프레임마다 Tick()을 호출하도록 설정합니다.  이 설정이 필요 없는 경우 비활성화하면 퍼포먼스가 향상됩니다.
    PrimaryActorTick.bCanEverTick = true;

    //자체 디폴트 컴포넌트 생성
    SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere"));
    StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
    ParticleComp = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ParticleComp"));

    //컴포넌트 어태치 및 피직스 설정
    SphereComp->SetupAttachment(RootComponent);
    SphereComp->SetSimulatePhysics(true);

    SphereComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    StaticMeshComp->AttachToComponent(SphereComp,FAttachmentTransformRules::KeepRelativeTransform);
    ParticleComp->AttachToComponent(StaticMeshComp,FAttachmentTransformRules::KeepRelativeTransform);

    //스태틱 메시에 맞추어 스피어 반경을 더 작은 크기로 설정
    SphereComp->SetSphereRadius(16.0f);

    //스피어 반경에 맞추어 스태틱 메시 스케일 및 위치 설정
    StaticMeshComp->SetRelativeLocation(FVector(0.0, 0.0, -12.0f));
    StaticMeshComp->SetRelativeScale3D(FVector(0.25, 0.25, 0.25));
}

// 게임 시작 또는 스폰 시 호출
void AActorToSpawn::BeginPlay()
{
    Super::BeginPlay();

}

// 프레임마다 호출
void AActorToSpawn::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

액터 스포너 생성

이제 스폰할 액터(Actor to Spawn)를 생성했으니, 커스텀 함수를 사용하여 새 액터를 스폰하는 액터 스포너(Actor Spawner)를 생성해 보겠습니다.

  1. C++ 클래스 마법사(C++ class wizard) 에서 이름이 ActorSpawner 인 새로운 액터(Actor) 클래스를 생성합니다.

    image_38.png

  2. ActorSpawner.h의 클래스 디폴트에서 다음 코드를 구현합니다.

    public:
    
        UFUNCTION()
        void SpawnActor();
    
    protected:
        UPROPERTY(EditAnywhere,BlueprintReadWrite)
        class UBoxComponent* SpawnVolume;
  3. ActorSpawner.cpp로 이동하여 다음 클래스 라이브러리를 포함합니다.

    #include "Components/BoxComponent.h"
    #include "ActorToSpawn.h"
  4. AActorSpawner::AActorSpawner 생성자 안에서 다음 코드를 구현합니다.

    AActorSpawner::AActorSpawner()
    {
        RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("DefaultSceneRoot"));
        SpawnVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("SpawnVolume"));
        SpawnVolume->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    }
  5. AActorSpawner::SpawnActor 클래스 메서드에 대해 다음 코드를 구현합니다.

    void AActorSpawner::SpawnActor()
    {
        FVector SpawnLocation = GetActorLocation();
        FRotator SpawnRotation = GetActorRotation();
    
            GetWorld()->SpawnActor<AActorToSpawn>(SpawnLocation,SpawnRotation);
    }
  6. 코드를 컴파일(Compile) 합니다.

  7. 콘텐츠 브라우저(Content Browser) 에서 C++ 클래스 폴더(C++ classes folder) 로 이동하여 ActorSpawner 클래스를 우클릭한 다음, C++ 클래스 액션(C++ Class Actions) 컨텍스트 메뉴에서 이름이 BP_ActorSpawnerActorSpawner 기반 블루프린트 클래스 생성(Create Blueprint class based on ActorSpawner) 을 선택합니다.

    image_39.png

    완성된 블루프린트는 아래 이미지와 같습니다.

    image_40.png

완성된 코드

ActorSpawner.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ActorSpawner.generated.h"

UCLASS()
class SPAWNDESTROYCPP_API AActorSpawner : public AActor
{
    GENERATED_BODY()

public: 
    // 이 액터 프로퍼티의 디폴트값 설정
    AActorSpawner();

    UFUNCTION()
    void SpawnActor();

protected:
    // 게임 시작 또는 스폰 시 호출
    virtual void BeginPlay() override;

    UPROPERTY(EditAnywhere,BlueprintReadWrite)
    class UBoxComponent* SpawnVolume;

public: 
    // 프레임마다 호출
    virtual void Tick(float DeltaTime) override;
};

ActorSpawner.cpp

#include "ActorSpawner.h"
#include "Components/BoxComponent.h"
#include "ActorToSpawn.h"

// 디폴트값 설정
AActorSpawner::AActorSpawner()
{
    // 이 액터가 프레임마다 Tick()을 호출하도록 설정합니다.  이 설정이 필요 없는 경우 비활성화하면 퍼포먼스가 향상됩니다.
    PrimaryActorTick.bCanEverTick = true;

    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("DefaultSceneRoot"));
    SpawnVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("SpawnVolume"));

    SpawnVolume->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
}

// 게임 시작 또는 스폰 시 호출
void AActorSpawner::BeginPlay()
{
    Super::BeginPlay();
}

void AActorSpawner::SpawnActor()
{
    FVector SpawnLocation = GetActorLocation();
    FRotator SpawnRotation = GetActorRotation();
    GetWorld()->SpawnActor<AActorToSpawn>(SpawnLocation,SpawnRotation);
}

// 프레임마다 호출
void AActorSpawner::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

추가 캐릭터 액션 명령 설정

액터 및 액터 스포너 클래스를 모두 설정했으니, 이제 액터 스포너의 스폰 액터 함수를 호출하는 기능을 사용하여 삼인칭 캐릭터를 설정해 보겠습니다. 또한 플레이어에게 액터 스포너의 스폰 액터 함수를 호출하는 능력을 부여하려면 플레이어 캐릭터에게 입력 키 매핑을 실행해야 합니다.

  1. 편집(Edit) > 프로젝트 세팅(Project Settings) > 엔진(Engine) > 입력(Input) 으로 이동한 후 바인딩(Bindings) > 액션 매핑(Action mappings) 카테고리에서 + 버튼 을 클릭하여 이름이 SpawnActors 및 DestroyActors인 매핑 두 개를 추가합니다.

    image_41.png

  2. SpawnActors 액션 키 매핑을 숫자 1 키에 설정하고, DestroyActors 액션 키 매핑을 숫자 2 키에 설정합니다.

    image_42.png

  3. ThirdPersonCharacter.h 파일을 열어 다음 클래스 선언을 실행합니다.

    protected:
        void SpawnActors();
        void DestroyActors();
  4. ThirdPersonCharacter.cpp로 이동하여 다음 클래스 라이브러리를 포함합니다.

    #include "Kismet/GameplayStatics.h"
    #include "ActorSpawner.h"
    #include "ActorToSpawn.h"

    게임플레이 스태틱에는 블루프린트와 C++에서 모두 호출 가능한 유용한 게임플레이 유틸리티 함수가 포함됩니다. 자세한 정보는 게임플레이 스태틱에 대한 다음 API 문서를 참조하세요.

  5. ASpawnDestroyCppCharacter::SpawnActorsASpawnDestroyCppCharacter::DestroyActors 메서드에 대해 다음 클래스 구현을 실행합니다.

    void ASpawnDestroyCppCharacter::SpawnActors()
    {
        //월드에서 액터 스포너를 찾고 스폰 액터 함수 호출
        AActor* ActorSpawnerTofind = UGameplayStatics::GetActorOfClass(GetWorld(),AActorSpawner::StaticClass());
    
        AActorSpawner* ActorSpawnerReference = Cast<AActorSpawner>(ActorSpawnerTofind);
        if (ActorSpawnerReference)
        {
            ActorSpawnerReference->SpawnActor();
        }
    }
    
    void ASpawnDestroyCppCharacter::DestroyActors()
    {
        //월드 내 모든 스폰할 액터를 구하고 액터 소멸 호출
        TArray<AActor*> FoundActors;
        UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActorToSpawn::StaticClass(), FoundActors);
        for (AActor* ActorFound :FoundActors)
        {
            ActorFound->Destroy();
        }
    }
  6. ASpawnDestroyCppCharacter::SetupPlayerInputComponent 메서드로 이동하여 다음 코드를 추가합니다.

    void ASpawnDestroyCppCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
    {
        PlayerInputComponent->BindAction("SpawnActors", IE_Pressed, this, &ASpawnDestroyCppCharacter::SpawnActors);
        PlayerInputComponent->BindAction("DestroyActors", IE_Pressed, this, &ASpawnDestroyCppCharacter::DestroyActors);
    }

완성된 코드

SpawnDestroyCppCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "SpawnDestroyCppCharacter.generated.h"

UCLASS(config=Game)
class ASpawnDestroyCppCharacter : public ACharacter
{
    GENERATED_BODY()

    /** 캐릭터 뒤에 카메라를 배치하는 카메라 붐 */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class USpringArmComponent* CameraBoom;

    /** 카메라 따라가기 */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class UCameraComponent* FollowCamera;

public:
    ASpawnDestroyCppCharacter();

    /** 베이스 회전 속도, 단위는 도(º)/초. 다른 스케일 값 조절로 인해 최종 회전 속도가 영향을 받을 수 있습니다. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
    float BaseTurnRate;

    /** 베이스 올려다보기/내려다보기 속도, 단위는 도(º)/초. 다른 스케일 값 조절로 인해 최종 속도가 영향을 받을 수 있습니다. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
    float BaseLookUpRate;

protected:
    /*레벨의 액터 스포너를 호출하여 스폰할 액터 스폰*/
    void SpawnActors();

    /*모든 ActorToSpawn 액터를 구하고 소멸 함수 호출 */
    void DestroyActors();

    /** VR의 HMD 오리엔테이션을 리셋합니다. */
    void OnResetVR();

    /** 앞뒤 입력으로 호출 */
    void MoveForward(float Value);

    /** 좌우 입력으로 호출 */
    void MoveRight(float Value);

    /** 
     * 입력을 통해 호출되어 지정된 속도로 회전 
     * @param Rate  정규화된 비율이며, 1.0인 경우 지정된 회전 속도의 100%를 의미합니다.
     */
    void TurnAtRate(float Rate);

    /**
     * 입력을 통해 호출되어 지정된 속도로 올려다보기/내려다보기 
     * @param Rate  정규화된 비율이며, 1.0인 경우 지정된 회전 속도의 100%를 의미합니다.
     */
    void LookUpAtRate(float Rate);

    /** 터치 입력 시작 시 핸들러 */
    void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);

    /** 터치 입력 중지 시 핸들러 */
    void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);

protected:

    // APawn 인터페이스
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    // APawn 인터페이스 종료

public:

    /** CameraBoom 서브오브젝트 반환 **/
    FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }

    /** FollowCamera 서브오브젝트 반환 **/
    FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};

SpawnDestroyCppCharacter.cpp

#include "SpawnDestroyCppCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "Kismet/GameplayStatics.h"
#include "ActorSpawner.h"
#include "ActorToSpawn.h"
//////////////////////////////////////////////////////////////////////////
// ASpawnDestroyCppCharacter

ASpawnDestroyCppCharacter::ASpawnDestroyCppCharacter()
{
    // 콜리전 캡슐 크기 설정
    GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

    // 입력에 대한 회전 속도 설정
    BaseTurnRate = 45.f;
    BaseLookUpRate = 45.f;

    // 컨트롤러 회전 시 회전하지 않습니다. 카메라에만 영향을 미치도록 합니다.
    bUseControllerRotationPitch = false;
    bUseControllerRotationYaw = false;
    bUseControllerRotationRoll = false;

    // 캐릭터 무브먼트 환경설정
    GetCharacterMovement()->bOrientRotationToMovement = true; // 캐릭터가 입력 방향으로 이동    
    GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // 위의 캐릭터가 이동하는 회전 속도
    GetCharacterMovement()->JumpZVelocity = 600.f;
    GetCharacterMovement()->AirControl = 0.2f;

    // 카메라 붐 생성(콜리전 있을 시 플레이어 쪽으로 들어옴)
    CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
    CameraBoom->SetupAttachment(RootComponent);
    CameraBoom->TargetArmLength = 300.0f; // 캐릭터 뒤의 카메라가 이 거리에서 따라옴 
    CameraBoom->bUsePawnControlRotation = true; // 컨트롤러 기반으로 암 회전

    // 카메라 따라가기 생성
    FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
    FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // 카메라를 붐 끝에 어태치하여 붐이 컨트롤러 오리엔테이션에 맞추어 조절되도록 함
    FollowCamera->bUsePawnControlRotation = false; // 카메라가 암 기준으로 회전하지 않음

    // 참고: 캐릭터로부터 상속받는 메시 컴포넌트에 대한 스켈레탈 메시와 애님 블루프린트 레퍼런스는 
    // C++ 직접 콘텐츠 레퍼런스를 방지하기 위해 이름이 MyCharacter인 파생 블루프린트 에셋에서 설정됨
}

//////////////////////////////////////////////////////////////////////////
// 입력

void ASpawnDestroyCppCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
    // 게임플레이 키 바인딩 설정
    check(PlayerInputComponent);
    PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
    PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

    PlayerInputComponent->BindAction("SpawnActors", IE_Pressed, this, &ASpawnDestroyCppCharacter::SpawnActors);
    PlayerInputComponent->BindAction("DestroyActors", IE_Pressed, this, &ASpawnDestroyCppCharacter::DestroyActors);
    PlayerInputComponent->BindAxis("MoveForward", this, &ASpawnDestroyCppCharacter::MoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &ASpawnDestroyCppCharacter::MoveRight);

    // 2가지 버전의 회전 바인딩이 있어 서로 다른 종류의 디바이스를 다양한 방식으로 처리할 수 있습니다.
    // 'turn'은 마우스와 같은 절대 델타를 제공하는 디바이스를 처리합니다.
    // 'turnrate'는 아날로그 조이스틱과 같이 변화의 속도를 취급할 디바이스에 사용합니다.
    PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
    PlayerInputComponent->BindAxis("TurnRate", this, &ASpawnDestroyCppCharacter::TurnAtRate);
    PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
    PlayerInputComponent->BindAxis("LookUpRate", this, &ASpawnDestroyCppCharacter::LookUpAtRate);

    // 터치 디바이스 처리
    PlayerInputComponent->BindTouch(IE_Pressed, this, &ASpawnDestroyCppCharacter::TouchStarted);
    PlayerInputComponent->BindTouch(IE_Released, this, &ASpawnDestroyCppCharacter::TouchStopped);

    // VR 헤드셋 기능
    PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &ASpawnDestroyCppCharacter::OnResetVR);
}

void ASpawnDestroyCppCharacter::SpawnActors()
{

    //레벨에서 액터 스포너를 찾고 스폰 액터 함수 호출
    AActor* ActorSpawnerTofind = UGameplayStatics::GetActorOfClass(GetWorld(),AActorSpawner::StaticClass());
    AActorSpawner* ActorSpawnerReference = Cast<AActorSpawner>(ActorSpawnerTofind);

    if (ActorSpawnerReference)
    {
        ActorSpawnerReference->SpawnActor();
    }
}

void ASpawnDestroyCppCharacter::DestroyActors()
{
    TArray<AActor*> FoundActors;
    UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActorToSpawn::StaticClass(), FoundActors);
    for (AActor* ActorFound :FoundActors)
    {
        ActorFound->Destroy();
    }
}

void ASpawnDestroyCppCharacter::OnResetVR()
{
    // 언리얼 에디터의 '기능 추가'를 사용해 프로젝트에 SpawnDestroyCpp를 추가한 경우, SpawnDestroyCpp.Build.cs 내 HeadMountedDisplay의 종속성이 자동 전파되지 않습니다.
    // 또한 링커 오류가 발생합니다.
    // 다음 중 하나를 실행해야 합니다.
    //      [프로젝트 이름].Build.cs의 PublicDependencyModuleNames에 'HeadMountedDisplay'를 추가하면 빌드에 성공합니다. VR 지원 시 적합합니다.
    // 또는
    //      아래의 ResetOrientationAndPosition 호출을 코멘트 처리하거나 삭제합니다. VR 미지원 시 적합합니다.
    UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}

void ASpawnDestroyCppCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
        Jump();
}

void ASpawnDestroyCppCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
        StopJumping();
}

void ASpawnDestroyCppCharacter::TurnAtRate(float Rate)
{
    // 속도 정보로부터 이 프레임에 대한 델타 계산
    AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void ASpawnDestroyCppCharacter::LookUpAtRate(float Rate)
{
    // 속도 정보로부터 이 프레임에 대한 델타 계산
    AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

void ASpawnDestroyCppCharacter::MoveForward(float Value)
{
    if ((Controller != nullptr) && (Value != 0.0f))
    {
        // 앞쪽 찾기
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        // 앞쪽 벡터 구하기
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
}

void ASpawnDestroyCppCharacter::MoveRight(float Value)
{
    if ( (Controller != nullptr) && (Value != 0.0f) )
    {
        // 오른쪽 찾기
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        // 오른쪽 벡터 구하기 
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

        // 해당 방향으로 이동 추가
        AddMovementInput(Direction, Value);
    }
}

씬 설정

이제 플레이어 캐릭터가 BP_Spawner의 SpawnActor 함수를 호출하는 데 꼭 필요한 함수 기능 생성을 마쳤으니, 레벨에서 BP_Spawner의 위치를 설정해야 합니다.

  1. 콘텐츠 브라우저(Content Browser) 에서 BP_ Spawner 의 인스턴스를 클릭해 레벨로 드래그합니다.

    image_43.png

  2. 에디터에서 플레이(Play-In Editor, PIE) 를 선택하여 스포너를 테스트합니다.

최종 결과

에디터에서 테스트할 때 1 키를 누르면 액터 스포너 클래스가 레벨로 스폰할 액터를 스폰합니다. 2 키를 누르면 레벨 내 모든 ActorsToSpawn이 소멸됩니다.

image_44.gif

언리얼 엔진의 이전 버전을 위해 작성된 페이지입니다. 현재 언리얼 엔진 5 버전을 위해 업데이트되지 않았습니다.