글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자입니다

 

목차

     

     


     

     

    Weapon - Equip&Unequip, Aim 구현하기

     

     
    Characters
      CAnimInstance.h .cpp
    CPlayer.h .cpp
    Utilities
      CHelpers.h
    CLog.h .cpp
    Weapons
      CWeapon.h .cpp
    CWeapon_AR4.h .cpp
    CWeaponComponent.h .cpp
    CGameMode.h .cpp
    Global.h
    CAnimNotifyState_Equip.h .cpp 생성

     


     

    CWeapon

     

    CWeapon.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "CWeapon.generated.h"
    
    //직렬화 해야하는 struct는 클래스 사이에 선언해야 한다.
    USTRUCT()
    struct FWeaponAimData
    {
    	GENERATED_BODY()
    
    public:
    	UPROPERTY(EditAnywhere)
    		float TargetArmLength;
    
    	UPROPERTY(EditAnywhere)
    		FVector SocketOffset;
    
    	UPROPERTY(EditAnywhere)
    		float FieldOfView;
    
    public:
    	void SetData(class ACharacter* InOwner);
    	void SetDataByNoneCurve(class ACharacter* InOwner);
    
    };
    
    //abstract를 쓰면 Unreal Editor에서 추상 베이스 클래스로 취급하겠다는 의미. 추상이되면 배치시킬 수 없다.
    UCLASS(abstract) 
    class U2212_04_API ACWeapon : public AActor
    {
    	GENERATED_BODY()
    
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		FName HolsterSocketName;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		class UAnimMontage* EquipMontage;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		float EquipMontage_PlayRate;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		FName RightHandSocketName;
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "Aim")
    		FWeaponAimData BaseData;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Aim")
    		FWeaponAimData AimData;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Aim")
    		class UCurveFloat* AimCurve;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Aim")
    		float AimingSpeed = 200;
    
    private:
    	UPROPERTY(VisibleAnywhere)
    		class USceneComponent* Root;
    
    protected:
    	UPROPERTY(VisibleAnywhere)
    		class USkeletalMeshComponent* Mesh;
    
    private:
    	UPROPERTY(VisibleAnywhere)
    		class UTimelineComponent* Timeline;
    
    public:	
    	ACWeapon();
    
    protected:
    	virtual void BeginPlay() override;
    
    public:	
    	virtual void Tick(float DeltaTime) override;
    
    public:
    	bool CanEquip();
    	void Equip();
    	void Begin_Equip();
    	void End_Equip();
    
    	bool CanUnequip();
    	void Unequip();
    
    public:
    	bool CanAim();
    	void Begin_Aim();
    	void End_Aim();
    
    private:
    	UFUNCTION()
    		void OnAiming(float Output);
    	
    private:
    	bool bEquipping;
    	bool bInAim;
    	bool bFiring;
    	bool bReload;
    	bool bAutoFire = true;
    
    private:
    	class ACPlayer* Owner;
    };

     

     

     

    CWeapon.cpp

    더보기
    #include "Weapons/CWeapon.h"
    #include "Global.h"
    #include "Character/CPlayer.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Components/TimelineComponent.h"
    #include "GameFramework/SpringArmComponent.h"
    #include "Camera/CameraComponent.h"
    
    void FWeaponAimData::SetData(ACharacter* InOwner)
    {
    	USpringArmComponent* springArm = CHelpers::GetComponent<USpringArmComponent>(InOwner);
    	springArm->TargetArmLength = TargetArmLength;
    	springArm->SocketOffset = SocketOffset;
    }
    
    void FWeaponAimData::SetDataByNoneCurve(ACharacter* InOwner)
    {
    	USpringArmComponent* springArm = CHelpers::GetComponent<USpringArmComponent>(InOwner);
    	springArm->TargetArmLength = TargetArmLength;
    	springArm->SocketOffset = SocketOffset;
    
    	UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(InOwner);
    	camera->FieldOfView = FieldOfView; //curve를 이용하여 FieldOfView를 만들어준다. 
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    ACWeapon::ACWeapon()
    {
    	PrimaryActorTick.bCanEverTick = true;
    
    	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
    	CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Mesh, "Mesh", Root);
    
    	CHelpers::CreateActorComponent<UTimelineComponent>(this, &Timeline, "Timeline");
    	//CurveFloat 
    	CHelpers::GetAsset<UCurveFloat>(&AimCurve, "CurveFloat'/Game/Character/Weapons/Curve_Aim.Curve_Aim'");
    }
    
    void ACWeapon::BeginPlay()
    {
    	Super::BeginPlay();
    
    	Owner = Cast<ACPlayer>(GetOwner());
    
    	if (HolsterSocketName.IsValid())
    		CHelpers::AttachTo(this, Owner->GetMesh(), HolsterSocketName);
    
    	BaseData.SetDataByNoneCurve(Owner);
    
    	if(!!AimCurve)
    	{
    		FOnTimelineFloat timeline;
    		timeline.BindUFunction(this, "OnAiming");
    
    		Timeline->AddInterpFloat(AimCurve, timeline); //이벤트 객체를 다룸.
    		Timeline->SetLooping(false);
    		Timeline->SetPlayRate(AimingSpeed);
    	}
    	
    }
    
    void ACWeapon::Tick(float DeltaTime)
    {
    	Super::Tick(DeltaTime);
    
    }
    
    bool ACWeapon::CanEquip()
    {
    	bool b = false;
    	//3개 다 false일 때 b = false
    	//3개 중 하나라도 true면 b = true
    	b |= bEquipping;
    	b |= bReload;
    	b |= bFiring;
    
    	return !b; //b = false일 때 True를 리턴
    }
    
    void ACWeapon::Equip()
    {
    	bEquipping = true;
    
    	if (!!EquipMontage)
    		Owner->PlayAnimMontage(EquipMontage, EquipMontage_PlayRate);
    }
    
    void ACWeapon::Begin_Equip()
    {
    	if (RightHandSocketName.IsValid())
    		CHelpers::AttachTo(this, Owner->GetMesh(), RightHandSocketName);
    }
    
    void ACWeapon::End_Equip()
    {
    	bEquipping = false;
    }
    
    bool ACWeapon::CanUnequip()
    {
    	bool b = false;
    	b |= bEquipping;
    	b |= bReload;
    	b |= bFiring;
    
    	return !b;
    }
    
    void ACWeapon::Unequip()
    {
    	if (HolsterSocketName.IsValid())
    		CHelpers::AttachTo(this, Owner->GetMesh(), HolsterSocketName);
    }
    
    bool ACWeapon::CanAim()
    {
    	bool b = false;
    	b |= bEquipping;
    	b |= bReload;
    	b |= bInAim;
    
    	return !b;
    }
    
    void ACWeapon::Begin_Aim()
    {
    	bInAim = true;
    
    	//우리는 생성자 ACWeapon()에서 AimCurve를 가져왔다.
    	if (!!AimCurve) //AimCurve가 있으면 true, 없으면 nullptr이기 때문에 false
    	{
    		Timeline->PlayFromStart();
    		AimData.SetData(Owner);
    
    		return;
    	}
    
    	AimData.SetDataByNoneCurve(Owner);
    }
    
    void ACWeapon::End_Aim()
    {
    	CheckFalse(bInAim);
    	bInAim = false;
    
    	if (!!AimCurve)
    	{
    		Timeline->ReverseFromEnd();
    		BaseData.SetData(Owner);
    
    		return;
    	}
    
    	BaseData.SetDataByNoneCurve(Owner);
    
    }
    
    void ACWeapon::OnAiming(float Output)
    {
    	UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(Owner);
    	camera->FieldOfView = FMath::Lerp(AimData.FieldOfView, BaseData.FieldOfView, Output);
    }

     

    생성자에 ACWeapon()AimCurve를 넣어준다.

    • CHelpers::GetAsset<UCurveFloat>(&AimCurve, "주소 레퍼런스 복사'");

    AimCurve의 유무에 따라 SetData()를 부를지 SetDataByNoneCurve()를 부를지 결정한다. 

    • AimCurve가 있다면, void FWeaponAimData::SetData(ACharacter* InOwner)
    • AimCurve가 없다면, void FWeaponAimData::SetDataByNoneCurve(ACharacter* InOwner)

     

     

    bool 조건체크 Tip

    • 다음과 같이 처음에 bool 변수 b을 false로 설정한다.
    • 아래에 bool 체크를 해야되는 것들이 true인지 false인지 체크한다.
    • 하나라도 true가 되면 b가 true가 된다.
      • b=true면 False를 리턴한다
      • b=false면 True를 리턴한다. (bEquipping, bReload, bFiring 모두 false인 경우이다).

     

     

     


     

    CWeapon_AR4

     

    CWeapon_AR4.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Weapons/CWeapon.h"
    #include "CWeapon_AR4.generated.h"
    
    UCLASS()
    class U2212_04_API ACWeapon_AR4 : public ACWeapon
    {
    	GENERATED_BODY()
    
    public:
    	ACWeapon_AR4();
    };

     

     

    CWeapon_AR4.cpp

    더보기
    #include "Weapons/CWeapon_AR4.h"
    #include "Global.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Animation/AnimMontage.h"
    
    ACWeapon_AR4::ACWeapon_AR4()
    {
    	USkeletalMesh* mesh;
    	CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/FPS_Weapon_Bundle/Weapons/Meshes/AR4/SK_AR4.SK_AR4'");
    	Mesh->SetSkeletalMesh(mesh);
    
    	//Equip
    	{
    		HolsterSocketName = "Rifle_AR4_Holster";
    		CHelpers::GetAsset<UAnimMontage>(&EquipMontage, "AnimMontage'/Game/Character/Animations/Rifle_Equip_Montage.Rifle_Equip_Montage'");
    		EquipMontage_PlayRate = 2.0f;
    		RightHandSocketName = "Rifle_AR4_RightHand";
    	}
    
    	//Aim
    	{
    		BaseData.TargetArmLength = 200;
    		BaseData.SocketOffset = FVector(0, 50, 15);
    		BaseData.FieldOfView = 90;
    
    		AimData.TargetArmLength = 80;
    		AimData.SocketOffset = FVector(0, 55, 10);
    		AimData.FieldOfView = 65;
    	}
    }

    #include "Animation/AnimMontage.h" 추가

    Equip Montage 넣어준다.

    • CHelpers::GetAsset<UAnimMontage>(&EquipMontage, "레퍼런스 복사한 경로를 넣어준다.");

     

     


     

    WeaponComponent

     

    WeaponComponent.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "Components/ActorComponent.h"
    #include "CWeaponComponent.generated.h"
    
    //BlueprintType을 써서 BP에서 공개되게 한다. 추후에 Animation BP에서 다루어야 한다. 추후에 switch로 Weapon Blending을 할 것이다.
    UENUM(BlueprintType) 
    enum class EWeaponType : uint8
    {
    	AR4, AK47, Pistol, Max,
    };
    
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FWeaponTypeChanged, EWeaponType, InPrevType, EWeaponType, InNewType);
    
    
    UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
    class U2212_04_API UCWeaponComponent : public UActorComponent
    {
    	GENERATED_BODY()
    
    private:
    	UPROPERTY(EditAnywhere, Category = "Settings")
    		TArray<TSubclassOf<class ACWeapon>> WeaponClasses; //Spawn시킬 때 class 타입을 TSubclassOf로 명시해준다. TArray는 그것에 대한 배열이다.
    
    public:
    	FORCEINLINE bool IsUnarmedMode() { return Type == EWeaponType::Max; }//Max는 아무것도 선택안한 상황.
    	FORCEINLINE bool IsAR4Mode() { return Type == EWeaponType::AR4; }
    	FORCEINLINE bool IsAK47Mode() { return Type == EWeaponType::AK47; }
    	FORCEINLINE bool IsPistolMode() { return Type == EWeaponType::Pistol; }
    
    public:	
    	// Sets default values for this component's properties
    	UCWeaponComponent();
    
    protected:
    	// Called when the game starts
    	virtual void BeginPlay() override;
    
    public:	
    	// Called every frame
    	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
    
    private:
    	class ACWeapon* GetCurrWeapon();//현재 무기를 리턴
    
    public:
    	void SetUnarmedMode();
    	void SetAR4Mode();
    	//void SetAK47Mode();
    	//void SetPistolMode();
    
    private:
    	void SetMode(EWeaponType InType);
    	void ChangeType(EWeaponType InType);
    
    public:
    	void Begin_Equip();
    	void End_Equip();
    
    public:
    	void Begin_Aim();
    	void End_Aim();
    
    public:
    	FWeaponTypeChanged OnWeaponTypeChanged;
    
    private:
    	EWeaponType Type = EWeaponType::Max; //Max는 아무것도 선택안한 상황.
    
    private:
    	class ACPlayer* Owner; //플레이어 변수
    	TArray<class ACWeapon*> Weapons; //무기 배열 변수
    };
    • void Begin_Equip();
    • void End_Equip();
    • void Begin_Aim();
    • void End_Aim();
    • 4개 부분을 작업하였다.

     

     

    WeaponComponent.cpp

    더보기
    #include "Weapons/CWeaponComponent.h"
    #include "Global.h"
    #include "CWeapon.h"
    #include "Character/CPlayer.h"
    
    UCWeaponComponent::UCWeaponComponent()
    {
    	PrimaryComponentTick.bCanEverTick = true;
    }
    
    void UCWeaponComponent::BeginPlay()
    {
    	Super::BeginPlay();
    
    	Owner = Cast<ACPlayer>(GetOwner());
    	CheckNull(Owner);
    
    	FActorSpawnParameters params;
    	params.Owner = Owner;
    	params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    
    	//받은 타입을 Spawn 시킨다.
    	for(TSubclassOf<ACWeapon> weaponClass : WeaponClasses)
    	{
    		if(!!weaponClass)
    		{
    			ACWeapon* weapon = Owner->GetWorld()->SpawnActor<ACWeapon>(weaponClass, params);//weaponClass 타입으로 Spawn시킨다.
    			Weapons.Add(weapon);
    		}
    	}
    }
    
    void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
    	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    }
    
    ACWeapon* UCWeaponComponent::GetCurrWeapon()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);//IsUnarmedMode가 true면 무기 리턴해줄수 없으므로 nullptr로 리턴 
    
    	return Weapons[(int32)Type]; //BP의 to int //현재 선택된 Weapon을 리턴해준다.
    }
    
    void UCWeaponComponent::SetUnarmedMode()
    {
    	CheckFalse(GetCurrWeapon()->CanUnequip());//현재 무기가 해제될 수 있는지 체크
    
    	GetCurrWeapon()->Unequip(); //무기 해제
    	ChangeType(EWeaponType::Max);//현재 무기를 Max로 바꾸어줌(Max는 아무것도 선택안한 상태)
    }
    
    void UCWeaponComponent::SetAR4Mode()
    {
    	SetMode(EWeaponType::AR4);
    }
    
    void UCWeaponComponent::SetMode(EWeaponType InType)
    {
    	if(Type == InType) //현재 무기와 장착하려는 무기가 같은 경우
    	{
    		SetUnarmedMode(); //무기 장착 해제
    
    		return; //무기 장착 해제 후 리턴
    	}
    	else if (IsUnarmedMode() == false)
    	{
    		CheckFalse(GetCurrWeapon()->CanUnequip());//현재 무기가 해제될 수 있는지 체크
    
    		GetCurrWeapon()->Unequip(); //무기 해제
    	}
    
    	CheckNull(Weapons[(int32)InType]); //장착될 무기가 있는지 체크
    	CheckFalse(Weapons[(int32)InType]->CanEquip());//장착될 무기가 장착될 수 있는 상황인지 체크
    
    	Weapons[(int32)InType]->Equip(); // 무기 장착
    
    	ChangeType(InType); //무기 변경 알려줌
    
    }
    
    void UCWeaponComponent::ChangeType(EWeaponType InType)
    {
    	EWeaponType type = Type;
    	Type = InType; //기존의 무기 타입에서 바뀐 무기 타입을 넣어줌.
    
    	if (OnWeaponTypeChanged.IsBound())
    		OnWeaponTypeChanged.Broadcast(type, InType);//기존 무기 타입, 바뀐 무기 타입
    	
    }
    
    void UCWeaponComponent::Begin_Equip()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->Begin_Equip();
    }
    
    void UCWeaponComponent::End_Equip()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->End_Equip();
    }
    
    void UCWeaponComponent::Begin_Aim()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->Begin_Aim();
    }
    
    void UCWeaponComponent::End_Aim()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->End_Aim();
    }
    • void Begin_Equip(), End_Equip(), Begin_Aim(), End_Aim() 각각 있는지 CheckNull로 체크해준다
      • 추후 디버깅을 위해 Null 체크를 하는 습관을 들이자.
    • GetCurrWeapon()

     

     

    Anim Notify State

     


     

    CAnimNotifyState_Equip

     

    새 C++ 클래스 - AnimNotifyState - CAnimNotifyState_Equip 생성

     

    CAnimNotifyState_Equip.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Animation/AnimNotifies/AnimNotifyState.h"
    #include "CAnimNotifyState_Equip.generated.h"
    
    UCLASS()
    class U2212_04_API UCAnimNotifyState_Equip : public UAnimNotifyState
    {
    	GENERATED_BODY()
    
    public:
    	FString GetNotifyName_Implementation() const override; //_Implementation 재정의 //override는 안 붙여도 되지만 가시성을 위해 붙여주었다.
    
    	void NotifyBegin(USkeletalMeshComponent * MeshComp, UAnimSequenceBase * Animation, float TotalDuration) override;
    	void NotifyEnd(USkeletalMeshComponent * MeshComp, UAnimSequenceBase * Animation) override;
    };

     

     

    CAnimNotifyState_Equip.cpp

    더보기
    #include "CAnimNotifyState_Equip.h"
    #include "Global.h"
    #include "Weapons/CWeaponComponent.h"
    
    FString UCAnimNotifyState_Equip::GetNotifyName_Implementation() const
    {
    	return "Equip";
    }
    
    void UCAnimNotifyState_Equip::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
    	float TotalDuration)
    {
    	Super::NotifyBegin(MeshComp, Animation, TotalDuration);
    	CheckNull(MeshComp);
    	CheckNull(MeshComp->GetOwner());
    
    	UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(MeshComp->GetOwner());
    	CheckNull(weapon);
    
    	weapon->Begin_Equip();
    }
    
    void UCAnimNotifyState_Equip::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
    {
    	Super::NotifyEnd(MeshComp, Animation);
    	CheckNull(MeshComp);
    	CheckNull(MeshComp->GetOwner());
    
    	UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(MeshComp->GetOwner());
    	CheckNull(weapon);
    
    	weapon->End_Equip();
    }

     

     

     

     

     

    BlueprintNativeEvent

    • BlueprintNativeEvent를 사용하면 GetNotifyName_Implementation으로 정의해줘야 한다.
    • 해당 클래스에 _Implementation이 없다면 자식 클래스에서 _Implementation으로 재정의해서 쓰라는 의미이다. 

     

     

     


     

     

     

     

    Rifle_Equip_Montage - ANS Equip 할당

     

    Rifle_Equip_Montage

    • 소켓 추가 - Rifle_AR4_RightHand
    • 노티파이 - 노티파이 스테이트 추가 - C Anim Notify State Equip을 22프레임부터 끝나는 시점까지 할당.

     


     

    CHelper - 소켓에 붙이는 AttachTo 함수 추가하기

     


     

    CHelper

     

    CHelper.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    
    #define CheckTrue(x) { if(x == true) return; }
    #define CheckTrueResult(x, y) { if(x == true) return y; }
    
    #define CheckFalse(x) { if(x == false) return;}
    #define CheckFalseResult(x, y) { if(x == false) return y;}
    
    #define CheckNull(x) { if(x == nullptr) return;}
    #define CheckNullResult(x, y) { if(x == nullptr) return y;}
    
    
    #define CreateTextRender()\
    {\
    	CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Tex", Root);\
    	Text->SetRelativeLocation(FVector(0, 0, 100));\
    	Text->SetRelativeRotation(FRotator(0, 180, 0));\
    	Text->SetRelativeScale3D(FVector(2));\
    	Text->TextRenderColor = FColor::Red;\
    	Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;\
    	Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));\
    }
    
    
    class U2212_04_API CHelpers
    {
    public:
    	template<typename T>
    	static void CreateComponent(AActor* InActor, T** OutComponent, FName InName, USceneComponent* InParent = nullptr)
    	{
    		*OutComponent = InActor->CreateDefaultSubobject<T>(InName);
    
    		if (!!InParent)
    		{
    			(*OutComponent)->SetupAttachment(InParent);
    
    			return;
    		}
    
    		InActor->SetRootComponent(*OutComponent);
    	}
    
    	//CreateActorComponent 추가
    	template<typename T>
    	static void CreateActorComponent(AActor* InActor, T** OutComponent, FName InName)
    	{
    		*OutComponent = InActor->CreateDefaultSubobject<T>(InName);
    	}
    
    	template<typename T>
    	static void GetAsset(T** OutObject, FString InPath)
    	{
    		ConstructorHelpers::FObjectFinder<T> asset(*InPath);
    		*OutObject = asset.Object;
    	}
    
    	template<typename T>
    	static void GetAssetDynamic(T** OutObject, FString InPath)
    	{
    		*OutObject = Cast<T>(StaticLoadObject(T::StaticClass(), nullptr, *InPath));
    	}
    
    	template<typename T>
    	static void GetClass(TSubclassOf<T>* OutClass, FString InPath)
    	{
    		ConstructorHelpers::FClassFinder<T> asset(*InPath);
    		*OutClass = asset.Class;
    	}
    
    	template<typename T>
    	static T* FindActor(UWorld* InWorld)
    	{
    		for (AActor* actor : InWorld->GetCurrentLevel()->Actors)
    		{
    			if (!!actor && actor->IsA<T>())
    				return Cast<T>(actor);
    		}
    
    		return nullptr;
    	}
    
    	template<typename T>
    	static void FindActors(UWorld* InWorld, TArray<T*>& OutActors)
    	{
    		for (AActor* actor : InWorld->GetCurrentLevel()->Actors)
    		{
    			if (!!actor && actor->IsA<T>())
    				OutActors.Add(Cast<T>(actor));
    		}
    	}
    
    	template<typename T>
    	static T* GetComponent(AActor* InActor)
    	{
    		return Cast<T>(InActor->GetComponentByClass(T::StaticClass()));
    	}
    
    	template<typename T>
    	static T* GetComponent(AActor* InActor, const FString& InName)
    	{
    		TArray<T*> components;
    		InActor->GetComponents<T>(components);
    
    		for (T* component : components)
    		{
    			if (component->GetName() == InName)
    				return component;
    		}
    
    		return nullptr;
    	}
    
    	static void AttachTo(AActor* InActor, USceneComponent* InParent, FName InSocketName)
    	{
    		InActor->AttachToComponent(InParent, FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSocketName);
    	}
    }

     

     

    추가된 코드

    static void AttachTo(AActor* InActor, USceneComponent* InParent, FName InSocketName)
    {
        InActor->AttachToComponent(InParent, FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSocketName);
    }

     


     

     

     

    실행화면