[C++] BeginFire() 수정 / SpawnActor() / AttachToComponent
GPCGun 클래스의 발사 키 입력 시 호출되는 BeginFire() 함수 수정
GPCGun 클래스의 BeginFire() 함수의 정의를 구현해주었다.
#include "GPCBullet.h"
void AGPCGun::BeginFire()
{
MuzzleFlash->Activate(true);
GunshotSound->Play();
FTransform Muzzle = Mesh->GetSocketTransform(MuzzleSocket);
Muzzle.SetLocation(Muzzle.GetLocation() + Muzzle.GetRotation().GetForwardVector() * 25);
FActorSpawnParameters SpawnParameters;
SpawnParameters.Owner = this;
SpawnParameters.Instigator = GetInstigator();
GetWorld()->SpawnActor<AActor>(BulletClass, Muzzle, SpawnParameters);
}
먼저 발사 키 입력 시, BeginFire() 함수가 호출되면서 MuzzleFlash와 GunshotSound가 발생하는 것을 확인했었다.
이어서, GPCBullet이 총의 Muzzle 부분에서부터 스폰되어 날아가도록 해주면 된다.
총의 Mesh에 있는 Muzzle 소켓의 위치를 받아오고, Rotation에서 Forward Vector를 뽑아와 현재 Location에 합하여
총알이 Spawn될 위치를 잡았다.
그리고 이어서 GetWorld()->SpawnActor<>()를 통해서 앞에서 선언했던 BulletClass를 Muzzle의 위치에 Spawn 시킨다.
* 원래는 BulletClass를 GPCGun 클래스에서 포함하는 것은 별로 좋지 않다.
사실 생성을 자체적으로 담당하는 팩토리 클래스를 두고, 그곳에서 Bullet을 생성하고 돌려주는 역할을 수행해주는 것이 가장 좋다.
그렇지만 지금은 GPCGun 클래스 내에서 해결해보는 방법으로 하였다.
그래서 위에 GPCBullet.h 헤더파일을 포함시켜준 것이다.
GetWorld()->SpawnActor<AActor>(BulletClass, Muzzle, SpawnParameters);
SpawnActor<AActor>는 UClass에 받아오는 것이 Actor로 받아오기 위함이다.
따라서 UClass는 Actor의 하위여야만 가능하다.
FActorSpawnParameters
이제 SpawnActor 인자 중, FActorSpawnParameters 인수를 만들어보았다.
FActorSpawnParameters에서 설정할 수 있는 것은 5가지가 있다.
- Name : 이름 지정이 가능. 다른 지정이 없다면 기본적으로 이름 뒤에 0, 1, 2를 붙임
- Template : 복사할 대상의 주소를 주면 복사하여 복사생성자 호출. 설정이 없다면 클래스의 기본값들을 복사해서 생성.
- Owner : 해당 Actor를 Spawn한 대상. CPP에서는 없어도 Spawn이 가능(그래도 설정해주는게 좋음)
- Instigator : 가해자. CPP에서는 없어도 Sapwn 가능(그래도 설정해주는게 좋음)
- OverrideLevel : Actor를 Spawn시킬 ULevel 설정
아무튼 그래서 FActorSpawnParameters 변수를 하나 선언하고, 해당 변수의 Owner와 Instigator만 설정해주었다.
그리고 SpawnParameters를 인수로 넣어주었다.
FActorSpawnParameters SpawnParameters;
SpawnParameters.Owner = this;
SpawnParameters.Instigator = GetInstigator();
GetWorld()->SpawnActor<AActor>(BulletClass, Muzzle, SpawnParameters);
총알 피격 시 실행될 로직 구현하기
OnActorBeginOverlap
블루프린터에서 썼던 것과 마찬가지로 충돌감지는 OnActorBeginOverlap을 사용해주게 될 것이다.
CPP에서 OnActorBeginOverlap는 이벤트 디스패처로 되어있다.
이는 특정 함수를 바인드 해놓으면 특정 시점에 호출해주는 역할을 한다.
OnActorBeginOverlap을 정의 피킹으로 타고가면 아래와 같이 선언이 있는 것을 볼 수 있다.
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams( FActorBeginOverlapSignature, AActor, OnActorBeginOverlap, AActor*, OverlappedActor, AActor*, OtherActor );
- TwoParams : 해당 델리게이트에 바인드 될 함수의 인자 갯수
- FActorBeginOverlapSignature : 해당 형식 이름
- AActor : 소속될 곳
- OnActorBeginOverlap : 이름
- AActor*, OverlappedActor : 바인드 될 인자 목록1
- AActor*, OtherActor : 바인드 될 인자 목록2
이를 델리게이트라고 부르는데, 반환형이 없다. ( 멀티캐스트 델리게이트 )
특정 함수를 본인에게 바인드했다가 콜 한번으로 바인드 된 모든 함수의 호출을 대리로 수행해주는 내용.
함수포인터와 유사하다고 볼 수 있다.
이제 OnActorBeginOverlap의 두 인자 형식에 맞는 값을 바인드 해주어 사용하면 된다.
먼저 GPCBullet 클래스 헤더파일에서 OnBulletBeginOverlap()함수를 선언해주었다.
해당 함수의 인자로는 OnActorBeginOverlap의 두 인자를 똑같이 넣어준다.
* 델리게이트를 통해 바인드해주고 싶다면 반드시 UFUNCTION()을 붙여주어야 한다.
UCLASS(Abstract)
class GPC_CPP_12_API AGPCBullet : public AActor
{
GENERATED_BODY()
.
.
.
private:
UFUNCTION()
void OnBulletBeginOverlap(AActor* const OverlappedActor, AActor* const OtherActor);
};
이어서 GPCBullet 클래스 CPP에서 OnBulletBeginOverlap 함수의 정의를 만들어 둔다.
그리고 #include "Kismet/GameplayStatics.h" 헤더파일을 포함시켜준다.
이 헤더에는 게임 플레이에 관련된 스태틱 함수들이 포함되어 있다.
#include "Kismet/GameplayStatics.h"
AGPCBullet::AGPCBullet()
{
.
.
.
OnActorBeginOverlap.AddDynamic(this, &ThisClass::OnBulletBeginOverlap);
}
.
.
.
void AGPCBullet::OnBulletBeginOverlap(AActor* const OverlappedActor, AActor* const OtherActor)
{
UGameplayStatics::ApplyDamage
(
OtherActor,
1,
GetInstigatorController(),
GetOwner(),
UDamageType::StaticClass()
);
}
OnActorBeginOverlap.AddDynamic(this, &ThisClass::OnBulletBeginOverlap);
* Add / AddUnique : 같은 내용에 대한 바인드 중복 추가를 허용할 것인지 유무
AGPCBullet() 생성자 함수에서는 OnActorBeginOverlap 델리게이트에 우리가 선언한 OnBulletBeginOverlap 함수를
바인드 해주었다.
OnBulletBeginOverlap() 함수에서는 GameplayStatics.h 헤더에 있는 스태틱 함수를 사용하게 된다.
이 함수에서는 충돌 시, 처리될 내용들이 담겨있다.
UGameplayStatics::ApplyDamage
(
OtherActor, //맞은 대상
1, //기본 데미지(Base Damage)
GetInstigatorController(),
GetOwner(), //무기(Weapon)
UDamageType::StaticClass()
);
캐릭터에 무기 붙이기
이제 캐릭터의 특정 소켓에 총을 붙여주도록 하겠다.
GPCCharacter 클래스의 헤더에서 무기를 받을 클래스를 선언해뒀다.
UCLASS(Abstract)
class GPC_CPP_12_API AGPCCharacter:
public ACharacter,
public IPosableOneInterface,
public IActableOneInterface
{
GENERATED_BODY()
.
.
.
protected:
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class AGPCGun> GunClass;
class IActableOneInterface* Gun;
};
이제 GPCCharacter 클래스의 CPP에서는에서 무기를 스폰해주도록 했다.
무기를 생성해주기 위해, CPP에도 GPCGun.h 헤더파일을 미리 포함시켜 주었다.
#include "GPCGun.h"
void AGPCCharacter::BeginPlay()
{
Super::BeginPlay();
FActorSpawnParameters SpawnParameters;
SpawnParameters.Owner = this;
SpawnParameters.Instigator = GetInstigator();
FAttachmentTransformRules const Rules(EAttachmentRule::SnapToTarget, true);
auto Temp = GetWorld()->SpawnActor<AGPCGun>(GunClass, FTransform(), SpawnParameters);
Temp->AttachToComponent(GetMesh(), Rules, "Hand_R");
Gun = Temp;
}
.
.
.
void AGPCCharacter::BeginActionA()
{
if (Gun != nullptr)
Gun->BeginActionA();
}