* ObjectInitializer : 오브젝트를 초기화
GPCWeapon 클래스의 AGPCWeapon() 생성자 수정
GPCWeapon 클래스의 AGPCWeapon() 생성자에 추가했던 (const FObjectInitializer const& ObjectInitiablizer = FObjectInitializer ::Get()) 기본 인수는 다시 지워주었다.
* SUPER() 멤버이니셜라이저와 Mesh를 정의했던 부분도 지워 AGPCWeapon() 생성자를 초기 상태로 만든다.
class GPC_CPP_12_API AGPCWeapon:
public AActor,
public IActableOneInterface,
public IWeaponInterface
{
public:
AGPCWeapon();
private:
UPROPERTY(VisibleAnywhere)
USkeletalMeshComponent* Mesh;
protected:
UPROPERTY(VisibleAnywhere)
EWeaponType WeaponType;
UPROPERTY(EditDefaultsOnly, Meta = (AllowPrivateAccess))
float Damage;
};
그리고 그냥 Mesh 변수는 MeshComponent에서 SkeletalMeshComponent 형식으로 바꿔주었다.
* SkeletalMesh는 StaticMesh로 바꾸는건 어렵지 않다. 반대로 StaticMesh를 SkeletalMesh로도 바꿀 순 있다.
또한 WeaponType과 Damage 변수의 접근 지정자를 private: 에서 protected: 로 변경해주었다
GPCWeapon 클래스의 cpp파일에선 GPC_CPP_12.h 헤더파일을 포함시켜주고, CreateDefaultSubobjectAuto()로
Mesh 인스턴스를 생성하였다. 이를 사용하면 알아서 type이 지정되어 생성된다.
바로 Mesh를 SetRootComponent()로 Root 지정도 해주었다.
#include "GPCWeapon.h"
#include "GPC_CPP_12.h"
AGPCWeapon::AGPCWeapon()
{
CreateDefaultSubobjectAuto(Mesh);
SetRootComponent(Mesh);
}
무기 Mesh 찾아보기
EpicGames의 마켓 플레이스에서 Military Weapons Dark 검색하여 다운로드 해주었다.
이 에셋에서 사용하지 않을 Grenade_Launcher / Pistols / Rocket_Launcher / Sniper_Rifle과 관련된 모든 파일들은 용량
정리를 위해 다 지워주었다. (애니메이션, FX, Sound, Textures 등등)
우리가 사용할 무기인 Assault_Rifle과 Shotgun만 남겨두면 된다.
※ Reload_Rifle_Ironsights_W 애니메이션을 보면 마지막에 사운드 큐가 RocketLauncher로 잘못 들어가있을 것이다.
이를 Rifle_ReloadInsert_Cue 사운드 큐로 바꿔줄 것
StaticMesh ↔ SkeletalMesh 변환
SkeletalMesh에서 StaticMesh로 변환하기
해당하는 SkeletalMesh 내부에 상단에 스태틱 메시 만들기 키를 눌러주면 StaticMesh로 바로 만들 수 있다.
StaticMesh에서도 마찬가지로 소켓 만들기가 가능하다. (소켓을 만들면 원래 Root bone이 있던 곳에 생성된다)
StaticMesh에서 SkeletalMesh로 변환하기
먼저 변환하고자 하는 StaticMesh를 우클릭 - 애셋 액션 / 익스포트를 선택하여 FBX 익스포트를 해준다.
* 여기서 StaticMesh 파일의 이름이 SkeletalMesh로 변환했을 때 bone의 이름이 된다.
그리고 다시 익스포트한 FBX를 임포트해준다.
여기서 메시의 Skeletal Mesh를 선택해주어야 한다.
그러면 Skeleton을 선택할 수 있는데, 그냥 비워주면 된다.
이제 임포트한 SkeletalMesh를 보면 bone의 이름이 익스포트할 때 파일의 이름으로 만들어진 것을 볼 수 있다.
Material이 깨져있는 것을 볼 수 있는데, 이는 StaticMesh일 때의 원본 Material로 바꿔주면 된다.
GPCWeapon의 파생 클래스 GPCGun 클래스 생성
언리얼 에디터에서 GPCWeapon 클래스를 우클릭 - GPCWeapon 파생 C++ 클래스 GPCGun을 생성해주었다.
GPCGun.h
생성된 GPCGun 클래스의 헤더의 선언부분이다.
#include "CoreMinimal.h"
#include "GPCWeapon.h"
#include "GPCGun.generated.h"
UCLASS(Abstract)
class GPC_CPP_12_API AGPCGun : public AGPCWeapon
{
GENERATED_BODY()
public:
AGPCGun();
void OnConstruction(FTransform const& Transform) override;
#pragma region IActableOneInterface
private:
void BeginActionA() final override { BeginFire(); }
void BeginActionB() final override { BeginAiming(); }
void BeginActionC() final override { Reload(); }
void EndActionA() final override { EndFire(); }
void EndActionB() final override { EndAiming(); }
void EndActionC() final override {}
//final override를 통해 이 이후로 재정의가 불가능 하도록 함
#pragma endregion
private:
virtual void BeginFire();
virtual void EndFire();
virtual void BeginAiming();
virtual void EndAiming();
virtual void Reload();
private:
UPROPERTY(EditDefaultsOnly, Meta = (AllowPrivateAccess))
FName MuzzleSocket; //총구
UPROPERTY(EditDefaultsOnly, Meta = (AllowPrivateAccess))
TSubclassOf<class UMatineeCameraShake> RecoilClass; //반동
UPROPERTY(VisibleAnywhere)
class UParticleSystemComponent* MuzzleFlash; //총구 화염
UPROPERTY(VisibleAnywhere)
class UAudioComponent* GunshotSound; //소리
};
void BeginActionA() final override { BeginFire(); }
IActableOneInterface의 가상 함수를 오버라이드를 하였지만 final을 붙여 이 이후로 더 이상 재정의가 불가능하도록 했다.
그리고 어짜피 GPCGun의 함수를 호출할 역할뿐이기 때문에 바로 정의를 붙여준다.
(이 함수는 GPCPlayerController - GPCCharacter - GPCGun 순으로 호출됨)
MuzzleSocket : 총구이름 / RecoilClass : 사격 반동 / MuzzleFlash : 총구 화염 / GunShotSound : 소리
void OnConstruction(FTransform const& Transform) override;
OnConstruction() 함수를 선언했다. 블루프린트에서 설정한 내용을 생성자에서 바로 받아올 수 없는 문제 때문이다.
이후, cpp에 함수들의 정의를 만들어준다.
GPCGun.cpp
//포함해주어할 헤더파일들
#include "GPCGun.h"
#include "Particles/ParticleSystemComponent.h"
#include "Components/AudioComponent.h"
#include "GPC_CPP_12.h"
AGPCGun::AGPCGun()
{
WeaponType = EWeaponType::Gun;
CreateDefaultSubobjectAuto(MuzzleFlash);
MuzzleFlash->SetAutoActivate(false);
CreateDefaultSubobjectAuto(GunshotSound);
GunshotSound->SetAutoActivate(false);
}
void AGPCGun::OnConstruction(FTransform const& Transform)
{
FAttachmentTransformRules const Rules(EAttachmentRule::SnapToTarget, true);
MuzzleFlash->AttachToComponent(GetRootComponent(), Rules, MuzzleSocket);
GunshotSound->AttachToComponent(GetRootComponent(), Rules, MuzzleSocket);
}
void AGPCGun::BeginFire()
{
}
void AGPCGun::EndFire()
{
}
void AGPCGun::BeginAiming()
{
}
void AGPCGun::EndAiming()
{
}
void AGPCGun::Reload()
{
}
#include "Particles/ParticleSystemComponent.h"
#include "Components/AudioComponent.h"
#include "GPC_CPP_12.h"
먼저 필요한 헤더파일들을 포함시켜준다.
MuzzleFlash 생성을 위해 ParticleSystemComponent 헤더를 포함해주고
GunshotSound 생성을 위해 AudioComponent 헤더를 포함해주었다.
그리고 인스턴스 생성을 위해 GPC_CPP_12 헤더도 포함했다.
AGPCGun::AGPCGun()
{
WeaponType = EWeaponType::Gun;
CreateDefaultSubobjectAuto(MuzzleFlash);
MuzzleFlash->SetAutoActivate(false);
CreateDefaultSubobjectAuto(GunshotSound);
GunshotSound->SetAutoActivate(false);
}
AGPCGun() 생성자에 해당 WeaponType을 지정해주는 것이다. 따라서 EWeapon::Gun으로 지정해주었다.
그리고 CreateDefaultSubobjectAuto(줄여서 CDSA)를 통해 MuzzleFlash와 GunshotSound 인스턴스 생성을 해준다.
생성과 동시에 SetAutoActivate를 false로 처리해주었다.
void AGPCGun::OnConstruction(FTransform const& Transform)
{
FAttachmentTransformRules const Rules(EAttachmentRule::SnapToTarget, true);
MuzzleFlash->AttachToComponent(GetRootComponent(), Rules, MuzzleSocket);
GunshotSound->AttachToComponent(GetRootComponent(), Rules, MuzzleSocket);
}
위에서 생성한 MuzzleFlash / GunshotSound 를 총구 위치에 붙여주어야 한다.
우선 Mesh가 있지만, 총구의 위치가 어딘지 알 수 없다. 따라서 MuzzleSocket 변수를 사용할 것이다.
Rifle의 SkeletalMesh를 보면 MuzzleFlash 소켓이 따로 존재하는데 이게 총구를 뜻한다.
이제 블루프린트 상에서 MuzzleSocket 변수에 MuzzleFlash 이름을 넣어주어 소켓의 위치를 알려줄 것이다.
그래서 이를 반영하기 위해 OnConstruction()에서 붙여주게된 것이다.
Rules 변수를 만들고, MuzzleFlash의 AttachToComponent()를 사용할 것이다.
* AttachToComponent(ParentComponent, Rules, SocketName);
MuzzleFlash와 GunshotSound는 Mesh에 붙일 것이기 때문에 GetRootComponent()로 현재 RootComponent인 Mesh를 받아왔다.
이후 빌드하고, 언리얼 에디터에서 GPCGun 기반 블루프린트를 생성(MyGPCGun)하였다.
MyGPCGun 블루프린트의 클래스 디폴트에서 MuzzleSocket에 MuzzleFlash(소켓이름)을 넣어주면 Construction()으로
인해 바로 반영되어 MuzzleFlash와 GunshotSound Component가 이동되는 것을 볼 수 있다.
'언리얼엔진' 카테고리의 다른 글
[C++] BeginFire() 수정 / SpawnActor() / AttachToComponent (0) | 2023.09.26 |
---|---|
[C++] MuzzleFlash, Sound 활성화 / GPCBullet (0) | 2023.09.22 |
[C++] ActableOneInterface / WeaponInterface (0) | 2023.09.20 |
[C++] 스테이트 머신(Standing - Crouching) / 에임 오프셋 / Camera Lag Enable (0) | 2023.09.19 |
[C++] 인터페이스 구현, 상속, 호출 (0) | 2023.09.18 |