초보 코린이의 성장 일지

UE4 SubAction Hammer Skill 본문

언리얼

UE4 SubAction Hammer Skill

코오린이 2023. 6. 27. 15:55

나이아가라를 이용한 Hammer 스킬을 구현해 볼 것이다.

1. 몽타주에 SubAction State를 넣어준다.

2. 각자 알맞은 데이터 값을 선택해준다.

1. 스킬 시전시 알맞은 몽타주가 나오는걸 볼 수 있다.


1. 나이아가라를 사용하여 발사하기 때문에, Actor 객체로 만들어서 사용할 것이다.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapons/CWeaponStructures.h"
#include "NiagaraDataInterfaceExport.h"
#include "CAura.generated.h"

UCLASS()
class U2212_06_API ACAura
	: public AActor
	, public INiagaraParticleCallbackHandler
{
	GENERATED_BODY()

private:
	UPROPERTY(EditDefaultsOnly, Category = "Damage")
		FHitData HitData;

	UPROPERTY(EditDefaultsOnly, Category = "Damage")
		float DamageInterval = 0.1f; // 데미지 연속적으로 들어가는 값

private:
	UPROPERTY(VisibleAnywhere)
		class USceneComponent* Root;

	UPROPERTY(VisibleAnywhere)
		class UNiagaraComponent* Niagara;

	UPROPERTY(VisibleAnywhere)
		class UBoxComponent* Box; // 나이아가라 충돌체

public:	
	ACAura();

protected:
	virtual void BeginPlay() override;

private:
	// 히트 목록
	TArray<class ACharacter*> Hitted;
	FTimerHandle TimerHandle;

};
#include "Weapons/AddOns/CAura.h"
#include "Global.h"
#include "NiagaraComponent.h"
#include "GameFramework/Character.h"
#include "Components/BoxComponent.h"

ACAura::ACAura()
{
	PrimaryActorTick.bCanEverTick = true;

	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
	CHelpers::CreateComponent<UNiagaraComponent>(this, &Niagara, "Niagara", Root);
	CHelpers::CreateComponent<UBoxComponent>(this, &Box, "Box", Root);

	UNiagaraSystem* niagara;
	CHelpers::GetAsset<UNiagaraSystem>(&niagara, "NiagaraSystem'/Game/sA_StylizedSwordSet/Fx/NS_Ulti_lv1.NS_Ulti_lv1'");
	Niagara->SetAsset(niagara);

}

void ACAura::BeginPlay()
{
	Super::BeginPlay();

}

1. 기본적으로 사용할 컴포넌트와 객체를 생성해준다.


#pragma once

#include "CoreMinimal.h"
#include "Weapons/CSubAction.h"
#include "Weapons/CWeaponStructures.h"
#include "CSubAction_Hammer.generated.h"

UCLASS(Blueprintable)
class U2212_06_API UCSubAction_Hammer : public UCSubAction
{
	GENERATED_BODY()

private:
	UPROPERTY(EditDefaultsOnly, Category = "Aura")
		TSubclassOf<class ACAura> AuraClass;

	// 스판시킬 위치 지정
	UPROPERTY(EditDefaultsOnly, Category = "Aura")
		FVector AuraLoction;
};
#include "Weapons/SubActions/CSubAction_Hammer.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/CStateComponent.h"
#include "Components/CMovementComponent.h"
#include "Weapons/CAttachment.h"
#include "Weapons/CDoAction.h"
#include "Weapons/AddOns/CGhostTrail.h"

void UCSubAction_Hammer::Begin_SubAction_Implementation()
{
	Super::Begin_SubAction_Implementation();

	FActorSpawnParameters params;
	params.Owner = Owner;
	params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;  // 객체가 있던 없던 무조건 Spawn 시키기

	FTransform transform; 
	transform.SetLocation(Owner->GetActorLocation());  // 위치
	transform.AddToTranslation(AuraLoction); // 보정 위치
	transform.SetRotation(FQuat(Owner->GetActorRotation())); // 캐릭터에 전방 방향 (회전방향)

	Owner->GetWorld()->SpawnActor<ACAura>(AuraClass, transform, params);

}

1. Hammer로가서 나이아가라 스킬 이펙트 위치를 조정해준다.


1. 클래스를 이제 생성해준다.

1. 이 이펙트를 사용하게 될 것이다.

 

1. 만들어준 CAura를 선택해준다.

1. 전방방향으로 나가는걸 확인할 수 있다.

1. 바닥쪽을 보면 바닥에 정확히 붙어서 나가는게 아니므로, -90만큼 위치를 내려준다.


1. 사용하고 있는 나이아가라 이펙트에 특정한 값을 꺼내와서 사용해 볼 것이다.

2. 충돌체 크기가 달라질 것이므로, 파티클 업데이트안에서 빼내올 것이다.

1. Export로 빼내오는데 이걸 사용했을시 한계가 존재한다.  Position, Speed, Size 등 float 7개의 값만 빼서 사용이 가능하다.

2. Condition To Export를 체크해주지 않으면, 값을 사용할 수 없다.

3. 빼서 사용하게 될 변수들을 빼내오려면, Link Inputs -> 파티클 카테고리 안에서 가져와야한다.

4. 초기가 붙은 변수가 있고, 안붙은 변수가 있는데 붙은 변수는 파티클이 처음에 Spawn될때 초기값이고, 붙지않은 변수는 업데이트될때 사용하게 될 변수이다.

5. PRESOLVE가 붙은건 처리되기 이전에 값들을 나타낸다.

1. Scale를 선택하게되면 Scale를 빼내오게 된다.

1. Mesh Renderer를 눌러보면, 외부로 뺄 변수들이 보인다.

2. 위에서 빼온 Scale가 이중에 값을 사용하기 위해 빼온거라고 생각하면 된다.

1. 빼내올 데이터 Scale를 지정해 줬지만, 어떠한 값에 의해서 나갈지 선택을 해줘야한다.

 

1. 어떠한 자료형으로 빼줄지 선택해 주면 된다. Object를 사용할 것이다.

1. 선택 후 다시 클릭해서 보면 사용자 파라미터가 선택되어 있는걸 확인할 수 있다.

2. 확인하기 편하게 보도록 이름을 변경해준다.


 1. 상속받은 부모함수에 들어가보면 하나의 함수가 존재한다. 순수가상함수도 아닌 이 함수를 어떠한 방법으로 사용해야 할까 생각을 해봐야 한다.

2. 인터페이스는 기본적으로 순수가상함수를 포함한다. virtual를 붙이지 않아도 순수가상함수로 취급을 해준다.

3. 순수가상함수는 정의가 없고 선언만 되어 있는 상태를 말한다. NativeEvent가 붙어있는 이유는 내가 정의를 해놓을 테니까 필요하면 가져다가 정의해서 사용하라는 의미이다.

4. NativeEvent가 붙어있는걸 사용하려면, Implementation를 붙여서 사용해야하는데 정의가 되어있지도 않다. 이유는 선언만 되어있기 때문에 상속받은 자식으로가서 정의해서 사용하라는 의미이다.

 

1. 우리가 사용하는 이펙트에 본체인데 왼쪽위에 보면 크기가 나온다. 저 크기를 보고 충돌체에 크기를 결정할 것이다.

2. Box를 사용하게되고, 이펙트 x, y, z에 절반 크기를 사용하여 Scale값과 곱하고 결합하면 원래 이펙트에 크기와 동일한 크기로 충돌체 Box 크기가 결정된다.

 

1. Box Extent는 절반값, 위에 Scale 1인 수치가 *해지면 Box 크기가 결정된다.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapons/CWeaponStructures.h"
#include "NiagaraDataInterfaceExport.h"
#include "CAura.generated.h"

UCLASS()
class U2212_06_API ACAura
	: public AActor
	, public INiagaraParticleCallbackHandler // 인터페이스를 상속 받아준다.
{
	GENERATED_BODY()

private:
	UPROPERTY(EditDefaultsOnly, Category = "Damage")
		FHitData HitData;

	UPROPERTY(EditDefaultsOnly, Category = "Damage")
		float DamageInterval = 0.1f; // 데미지 연속적으로 들어가는 값

private:
	UPROPERTY(VisibleAnywhere)
		class USceneComponent* Root;

	UPROPERTY(VisibleAnywhere)
		class UNiagaraComponent* Niagara;

	UPROPERTY(VisibleAnywhere)
		class UBoxComponent* Box; // 나이아가라 충돌체

public:	
	ACAura();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void OnSystemFinished(class UNiagaraComponent* PSystem);

public:
	/*1. 상속받은 부모함수에 들어가보면 하나의 함수가 존재한다.순수가상함수도 아닌 이 함수를 어떠한 방법으로 사용해야 할까 생각을 해봐야 한다.
	  2. 인터페이스는 기본적으로 순수가상함수를 포함한다.virtual를 붙이지 않아도 순수가상함수로 취급을 해준다.
	  3. 순수가상함수는 정의가 없고 선언만 되어 있는 상태를 말한다.NativeEvent가 붙어있는 이유는 내가 정의를 해놓을 테니까 필요하면 가져다가 정의해서 사용하라는 의미이다.
	  4. NativeEvent가 붙어있는걸 사용하려면, Implementation를 붙여서 사용해야하는데 정의가 되어있지도 않다.이유는 선언만 되어있기 때문에 상속받은 자식으로가서 정의해서 사용하라는 의미이다.*/
	// 인터페이스 부모함수 재정의
	void ReceiveParticleData_Implementation(const TArray<FBasicParticleData>& Data, UNiagaraSystem* NiagaraSystem);

private:
	// 히트 목록
	TArray<class ACharacter*> Hitted;
	FTimerHandle TimerHandle;

};
#include "Weapons/AddOns/CAura.h"
#include "Global.h"
#include "NiagaraComponent.h"
#include "GameFramework/Character.h"
#include "Components/BoxComponent.h"

void ACAura::OnSystemFinished(UNiagaraComponent* PSystem)
{
	Destroy();
}

void ACAura::ReceiveParticleData_Implementation(const TArray<FBasicParticleData>& Data, UNiagaraSystem* NiagaraSystem)
{
	INiagaraParticleCallbackHandler::ReceiveParticleData_Implementation(Data, NiagaraSystem);

	// Extent 해놓고 Scale값에 곱해질 값이므로, 0번을 찾아서 넣어준다.
	Box->SetRelativeScale3D(Data[0].Position);

}

 

1. 충돌체가될 Box에 크기를 나이아가라 이펙트에 맞춰주기 위해 Hidden을 켜주고, 작업을 시작한다.

2. Box가 보이지만 사라지지 않으므로, Destory로 제거해준다.

3. Box 크기를 결정해준다.

 

1. 나가는 이펙트 크기와 비슷하게 크기가 결정된걸 확인할 수 있다.

2. 하지만 높이가 맞지 않는다.  이걸 맞춰줄 것이다.

1. 앞으로 나아가는 X축은 일치한다. 밀려서 나가서 그렇게 보일뿐 나머지 축을 잡아줄 것이다.

1. 플레이어 전방은 X축 이지만, 초록색 방향표시는 Y축을 나타낸다. 그래서 처음에 X축을 사용할 수 있도록 회전을 주고 작업을 시작했지만, C++코드에도 주석을 써놨듯이 Y축을 0으로 만든 이유는 이걸 잡아주기 위해서이다.

2. Y축을 0으로 만든 이유 2번째는 기준점을 맞춰주기 위해 어디서 부터 나이아가라 이펙트가 나갈지 선택을 해줘야하는데 World도 선택하는게 아닌 Player 지점으로부터 사용할 수 있도록 만들어 주기 위함.

 

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapons/CWeaponStructures.h"
#include "NiagaraDataInterfaceExport.h"
#include "CAura.generated.h"

UCLASS()
class U2212_06_API ACAura
	: public AActor
	, public INiagaraParticleCallbackHandler // 인터페이스를 상속 받아준다.
{
	GENERATED_BODY()

private:
	UPROPERTY(EditDefaultsOnly, Category = "Damage")
		FHitData HitData;

	UPROPERTY(EditDefaultsOnly, Category = "Damage")
		float DamageInterval = 0.1f; // 데미지 연속적으로 들어가는 값

private:
	UPROPERTY(VisibleAnywhere)
		class USceneComponent* Root;

	UPROPERTY(VisibleAnywhere)
		class UNiagaraComponent* Niagara;

	UPROPERTY(VisibleAnywhere)
		class UBoxComponent* Box; // 나이아가라 충돌체

public:	
	ACAura();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void OnSystemFinished(class UNiagaraComponent* PSystem);

private:
	// 충돌 이벤트
	UFUNCTION()
		void OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	UFUNCTION()
		void OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

public:
	/*1. 상속받은 부모함수에 들어가보면 하나의 함수가 존재한다.순수가상함수도 아닌 이 함수를 어떠한 방법으로 사용해야 할까 생각을 해봐야 한다.
	  2. 인터페이스는 기본적으로 순수가상함수를 포함한다.virtual를 붙이지 않아도 순수가상함수로 취급을 해준다.
	  3. 순수가상함수는 정의가 없고 선언만 되어 있는 상태를 말한다.NativeEvent가 붙어있는 이유는 내가 정의를 해놓을 테니까 필요하면 가져다가 정의해서 사용하라는 의미이다.
	  4. NativeEvent가 붙어있는걸 사용하려면, Implementation를 붙여서 사용해야하는데 정의가 되어있지도 않다.이유는 선언만 되어있기 때문에 상속받은 자식으로가서 정의해서 사용하라는 의미이다.*/
	// 인터페이스 부모함수 재정의
	void ReceiveParticleData_Implementation(const TArray<FBasicParticleData>& Data, UNiagaraSystem* NiagaraSystem);

private:
	// 히트 목록
	TArray<class ACharacter*> Hitted;
	FTimerHandle TimerHandle;

};
#include "Weapons/AddOns/CAura.h"
#include "Global.h"
#include "NiagaraComponent.h"
#include "GameFramework/Character.h"
#include "Components/BoxComponent.h"

ACAura::ACAura()
{
	PrimaryActorTick.bCanEverTick = true;

	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
	CHelpers::CreateComponent<UNiagaraComponent>(this, &Niagara, "Niagara", Root);
	CHelpers::CreateComponent<UBoxComponent>(this, &Box, "Box", Root);

	UNiagaraSystem* niagara;
	CHelpers::GetAsset<UNiagaraSystem>(&niagara, "NiagaraSystem'/Game/sA_StylizedSwordSet/Fx/NS_Ulti_lv1.NS_Ulti_lv1'");
	Niagara->SetAsset(niagara);

}

void ACAura::BeginPlay()
{
	Super::BeginPlay();

	Niagara->SetNiagaraVariableObject("Mesh_Scale", this); // 빼온 값 사용할 수 있도록 지정.
	Niagara->OnSystemFinished.AddDynamic(this, &ACAura::OnSystemFinished);

	Box->OnComponentBeginOverlap.AddDynamic(this, &ACAura::OnComponentBeginOverlap);
	Box->OnComponentEndOverlap.AddDynamic(this, &ACAura::OnComponentEndOverlap);

	FTimerDelegate delegate = FTimerDelegate::CreateLambda([&]()
		{
			for (int32 i = Hitted.Num() - 1; i >= 0; i--)
				HitData.SendDamage(Cast<ACharacter>(GetOwner()), this, Hitted[i]);
		});

	GetWorld()->GetTimerManager().SetTimer(TimerHandle, delegate, DamageInterval, true, 0);
}

void ACAura::OnSystemFinished(UNiagaraComponent* PSystem)
{
	GetWorld()->GetTimerManager().ClearTimer(TimerHandle); // 끝이나면 Timer 제거

	Destroy();
}

void ACAura::ReceiveParticleData_Implementation(const TArray<FBasicParticleData>& Data, UNiagaraSystem* NiagaraSystem)
{
	INiagaraParticleCallbackHandler::ReceiveParticleData_Implementation(Data, NiagaraSystem);

	// Extent 해놓고 Scale값에 곱해질 값이므로, 0번을 찾아서 넣어준다.
	Box->SetRelativeScale3D(Data[0].Position);

	// Y를 0으로 만든 이유는, 플레이어를 눌러보면 전방방향이 Y축을 향해 있어서 X축으로 변경해주기 위해 회전을 줬었다.
	// 그걸 토대로 Y축으로 발사가 되면 안되므로, 값을 준 것이다.
	FVector location = Box->GetScaledBoxExtent(); // 부피 : Box Extent * 나이아가라 Scale
	location.Y = 0; 

	Box->SetRelativeLocation(location);
}

void ACAura::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	CheckTrue(GetOwner() == OtherActor);

	ACharacter* character = Cast<ACharacter>(OtherActor);
	if (!!character)
		Hitted.AddUnique(character);

}

void ACAura::OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	CheckTrue(GetOwner() == OtherActor);

	ACharacter* character = Cast<ACharacter>(OtherActor);
	if (!!character)
		Hitted.Remove(character);

}

1. 충돌을 하기 위해 연결해준다.

2. Timer를 사용하여 일정시간마다 충돌하도록 만들어 준다.

1. Hit 몽타주를 선택해준다.


https://www.youtube.com/watch?v=IgX_be5bmHo 

 

'언리얼' 카테고리의 다른 글

UE4 SubAction Warp, Top View  (0) 2023.06.30
UE4 SubAction Warp  (0) 2023.06.29
UE4 SubAction Sword Skill Collision, Hammer Init  (0) 2023.06.26
UE4 SubAction Fist, Sword Skill  (0) 2023.06.23
UE4 SubAction Fist, Ghost Trail  (0) 2023.06.22
Comments