초보 코린이의 성장 일지

UE4 SubAction Fist, Sword Skill 본문

언리얼

UE4 SubAction Fist, Sword Skill

코오린이 2023. 6. 23. 14:36

충돌처리를 할때 Attach에서 무기를 붙임과 동시에 충돌 처리 델리게이션 이벤트를 넣어놓았다 .

그러면서 SubAction에서 HitData를 가지고 판단하여 처리를 하게된다.

DoAction에서는 부모에 함수를 오버라이딩해서 연결을 통해 사용할 곳에서 호출하도록 설계가 되어있다.

 

#pragma once

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

UCLASS(Blueprintable) // BP가 될 수 있도록, 
class U2212_06_API UCSubAction_Fist : public UCSubAction
{
	GENERATED_BODY()

public:
	// 키가 눌렸을때, 오버라이딩 해준다.
	void Pressed() override;
    
	void Begin_SubAction_Implementation() override;
	void End_SubAction_Implementation() override;

private:
	UFUNCTION()
		void OnAttachmentEndCollision();

	UFUNCTION()
		void OnAttachmentBeginOverlap(class ACharacter* InAttacker, AActor* InAttackCuaser, class ACharacter* InOther);

};
#include "Weapons/SubActions/CSubAction_Fist.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_Fist::Pressed()
{
	// 다른 액션이 애니메이션 플레이중에 개입되지 못하도록 조건 처리.
	CheckFalse(State->IsIdleMode());
	// IsSubActionMode가 이미 켜져있으면 들어가면 안된다.
	CheckTrue(State->IsSubActionMode());

	Super::Pressed(); // 부모에 아무런 기능도 없지만 혹시 모르니 콜해준다.

	// 다른 Action이 개입 못하도록 켜준다.
	State->SetActionMode();
	State->OnSubActionMode();

	GhostTrail = CHelpers::Play_GhostTrail(GhostTrailClass, Owner); // 지정한 시간뒤에 플레이 될 것이다.

	// 액션 데이터 받기
	ActionData.DoAction(Owner);

}

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

	// DoAction에 연결되어 있는 이벤트 제거해주지 않으면, 모든 Hit가 첫번째 타격 데미지로 들어온다.
	Attachment->OnAttachmentEndCollision.Remove(DoAction, "OnAttachmentEndCollision");
	Attachment->OnAttachmentBeginOverlap.Remove(DoAction, "OnAttachmentBeginOverlap");

	// 액션이 시작될때, 충돌 이벤트 연결
	Attachment->OnAttachmentEndCollision.AddDynamic(this, &UCSubAction_Fist::OnAttachmentEndCollision);
	Attachment->OnAttachmentBeginOverlap.AddDynamic(this, &UCSubAction_Fist::OnAttachmentBeginOverlap);


}

void UCSubAction_Fist::End_SubAction_Implementation()
{
	Super::End_SubAction_Implementation();

	// 액션이 끝나면 연결되어있는 이벤트를 제거해준다.
	Attachment->OnAttachmentEndCollision.Remove(this, "OnAttachmentEndCollision");
	Attachment->OnAttachmentBeginOverlap.Remove(this, "OnAttachmentBeginOverlap");

	// 다시 끝날때 위에서 제거해준 DoAction 다시 연결
	Attachment->OnAttachmentEndCollision.AddDynamic(DoAction, &UCDoAction::OnAttachmentEndCollision);
	Attachment->OnAttachmentBeginOverlap.AddDynamic(DoAction, &UCDoAction::OnAttachmentBeginOverlap);

	// 원래 상태로 다시 돌려주기
	State->SetIdleMode();
	State->OffSubActionMode();

	Movement->Move();
	Movement->DisableFixedCamera();

	GhostTrail->Destroy();

	HitIndex = 0; // 모든 액션이 끝나면 인덱스 초기화

}

void UCSubAction_Fist::OnAttachmentBeginOverlap(ACharacter* InAttacker, AActor* InAttackCuaser, ACharacter* InOther)
{
	CheckNull(InOther);

	for (ACharacter* character : Hitted)
		CheckTrue(character == InOther);

	// 중복 제거
	Hitted.AddUnique(InOther);

	CheckTrue(HitIndex >= HitDatas.Num()); // Index가 HitDatas 보다 크면 안된다.
	HitDatas[HitIndex].SendDamage(Owner, InAttackCuaser, InOther); // 데미지 전달

}

void UCSubAction_Fist::OnAttachmentEndCollision()
{
	Hitted.Empty(); // Collision이 끝날떄마다 Hitted을 제거, 그래야 다시 들어올 수 있다.

	HitIndex++; // 충돌 구간이 끝나면 다음 공격이 되어야 하므로 증가
}

1. 여기서 중요한점은 Hit가 되면 다시 제거해주고 다시 맞을때 Hit가 다시 들어오는 방법으로 시작과 끝을 자유자제로 컨트롤 할 수 있게된다.

1. 각 구간에 Collision을 넣어주고, SubAction 구간안에 넣어줘야한다. 그 이유는 SubAction이 종료가 먼저 되어버리면 Collision을 종료시킬수 가 없다. 

1. Fist에 스킬 타격이 총5대 이므로, Hit 5개를 추가해준다.

2. 마지막 Hit에 Launch를 줘서 날라가게끔 만들어 준다.

1. 마지막 Hit시 Launch만큼 날아가는걸 볼 수 있다.


#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CWeaponComponent.generated.h"

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class U2212_06_API UCWeaponComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

};
#include "Components/CWeaponComponent.h"
#include "Global.h"
#include "CStateComponent.h"
#include "GameFramework/Character.h"
#include "Weapons/CWeaponAsset.h"
#include "Weapons/CAttachment.h"
#include "Weapons/CEquipment.h"
#include "Weapons/CDoAction.h"
#include "Weapons/CSubAction.h"

UCWeaponComponent::UCWeaponComponent()
{
	PrimaryComponentTick.bCanEverTick = true;

}

void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// SubAction 있다면 콜해주기.
	if (!!GetSubAction())
		GetSubAction()->Tick(DeltaTime);
}

1. Sword에서 만들 스킬은 일자로 나가게 되는 스킬을 구현하게 될 것이므로, 이동하는 구간을 Tick로 처리할것이기 때문에 Tick을 사용할 수 있도록 WeaponComponent에 정의해준다.


1. SubAction을 상속받아서 Sword Skill을 위한 클래스를 생성해준다.

#pragma once

#include "CoreMinimal.h"
#include "Weapons/CSubAction.h"
#include "Weapons/CWeaponStructures.h"
#include "Kismet/KismetSystemLibrary.h"
#include "CSubAction_Sword.generated.h"

UCLASS(Blueprintable) // BP화 시켜주기.
class U2212_06_API UCSubAction_Sword : public UCSubAction
{
	GENERATED_BODY()

private:
    UPROPERTY(EditDefaultsOnly, Category = "Trace")
        float Distance = 1000;

    UPROPERTY(EditDefaultsOnly, Category = "Trace")
        float Speed = 200;

    UPROPERTY(EditDefaultsOnly, Category = "Trace")
        TEnumAsByte<EDrawDebugTrace::Type> DrawDebug; // 디버그 확인용

private:
    UPROPERTY(EditDefaultsOnly, Category = "Action")
        FDoActionData ActionData;

    UPROPERTY(EditDefaultsOnly, Category = "Action")
        FHitData HitData;

private:
    UPROPERTY(EditAnywhere, Category = "Add-On")
        TSubclassOf<class ACGhostTrail> GhostTrailClass;

public:
    void Pressed() override;
    void Begin_SubAction_Implementation() override;
    void End_SubAction_Implementation() override;
    void Tick_Implementation(float InDeltaTime) override;

private:
    bool bMoving;

    FVector Start; // 시작지점
    FVector End; // 끝지점

private:
    class ACGhostTrail* GhostTrail;

};
#include "Weapons/SubActions/CSubAction_Sword.h"
#include "Global.h"
#include "Weapons/CAttachment.h"
#include "Weapons/CDoAction.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
#include "Components/CStateComponent.h"
#include "Components/CMovementComponent.h"
#include "Components/CapsuleComponent.h"
#include "Weapons/AddOns/CGhostTrail.h"

void UCSubAction_Sword::Pressed()
{
	Super::Pressed();

	CheckFalse(State->IsIdleMode());
	CheckTrue(State->IsSubActionMode());

	State->SetActionMode();
	State->OnSubActionMode();

	GhostTrail = CHelpers::Play_GhostTrail(GhostTrailClass, Owner);

	ActionData.DoAction(Owner);

}

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

}

void UCSubAction_Sword::End_SubAction_Implementation()
{
	Super::End_SubAction_Implementation();

	bMoving = false;

	State->SetIdleMode();
	State->OffSubActionMode();

	Movement->Move();
	Movement->DisableFixedCamera();

	if (!!GhostTrail)
		GhostTrail->Destroy();

}

void UCSubAction_Sword::Tick_Implementation(float InDeltaTime)
{

}

1. 우선 에디터안에서 BP로 만들어줘야 하기 때문에 Tick는 놔둔 상태로 넘어간다.

1. 클래스로 생성해준다.

1. 사용할 몽타주에 노티파이를 넣어준다.

1. 사용할 몽타주와 Hit 몽타주를 넣어준다.

1. Ghost Trail도 사용할 것이므로, 만들어 놓은 Fist Ghost를 복사해서 가져와서 값을 변경해준다.

 

1. Sword DataAsset로가서 SubActionClass를 선택해준다.

1. 찌르기 모션이 나오게된다. 움직임은 아직 제자리이므로 Tick에서 작업을 진행할 것이다.

#include "Weapons/SubActions/CSubAction_Sword.h"
#include "Global.h"
#include "Weapons/CAttachment.h"
#include "Weapons/CDoAction.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
#include "Components/CStateComponent.h"
#include "Components/CMovementComponent.h"
#include "Components/CapsuleComponent.h"
#include "Weapons/AddOns/CGhostTrail.h"

void UCSubAction_Sword::Pressed()
{
	Super::Pressed();

	CheckFalse(State->IsIdleMode());
	CheckTrue(State->IsSubActionMode());

	State->SetActionMode();
	State->OnSubActionMode();

	GhostTrail = CHelpers::Play_GhostTrail(GhostTrailClass, Owner);

	ActionData.DoAction(Owner);

}

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

	bMoving = true;

	Start = Owner->GetActorLocation();
	End = Start + Owner->GetActorForwardVector() * Distance; // 이동할 거리

	// 나가는 방향 Draw로 확인
	if (DrawDebug == EDrawDebugTrace::ForDuration)
		DrawDebugDirectionalArrow(Owner->GetWorld(), Start, End, 25, FColor::Green, true, 5, 0, 3);
}

void UCSubAction_Sword::End_SubAction_Implementation()
{
	// 액션 끝나면 다 돌려주기.
	Super::End_SubAction_Implementation();

	bMoving = false;

	State->SetIdleMode();
	State->OffSubActionMode();

	Movement->Move();
	Movement->DisableFixedCamera();

	if (!!GhostTrail)
		GhostTrail->Destroy();

}

void UCSubAction_Sword::Tick_Implementation(float InDeltaTime)
{
	Super::Tick_Implementation(InDeltaTime);
	CheckFalse(bMoving); // 무빙이 false면 실행 x

	// 이동 후 멈춰야할 지점 정하기.
	FVector location = Owner->GetActorLocation();
	float radius = Owner->GetCapsuleComponent()->GetScaledCapsuleRadius(); // Capsule 절반만큼 멈추게 설정,

	// 뒤에 오차값 매개변수를 radius로 설정 (오차값만큼 들어오면 같은 값으로 간주하라는 의미)
	if (location.Equals(End, radius)) 
	{
		bMoving = false;
		Start = End = Owner->GetActorLocation(); // 최종위치로 다시 잡아준다. 초기화도 하는겸

		return;
	}

	// 방향, 속도만큼 이동, AddActorWorldOffset를 들어가서 보면 현재 속도에서 계속 값을 더 해가는 역할을 수행한다.
	FVector direction = (End - Start).GetSafeNormal2D();
	Owner->AddActorWorldOffset(direction * Speed, true);

}

1. Tick에 값을 누적시켜 이동하도록 설정해주고, 정했던 거리보다 더 멀리 날아가는걸 방지하기 위해 제한도 걸어준다.

2. 스킬이 완성되었다.


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

 

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

UE4 SubAction Hammer Skill  (0) 2023.06.27
UE4 SubAction Sword Skill Collision, Hammer Init  (0) 2023.06.26
UE4 SubAction Fist, Ghost Trail  (0) 2023.06.22
UE4 SubAction Fist, Camera Move  (0) 2023.06.21
UE4 SubAction Fist  (0) 2023.06.20
Comments