지난번에 구현한 주먹 난타 스킬에 충돌이 적용되게 코드를 추가해보자. 마지막 공격에 적이 주먹에 맞아 나가떨어지는 모습을 보이도록 구현해보자. 주먹 스킬 외에 검 스킬도 추가하자. 오늘은 전진하면서 검으로 찌르는 일섬 스킬을 구현해볼 것이다.    

 

 

 


 

 

 
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로 넘어간다.

 

 

※ 참고:

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 생성

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

실행화면