발사체에는 스태틱 메시가 필요하다. 그래야 월드에서 스폰할 때 발사체를 볼 수 있기 때문이다.
발사체.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Projectile.generated.h"
UCLASS()
class TOONTANKS_API AProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
private:
UPROPERTY(EditDefaultsOnly, Category="Combat")
UStaticMeshComponent* ProjectileMesh;
};
발사체.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Components/StaticMeshComponent.h"
#include "Projectile.h"
// Sets default values
AProjectile::AProjectile()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false; //발사체에는 틱이 필요없으므로 false
ProjectileMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Projectile Mesh"));
RootComponent = ProjectileMesh;
}
// Called when the game starts or when spawned
void AProjectile::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
기본적인 ProejctileMesh하나를 만들고 블루프린트 클래스를 하나 만들어 주었다.
메시를 골라주면 발사체가 만들어졌다!
발사체 생성 액터는 SpawnActor 함수가 있어야 한다.
우리는 c++ 함수를 호출해 동적으로 새로운 개체를 생성할 것이다. 우리는 블루프린트보다 c++에서 만들기 원하는데, AProjectile이라는 c++에 기반한 블루프린트에 추가적인 정보가 있기 때문이다(정적 메시 컴포넌트로 선택한 메시). 메시를 얻기 위해서는 블루프린트 버전으로 생성해야 한다.
그러면 c++에서 블루프린트 기반 객체를 어떻게 생성한다는 것일까?
이것은 탬플릿이다. TSubclassOf는 타입을 파라미터로 가지고 UClass라는 걸 저장하는 변수를 갖게 해준다. UClass는 클래스 타입이지만 언리얼 엔진의 리플렉션 시스템과 상호작용할 수 있 습니다. 여기서 리플렉션 시스템이란 C++와 블루프린트 사이 정보를 교환하는 것을 말합니다.
TSubclassOf는 이름에서 알 수 있듯이 전달하는 타입이나 그 타입의 하위 클래스를 기반으로 클래스를 나타내는 변수를 만들 수 있습니다. 우리가 타입을 선택할 수 있고 TSubclassOf는 그에 따른 모든 타입을 나타내는 UClass를 보유할 수 있게 한다. 타입을 기반으로 한 블루프린트도 마찬가지다.
왜 굳이 TSubclassOf변수를 만들었을까?
Spawn Actor가 동작하는 방법에 대해서 먼저 알고 있어야 한다. SpawnActor은 UWorld 클래스에 속하는 함수다. world를 가져오면 SpawnActor을 호출할 수 있다. SpawnActor은 또 다른 템플릿 함수이고 기본 하위 객체를 생성하는 것과 유사하게 새로운 객체를 생성하도록 되어 있다.
기본 하위 객체를 생성하는 것은 컴포넌트를 생성하기 위해 설계되었고 우리는 특정 클래스를 위한
생성자에서만 사용할 수 있다. SpawnActor는 게임이 실행되는동안 호출될 수 있고 액터를 생성한다. 이는 템플릿이기 때문에 형식이 필요한데, 발사체를 생성하고 싶다면 c++ 클래스 타입에 AProjectile을 넘기면 된다 SPawnActor<"AProjectile">()
이것은 C++의 SpawnActor 함수가 반환하는 c++ 클래스인 것이다. 괄호 안에는 UClass를 포함한 매개변수가 들어간다(UClass, Location, Rotation). UClss*는 발사체를 생성하는 타입을 나타낸다. 이것이 생성되는 객체의 실제 타입이다. Location은 객체를 어디로 생성할지 결정하는 FVector이고 Rotation은 회전을 지정한다. 예를 들면 SpawnActor<"AProjectile">(ProjectileClass, Loc, Rot) 이렇다.
UClasss는 클래스 타입이다. SpawnActor을 호출할 때 UClass를 넘겨 SPawnActor 함수와 객체에 관한 정보를 제공한다. SpawnActor에게 어떤 c++ 타입을 만들어야 하는지 알려준다.
AProjectile을 기반으로 블루프린트를 생성하고 블루프린트에서 속성을 설정한다고 생각해보자.
만약 우리가 c++ 클래스 자체를 생성한다면 정적 메시는 새로 생긴 발사체 객체에 적용될 지 않을 것이다. 눈에 보이지 않은 액터가 생긴 것이다. 하지만 UClass 변수는 블루프린트 클래스를 나타낸다. UClass는 C++ 쪽에서 변수를 가지게 해준다(블루프린트 타입). SpawnActor은 그 클래스를 인풋 매개 변수로 받아 UClass로 정의되는 블루프린트에 기반하여 새로운 객체를 생성한다.
새로운 발사체는 정적 메시 속성 집합을 가진다. c++클래스가 아닌 블루프린트에 기반한 새로운 객체를 생성하기 때문이다. 이것이 TSubclassOf를 만든 이유다. UClass를 저장하고 BP_Tank 블루 프린트에서 BP_Projectile롸 동일하게 설정할 수 있기 때문이다.
이제 액터를 만들어 본다
GetWorld()-> SpawnActor()로 호출하면 이 함수에 인풋으로 C++타입을 꺾쇠 괄호 안에 넣어야 한다는 것을 알 수 있다.
UPROPERTY(EditDefaultsOnly, Category = "Combat")
TSubclassOf<class AProjectile> ProjectileClass; //클래스 타입을 나타내는 C++변수, 타입은 AProjectile
void ABasePawn::Fire()
{
FVector Location = ProjectileSpawnPoint->GetComponentLocation();
FRotator Rotation = ProjectileSpawnPoint->GetComponentRotation();
GetWorld()->SpawnActor<AProjectile>(ProjectileClass, Location, Rotation );
}
베럴의 끝에서 발사체가 만들어지고 공중에 그냥 떠있는 것이 이상해 보이지만 아직 1단계다!!
TIL: 발사체 액터를 생성하는 것이 이렇게 어려운 일이구나...TSubclassOf 부터 이제 머리에 한계가 오기 시작했다. Spawn되는 액터를 Projectile로 만들어준다는건가?.. 다시 정리하면서 읽어봐야겠다.
오늘 한 것을 살짝 정리해본다
1. TSubclassOf란?
TSubclassOf<AProjectile> ProjectileClass;
상황 예시:
FVector Loc = FVector(0.0f, 0.0f, 0.0f);
FRotator Rot = FRotator::ZeroRotator;
// TSubclassOf 변수로 블루프린트 클래스 참조
if (ProjectileClass)
{
GetWorld()->SpawnActor<AProjectile>(ProjectileClass, Loc, Rot);
}
예를 들어 정리
1. C++에서 AProjectile이라는 클래스가 있고, 이를 기반으로 블루프린트 BP_Projectile을 생성했습니다
2. BP_Projectile에서 메시와 속성을 설정했습니다.
3. 게임 도중 이 블루프린트를 기반으로 발사체를 생성하려면: