[UE] 주먹 스킬 충돌처리, 전진 찌르기 스킬 구현하기

지난번에 구현한 주먹 난타 스킬에 충돌이 적용되게 코드를 추가해보자. 마지막 공격에 적이 주먹에 맞아 나가떨어지는 모습을 보이도록 구현해보자. 주먹 스킬 외에 검 스킬도 추가하자. 오늘은 전진하면서 검으로 찌르는 일섬 스킬을 구현해볼 것이다.
목차
Plugins |
||||
Weapon |
||||
Resource |
||||
Icon128.png weapon_thumbnail_icon.png |
||||
Source |
||||
Weapon | ||||
SWeaponCheckBoxes.h .cpp SWeaponDetailsView.h .cpp SWeaponDoActionData.h .cpp SWeaponEquipmentData.h .cpp SWeaponHitData.h .cpp SWeaponLeftArea.h .cpp Weapon.Build.cs WeaponAssetEditor.h .cpp WeaponAssetFactory.h .cpp WeaponCommand.h .cpp WeaponContextMenu.h .cpp WeaponModule.h .cpp WeaponStyle.h .cpp |
||||
Source | ||
U2212_06 | ||
Characters | ||
CAnimInstance.h .cpp CEnemy.h .cpp CPlayer.h .cpp ICharacter.h .cpp |
||
Components | ||
CMontagesComponent.h .cpp CMovementComponent.h .cpp CStateComponent.h .cpp CStatusComponent.h .cpp CWeaponComponent.h .cpp |
||
Notifies | ||
CAnimNotifyState_BeginAction.h .cpp CAnimNotify_CameraShake.h .cpp CAnimNotifyState_EndAction.h .cpp CAnimNotify_EndState.h .cpp CAnimNotifyState.h .cpp CAnimNotifyState_CameraAnim.h .cpp CAnimNotifyState_Collision.h .cpp CAnimNotifyState_Combo.h .cpp CAnimNotifyState_Equip.h .cpp CAnimNotifyState_SubAction.h .cpp |
||
Utilities | ||
CHelper.h CLog.h .cpp |
||
Weapons | ||
CCamerModifier.h .cpp CGhostTrail.h .cpp CDoAction_Combo.h .cpp CSubAction_Fist.h .cpp CSubAction_Sword.h .cpp 생성 CAttachment.h .cpp CDoAction.h .cpp CEquipment.h .cpp CSubAction.h .cpp CWeaponAsset.h .cpp CWeaponStructures.h .cpp |
||
Global.h CGameMode.h .cpp U2212_06.Build.cs |
||
U2212_06.uproject | ||
주먹 난타 스킬 충돌처리(SubAction_Fist - Hit)

CAttachment에 충돌처리를 위한 DELEGATE들을 넣었다.
- DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAttachmentBeginCollision);
- DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAttachmentEndCollision);
- DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAttachmentBeginOverlap, class ACharacter*, InAttacker, AActor*, InAttackCauser, class ACharacter*, InOther);
- DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAttachmentEndOverlap, class ACharacter*, InAttacker, class ACharacter*, InOther);
SubAction은 자기 스스로가 HIt Data를 가지고 자기가 충돌했을때 Hit Data를 사용한다.
Collision이 끝났을 때
- FAttachmentEndCollision이 끝났을 때 중복된 객체를 제거해주고 Hit 카운트를 올려준다.
CSubAction_Fist
CSubAction_Fist.h
#pragma once #include "CoreMinimal.h" #include "Weapons/CSubAction.h" #include "Weapons/CWeaponStructures.h" #include "CSubAction_Fist.generated.h" UCLASS(Blueprintable)//블루프린트화해서 설정할 수 있도록 Blueprintable 명시 class U2212_06_API UCSubAction_Fist : public UCSubAction { GENERATED_BODY() public: UPROPERTY(EditAnywhere, Category = "Action") FDoActionData ActionData; UPROPERTY(EditAnywhere, Category = "Action") TArray<FHitData> HitDatas; UPROPERTY(EditAnywhere, Category = "Action") TSubclassOf<class ACGhostTrail> GhostTrailClass; 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* InAttackCauser, class ACharacter* InOther); private: TArray<class ACharacter *> Hitted; int32 HitIndex;//Hit Montage를 카운팅하기 위한 변수 private: class ACGhostTrail* GhostTrail; };
SubAction에서 상속받은 함수 오버라이드
- void Begin_SubAction_Implementation() override;
함수 추가
- UFUNCTION()
void OnAttachmentEndCollision(); - UFUNCTION()
void OnAttachmentBeginOverlap(class ACharacter* InAttacker, AActor* InAttackCauser, class ACharacter* InOther);
CSubAction_Fist.cpp
#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()); CheckTrue(State->IsSubActionMode()); Super::Pressed(); State->SetActionMode(); State->OnSubActionMode(); GhostTrail = CHelpers::Play_GhostTrail(GhostTrailClass, Owner);//GhostTrail 플레이. ActionData.DoAction(Owner); } void UCSubAction_Fist::Begin_SubAction_Implementation() { Super::Begin_SubAction_Implementation(); //DoAction의 충돌 이벤트 연결 제거(=DELEGATE 연결 제거) Attachment->OnAttachmentEndCollision.Remove(DoAction, "OnAttachmentEndCollision"); Attachment->OnAttachmentBeginOverlap.Remove(DoAction, "OnAttachmentBeginOverlap"); //SubAction의 충돌 이벤트 연결(=DELEGATE 연결) Attachment->OnAttachmentEndCollision.AddDynamic(this, &UCSubAction_Fist::OnAttachmentEndCollision); Attachment->OnAttachmentBeginOverlap.AddDynamic(this, &UCSubAction_Fist::OnAttachmentBeginOverlap); } void UCSubAction_Fist::End_SubAction_Implementation() { Super::End_SubAction_Implementation(); //SubAction의 충돌 이벤트 연결 제거(=DELEGATE 연결 제거) Attachment->OnAttachmentEndCollision.Remove(this, "OnAttachmentEndCollision"); Attachment->OnAttachmentBeginOverlap.Remove(this, "OnAttachmentBeginOverlap"); //DoAction의 충돌 이벤트 다시 연결(=DELEGATE 연결) Attachment->OnAttachmentEndCollision.AddDynamic(DoAction, &UCDoAction::OnAttachmentEndCollision); Attachment->OnAttachmentBeginOverlap.AddDynamic(DoAction, &UCDoAction::OnAttachmentBeginOverlap); //원래 상태로 돌려준다. State->SetIdleMode(); State->OffSubActionMode(); Movement->Move(); Movement->DisableFixedCamera(); GhostTrail->Destroy();//GhostTrail를 지워준다. HitIndex = 0;//HitIndex 초기화 } void UCSubAction_Fist::OnAttachmentBeginOverlap(ACharacter* InAttacker, AActor* InAttackCauser, ACharacter* InOther) { CheckNull(InOther); for (ACharacter* character : Hitted) CheckTrue(character == InOther); Hitted.AddUnique(InOther); CheckTrue(HitIndex >= HitDatas.Num()); HitDatas[HitIndex].SendDamage(Owner, InAttackCauser, InOther); } void UCSubAction_Fist::OnAttachmentEndCollision() { Hitted.Empty();//사용이 끝난 Hit Data는 비워준다. HitIndex++;//다음 Hit Montage가 나오도록 인덱스를 카운팅하여 다음 Hit Data로 넘어간다. }
함수 정의
- void UCSubAction_Fist::Begin_SubAction_Implementation()
함수 정의
- void UCSubAction_Fist::OnAttachmentBeginOverlap(ACharacter* InAttacker, AActor* InAttackCauser, ACharacter* InOther)
- void UCSubAction_Fist::OnAttachmentEndCollision()
- Hitted.Empty();
- 사용이 끝난 Hit Data는 비워준다.
- HitIndex++;
- 다음 Hit Data로 넘어간다.
- Hitted.Empty();
※ 참고:
CSubAction_Fist에
CSubAction에 있는void Begin_SubAction_Implementation()와 End_SubAction_Implementation()를 오버라이드하여 사용하였다.

BP_CSubAction_Fist에 Hit Data 넣어주기
BP_CSubAction_Fist


Hit Data 넣어주기
- 주먹 난타 스킬의 주먹과 발차기가 5번이기 때문에 HitData를 5개 넣어준다.
Ghost Trail Class
- BP_CGhostTrail를 넣어준다.
Fist_Skill_Hit_5_Montage
Fist_Skill_Hit_5_Montage

스킬 마지막 Hit Montage로 사용할 몽타주를 생성하고 프레임 마지막 부분에 End State을 추가한다.
State Type은 Hitted로 설정해준다.
Fist_Skill_Montage
Fist_Skill_Montage

노티파이 트랙 추가
- 새로 만든 노티파이 트랙에 Collision을 추가해준다.
실행화면

전진 일섬 스킬 구현하기
WeaponComponent
WeaponComponent.h
#pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "CWeaponComponent.generated.h" UENUM(BlueprintType) enum class EWeaponType : uint8 { Fist, Sword, Hammer, Warp, Around, Bow, Max, }; DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FWeaponTypeChanged, EWeaponType, InPrevType, EWeaponType, InNewType); UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent)) class U2212_06_API UCWeaponComponent : public UActorComponent { GENERATED_BODY() private://DataAsset을 받아온다. UPROPERTY(EditAnywhere, Category = "DataAsset") class UCWeaponAsset* DataAssets[(int32)EWeaponType::Max]; public: //무기 Type이 맞는지 확인해주는 함수들 FORCEINLINE bool IsUnarmedMode() { return Type == EWeaponType::Max; } FORCEINLINE bool IsFistMode() { return Type == EWeaponType::Fist; } FORCEINLINE bool IsSwordMode() { return Type == EWeaponType::Sword; } FORCEINLINE bool IsHammerMode() { return Type == EWeaponType::Hammer; } FORCEINLINE bool IsWarpMode() { return Type == EWeaponType::Warp; } FORCEINLINE bool IsAroundMode() { return Type == EWeaponType::Around; } FORCEINLINE bool IsBowMode() { return Type == EWeaponType::Bow; } public: UCWeaponComponent(); protected: virtual void BeginPlay() override; public: virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; private: bool IsIdleMode();//StateComponent, WeaponComponent 둘 다 같은 레벨이다. 서로 소유할 수 없는 관계이기 때문에 참조만해서 리턴받기 위해 IsIdleMode를 사용한다. public: class ACAttachment* GetAttachment(); class UCEquipment* GetEquipment(); class UCDoAction* GetDoAction(); class UCSubAction* GetSubAction(); public: //무기 세팅 void SetUnarmedMode(); void SetFistMode(); void SetSwordMode(); void SetHammerMode(); void SetWarpMode(); void SetAroundMode(); void SetBowMode(); void DoAction(); void SubAction_Pressed(); void SubAction_Released(); private: void SetMode(EWeaponType InType); void ChangeType(EWeaponType InType); public: //무기가 바뀌었을때 통보해줄 delegate FWeaponTypeChanged OnWeaponTypeChange; private: class ACharacter* OwnerCharacter; EWeaponType Type = EWeaponType::Max; };
Tick을 실행시켜주는 코드
- UCWeaponComponent::UCWeaponComponent() {
PrimaryComponentTick.bCanEverTick = true; }
함수 추가
- virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
WeaponComponent.cpp
#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() { //Tick을 실행시켜주는 코드 PrimaryComponentTick.bCanEverTick = true; } void UCWeaponComponent::BeginPlay() { Super::BeginPlay(); OwnerCharacter = Cast<ACharacter>(GetOwner()); for (int32 i=0; i < (int32)EWeaponType::Max; i++) { if (!!DataAssets[i]) //DataAssets[i]이 있다면(=무기가 할당되어 있다면) DataAssets[i]->BeginPlay(OwnerCharacter);//BeginPla y 시 OwnerCharacter에 Spawn시켜준다. } } void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (!!GetSubAction())//SubAction이 있다면 GetSubAction()->Tick(DeltaTime);//SubAction의 Tick을 콜 해준다. } bool UCWeaponComponent::IsIdleMode() { return CHelpers::GetComponent<UCStateComponent>(OwnerCharacter)->IsIdleMode(); } ACAttachment* UCWeaponComponent::GetAttachment() { CheckTrueResult(IsUnarmedMode(), nullptr); CheckFalseResult(!!DataAssets[(int32)Type], nullptr); return DataAssets[(int32)Type]->GetAttachment(); } UCEquipment* UCWeaponComponent::GetEquipment() { CheckTrueResult(IsUnarmedMode(), nullptr); CheckFalseResult(!!DataAssets[(int32)Type], nullptr); return DataAssets[(int32)Type]->GetEquipment(); } UCDoAction* UCWeaponComponent::GetDoAction() { CheckTrueResult(IsUnarmedMode(), nullptr); CheckFalseResult(!!DataAssets[(int32)Type], nullptr); return DataAssets[(int32)Type]->GetDoAction(); } UCSubAction* UCWeaponComponent::GetSubAction() { CheckTrueResult(IsUnarmedMode(), nullptr); CheckFalseResult(!!DataAssets[(int32)Type], nullptr); return DataAssets[(int32)Type]->GetSubAction(); } void UCWeaponComponent::SetUnarmedMode() { GetEquipment()->Unequip(); ChangeType(EWeaponType::Max); } void UCWeaponComponent::SetFistMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Fist); } void UCWeaponComponent::SetSwordMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Sword); } void UCWeaponComponent::SetHammerMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Hammer); } void UCWeaponComponent::SetWarpMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Warp); } void UCWeaponComponent::SetAroundMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Around); } void UCWeaponComponent::SetBowMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Bow); } void UCWeaponComponent::SetMode(EWeaponType InType) { if (Type == InType) { SetUnarmedMode(); return; } else if (IsUnarmedMode() == false) { GetEquipment()->Unequip(); } if (!!DataAssets[(int32)InType]) { DataAssets[(int32)InType]->GetEquipment()->Equip(); ChangeType(InType); } } void UCWeaponComponent::ChangeType(EWeaponType InType) { EWeaponType prevType = Type; Type = InType; if (OnWeaponTypeChange.IsBound()) OnWeaponTypeChange.Broadcast(prevType, InType); } void UCWeaponComponent::DoAction() { if (!!GetDoAction()) GetDoAction()->DoAction(); } void UCWeaponComponent::SubAction_Pressed() { if (!!GetSubAction()) GetSubAction()->Pressed(); } void UCWeaponComponent::SubAction_Released() { if (!!GetSubAction()) GetSubAction()->Released(); }
Tick 실행
- UCWeaponComponent::UCWeaponComponent() {
PrimaryComponentTick.bCanEverTick = true;
}
함수 정의
- void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
CSubAction_Sword 생성
새 C++ 클래스 - CSubAction - CSubAction_Sword 생성


CSubAction_Sword .h
#pragma once #include "CoreMinimal.h" #include "Weapons/CSubAction.h" #include "Weapons/CWeaponStructures.h" #include "Kismet/KismetSystemLibrary.h" #include "CSubAction_Sword.generated.h" UCLASS(Blueprintable) class U2212_06_API UCSubAction_Sword : public UCSubAction { GENERATED_BODY() private: UPROPERTY(EditDefaultsOnly, Category = "Trace") float Distance = 1000.0;//이동거리 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; };
CSubAction_Sword .cpp
#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() { CheckFalse(State->IsIdleMode()); CheckTrue(State->IsSubActionMode()); Super::Pressed(); 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();//시작 위치 = Owner의 위치 End = Start + Owner->GetActorForwardVector() * Distance;//끝 위치 = 시작 위치 + Owner의 앞 방향으로 (헤더에서 기본값 설정한)Distance 거리 //Draw Debug 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;//동작이 끝났으니 이동하지 못하게 false로 만들어준다. State->SetIdleMode(); State->OffSubActionMode(); Movement->Move(); Movement->DisableFixedCamera(); if (!!GhostTrail) GhostTrail->Destroy();//GhostTrail를 없앤다. } void UCSubAction_Sword::Tick_Implementation(float InDeltaTime) { Super::Tick_Implementation(InDeltaTime); CheckFalse(bMoving); FVector location = Owner->GetActorLocation(); float radius = Owner->GetCapsuleComponent()->GetScaledCapsuleRadius(); //location이 radius 오차값 내에서 End와 같다면 //Owner(여기서는 Player)의 위치가 radius 오차값 내의 End 위치에 도달했다면 if(location.Equals(End, radius)) { bMoving = false;//일섬 공격이 끝났으니 이동하지 못하게 false로 만들어준다. Start = End = Owner->GetActorLocation();//시작과 끝 위치를 현재 Owner의 위치로 설정해준다. 초기화. return; } FVector direction = (End - Start).GetSafeNormal2D(); Owner->AddActorWorldOffset(direction * Speed, true); }
BP_CSubAction_Sword 생성
CSubAction_Sword 기반 블루프린트 클래스 생성 - BP_CSubAction_Sword 생성




코드로 설정한 Trace 데이터가 들어온다.
Action Data를 설정해준다.
Hit Data를 설정해준다.
Ghost Trail Class를 BP_CGhostTrail_Sword로 설정한다.
Sword_Skill_Montage 생성

노트파이 트랙 추가
- SubAction 추가
- Collision 추가
BP_CGhostTrail_Sword 생성

BP_CGhostTrail 복제 - BP_CGhostTrail_Sword 생성

실행화면

'⭐ Unreal Engine > UE RPG Skill' 카테고리의 다른 글
[UE] 해머 스킬 만들기 (0) | 2023.06.27 |
---|---|
[UE] 일섬 스킬 충돌, 해머 스킬 만들기 (0) | 2023.06.26 |
[UE] 잔상효과 구현하기 (0) | 2023.06.22 |
[UE] 카메라 애니메이션 구현하기 (0) | 2023.06.21 |
[UE] 주먹 난타 스킬 구현하기 (0) | 2023.06.20 |
댓글
이 글 공유하기
다른 글
-
[UE] 해머 스킬 만들기
[UE] 해머 스킬 만들기
2023.06.27 -
[UE] 일섬 스킬 충돌, 해머 스킬 만들기
[UE] 일섬 스킬 충돌, 해머 스킬 만들기
2023.06.26 -
[UE] 잔상효과 구현하기
[UE] 잔상효과 구현하기
2023.06.22 -
[UE] 카메라 애니메이션 구현하기
[UE] 카메라 애니메이션 구현하기
2023.06.21
댓글을 사용할 수 없습니다.