[C++] Socket에 Gun 위치 / 탄피 / 반동 / 피격효과
* MyGPC... 블루프린트 이름들을 모두 BP_...으로 이름을 수정함
블루프린트 뷰 포트 상에서 Character의 손에 Gun 위치시키기
GPCCharacter 클래스 헤더에서 필요한 함수와 변수를 추가했다.
UCLASS(Abstract)
class GPC_CPP_12_API AGPCCharacter:
public ACharacter,
public IPosableOneInterface,
public IActableOneInterface
{
public:
void OnConstruction(FTransform const& Transform) override;
protected:
UPROPERTY(EditDefaultsOnly)
FName GunSocket;
UPROPERTY()
UChildActorComponent* Gun;
};
먼저 OnConstruction() 함수를 추가해주었다.
그리고 ChildActorComponent로 Gun을 만들어주고, 소켓의 이름을 담을 GunSocket 변수를 만들어주었다.
* GunSocket은 블루프린트 상에서 총이 위치할 소켓의 이름을 넣어줄 것임.
* 저번에 GPCCharacter 클래스에 추가했던 class IActableOneInterface* Gun;은 지워주었다.
GPCCharacter 클래스의 CPP의 AGPCCharacter() 생성자에서 먼저 Gun을 생성하고, Root Component에 붙여주었다.
AGPCCharacter::AGPCCharacter()
{
CreateDefaultSubobjectAuto(Gun)->SetupAttachment(GetRootComponent());
}
void AGPCCharacter::OnConstruction(FTransform const& Transform)
{
Super::OnConstruction(Transform);
FAttachmentTransformRules const Rules(EAttachmentRule::SnapToTarget, true);
Gun->AttachToComponent(GetMesh(), Rules, GunSocket);
Gun->SetChildActorClass(GunClass);
Gun->CreateChildActor();
Gun->GetChildActor()->SetOwner(this);
Gun->GetChildActor()->SetInstigator(this);
}
void AGPCCharacter::BeginActionA()
{
if (auto ActableOne = Cast<IActableOneInterface>(Gun->GetChildActor()))
ActableOne->BeginActionA();
}
OnConstruction() 함수에 Super콜을 넣어주었다.(여기서는 부모 Construction에 딱히 뭐가 없어서 하지 않아도 된다)
이제 Gun을 AttachToComponent()로 GunSocket의 위치에 붙여주게 된다.
그리고 Gun ChildActor의 클래스를 GunClass로 지정해주고, CreateChildActor()로 Gun을 생성하게 되는 것이다.
밑의 BeginActionA() 함수는 Gun의 ChildActor의 ActableOneInterface를 받아와서 유효하다면, BeginActionA()를 호출하도록 만들었다. 나머지 BeginAction()과 EndAction() 함수들도 이와 같인 구조로 모두 정의해준다.
이후, 빌드하고 언리얼 에디터로 돌아와서, Idle_Rifle_Hip 애니메이션으로 들어온다.
여기서 스켈레톤 트리 중, 총을 손이 쥐고 있을 hand_r 본 밑에 소켓 추가로 hand_r_gun 소켓을 만들어주었다.
그리고 hand_r_gun 소켓에 프리뷰 애셋 추가를 통해 Rifle을 넣어주고, 소켓의 Location / Rotation을 조절해 총의 방향을 조절해준다.
그리고 BP_Character 블루프린트의 클래스 디폴트에 생긴 Gun Socket과 Gun Class에 각각 바인딩시켜주면 된다.
플레이 시, 총의 모습이 캐릭터에 가려져 잘 보이지 않음으로 SpringArm의 위치를 캐릭터의 오른쪽으로 조금 조정했다.
- Transform 위치 Z : 50.0
- Socket Offset Y / Z : 75.0
Movement->InitialSpeed = 10000;
사격 시, 탄속을 좀 더 빠르게 하기 위해 GPCBullet 클래스 CPP의 AGPCBullet 생성자에서 Movement(ProjectileMovementComponent)의 초기 속도를 10000으로 수정해주었다.
사격 시, 탄피 배출 구현하기
GPCGun 클래스에서 탄피를 구현해줄 것이다.
UCLASS(Abstract)
class GPC_CPP_12_API AGPCGun : public AGPCWeapon
{
GENERATED_BODY()
.
.
.
private:
UPROPERTY(VisibleAnywhere)
class UParticleSystemComponent* AmmoEjection;
UPROPERTY(EditDefaultsOnly, Meta = (AllowPrivateAccess))
FName AmmoEjectionSocket;
};
먼저 클래스 헤더파일에서 탄피로 사용될 ParticleSystemComponent를 하나 만들어준다. (AmmoEjection)
그리고 AmmoEjection 파티클이 위치할 Socket의 이름을 담을 AmmoEjectionSocket 변수도 만들어준다.
* AmmoEjectionSocket 또한 블루프린트 상에서 Socket의 이름을 바인드해줄 것이다.
애초에 Gun의 Skeleton Mesh를 보면 이미 만들어져있는 소켓이 있음.
GPCGun 클래스 CPP로 와서 AGPCGun() 생성자에서 AmmoEjection 컴포넌트를 생성해준다.
그리고 AmmoEjection의 AutoActivate를 일단 false로 설정해주었다.
AGPCGun::AGPCGun()
{
CreateDefaultSubobjectAuto(AmmoEjection);
AmmoEjection->SetAutoActivate(false);
}
void AGPCGun::OnConstruction(FTransform const& Transform)
{
FAttachmentTransformRules const Rules(EAttachmentRule::SnapToTarget, true);
AmmoEjection->AttachToComponent(GetRootComponent(), Rules, AmmoEjectionSocket);
}
void AGPCGun::BeginFire()
{
AmmoEjection->Activate(true);
}
그리고 OnConstruction() 함수에서 AmmoEjection 컴포넌트를 지정한 Socket 위치에 붙여주게 된다.
* 만약 BeginPlay() 함수에 Super::BeginPlay(); 콜이 없다면 꼭 추가해줄 것.
그리고 사격 버튼을 눌러 BeginFire() 함수가 호출되면 AmmoEjection 파티클을 활성화시켜주면 된다.
이제 빌드하고 BP_Gun 블루프린트의 클래스 디폴트에서 AmmoEjection Socket에 소켓 이름을 바인딩 해주었다.
* 만약 소켓 지정 후, 탄피 배출 방향이 어색하다면 Skeleton Mesh에서 해당 소켓의 Rotation을 회전시켜 조절해줄 것.
* 이벤트그래프 상에서 Tick 함수를 임시로 구현해놨던 것은 지워버렸음
사격 시, 카메라 반동 구현
이는 언리얼 에디터에서 블루프린트로 구현해줄 것이다.
콘텐츠 브라우저에서 우클릭 - 블루프린트 클래스 - MatineeCameraShake를 부모로 선택하여 BP_Recoil 블루프린트를
추가해주었다. (Recoil : 반동)
이렇게 BP_Recoil 블루프린트를 만들었다면 BP_Gun 블루프린트의
클래스 디폴트에 있는 Recoil Class에 BP_Recoil을 바인드 해준다.
그리고 BP_Recoil 블루프린트의 클래스 디폴트에서 반동 값을 조절해주면 된다.
이후, GPCGun 클래스 CPP로 와서 사격 시 호출되는 BeginFire() 함수에 카메라 Shake가 시작되도록 추가해준다.
#include "GameFramework/PlayerController.h"
void AGPCGun::BeginFire()
{
if(auto Controller = Cast<APlayerController>(GetInstigatorController()))
Controller->PlayerCameraManager->StartMatineeCameraShake(RecoilClass);
}
CameraShake를 사용하기 전에 먼저 GameFramework/PlayerController.h 헤더를 먼저 포함시켜준다.
Controller에 바인딩된 값이 유효하다면, Controller의 PlayerCameraManager의 MatineeCameraShake를 불러주면 된다.
그리고 GPCCharacter 클래스 CPP의 OnConstruction() 함수에 이 부분을 추가해줌.
Gun->GetChildACtor()->SetOwner(this);
Gun->GetChildACtor()->SetInstigator(this);
총알의 피격 효과 구현하기
GPCBullet 클래스에서 새 파티클 컴포넌트를 추가해준다.
UCLASS(Abstract)
class GPC_CPP_12_API AGPCBullet : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleAnywhere)
class UParticleSystemComponent* HitEffect;
UPROPERTY(VisibleAnywhere)
class UDecalComponent* Mark;
};
HitEffect 이름으로 ParticleSystemComponent를 하나 만들어주고, Decal 컴포넌트 Mark도 만들었다.
GPCBullet 클래스 CPP로 와서 먼저 Decal 컴포넌트를 사용하기 위해 DecalComponent.h를 포함해주었다.
#include "Components/DecalComponent.h"
AGPCBullet::AGPCBullet()
{
CreateDefaultSubobjectAuto(HitEffect);
HitEffect->SetupAttachment(Collider);
HitEffect->bAutoActivate = false;
CreateDefaultSubobjectAuto(Mark);
Mark->SetupAttachment(Collider);
Mark->SetFadeScreenSize(0);
}
void AGPCBullet::OnBulletBeginOverlap(AActor* const OverlappedActor, AActor* const OtherActor)
{
HitEffect->Activate();
//DestroyActor(); 잠깐 주석해줌. HitEffect 나오자마자 제거되서 그럼
}
AGPCBullet() 생성자에서 HitEffect와 Mark를 생성해주고, 각각 Collider에 붙여준다.
HitEffect의 AutoActivate는 false로 설정해주었다.
Mark의 SetFadeScreenSize()는 0으로 설정해주었다.
* SetFadeScreenSize : 스크린 사이즈를 기준으로 어느정도 비율 아래로 작아지는 경우, Fade 처리한다.
0이 아니라면 멀어질 때 화면 밖으로 사라져버린다.
그리고 Bullet이 Overlap 충돌되서 OnBulletBeginOverlap()이 호출되면 HitEffect의 Activate를 활성화시켜준다.
이후 BP_Bullet 블루프린트로 와서 Mark 컴포넌트에 Decal을 바인드 해준다.
그리고 데칼 크기를 조금 작게 설정해주었다.
빌드하고 총을 사격해보면 Character가 Capsule을 충돌 대상으로 삼아서 다리나 팔 사이를 총알이 그냥 통과하지 못한다.
이를 해결하고 싶다면 Mesh를 충돌 대상으로 바꿔주어야 한다.
그렇다면 Overlap 이벤트가 아닌 Hit 이벤트로 충돌을 감지해야 한다.