[박치영] 충돌 및 상태패턴 기능 추가

main
pcyoung 2023-08-06 19:38:31 +09:00 committed by PCYPC\pcy35
parent 79cfdb003a
commit b83ca133ca
9 changed files with 259 additions and 38 deletions

View File

@ -61,3 +61,42 @@ void ABaseWeapon::SimulateWeaponPhysics()
GetItemMesh()->SetCollisionProfileName(TEXT("PhysicsActor"), true); GetItemMesh()->SetCollisionProfileName(TEXT("PhysicsActor"), true);
GetItemMesh()->SetSimulatePhysics(true); GetItemMesh()->SetSimulatePhysics(true);
} }
TArray<UAnimMontage*> ABaseWeapon::GetActionMontage(ECharacterAction characterAction)
{
TArray<UAnimMontage*> outputArr;
switch (characterAction)
{
case ECharacterAction::Nothing:
break;
case ECharacterAction::GeneralAction:
break;
case ECharacterAction::LightAttack:
outputArr = LightAttackMontage;
break;
case ECharacterAction::HeavyAttack:
outputArr = HeavyAttackMontage;
break;
case ECharacterAction::ChargedAttack:
outputArr = ChargedAttackMontage;
break;
case ECharacterAction::FallingAttack:
outputArr = FallingAttackMontage;
break;
case ECharacterAction::SprintAttack:
outputArr = SprintAttackMontage;
break;
case ECharacterAction::Dodge:
outputArr = DodgeMontage;
break;
case ECharacterAction::EnterCombat:
outputArr.Add(EnterCombat);
break;
case ECharacterAction::ExitCombat:
outputArr.Add(ExitCombat);
break;
default:
break;
}
return outputArr;
}

View File

@ -6,6 +6,7 @@
#include "Actor/BaseEquippable.h" #include "Actor/BaseEquippable.h"
#include "Enums/CombatType.h" #include "Enums/CombatType.h"
#include "Animation/CombatAnimInstance.h" #include "Animation/CombatAnimInstance.h"
#include "Components/StateManagerComponent.h"
#include "BaseWeapon.generated.h" #include "BaseWeapon.generated.h"
/** /**
@ -32,10 +33,7 @@ public:
//Normal //Normal
void SimulateWeaponPhysics(); void SimulateWeaponPhysics();
public: public:
FORCEINLINE UAnimMontage* GetEnterCombat() const { return EnterCombat; } TArray<UAnimMontage*> GetActionMontage(ECharacterAction characterAction);
FORCEINLINE UAnimMontage* GetExitCombat() const { return ExitCombat; }
FORCEINLINE TArray<UAnimMontage*>& GetAttackMontage() { return AttackMontage; }
FORCEINLINE TArray<UAnimMontage*>& GetDodgeMontage() { return DodgeMontage; }
protected: protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Initialization") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Initialization")
@ -45,7 +43,6 @@ protected:
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components") UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components")
TObjectPtr<class UCombatComponent> CombatComponent; TObjectPtr<class UCombatComponent> CombatComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components") UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components")
TObjectPtr<class UCollisionComponent> CollisionComponent; TObjectPtr<class UCollisionComponent> CollisionComponent;
@ -54,9 +51,17 @@ protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages")
TObjectPtr<UAnimMontage> ExitCombat; TObjectPtr<UAnimMontage> ExitCombat;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages")
TArray<TObjectPtr<UAnimMontage>> AttackMontage; TArray<TObjectPtr<UAnimMontage>> LightAttackMontage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages")
TArray<TObjectPtr<UAnimMontage>> DodgeMontage; TArray<TObjectPtr<UAnimMontage>> DodgeMontage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages")
TArray<TObjectPtr<UAnimMontage>> HeavyAttackMontage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages")
TArray<TObjectPtr<UAnimMontage>> ChargedAttackMontage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages")
TArray<TObjectPtr<UAnimMontage>> FallingAttackMontage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages")
TArray<TObjectPtr<UAnimMontage>> SprintAttackMontage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stat") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stat")
float Damage; float Damage;

View File

@ -65,6 +65,8 @@ ACombatCharacter::ACombatCharacter()
StateManagerComponent = CreateDefaultSubobject<UStateManagerComponent>(TEXT("StateManagerComponent")); StateManagerComponent = CreateDefaultSubobject<UStateManagerComponent>(TEXT("StateManagerComponent"));
StateManagerComponent->OnStateBegin.AddUObject(this, &ACombatCharacter::CharacterStateBegin); StateManagerComponent->OnStateBegin.AddUObject(this, &ACombatCharacter::CharacterStateBegin);
StateManagerComponent->OnStateEnd.AddUObject(this, &ACombatCharacter::CharacterStateEnd); StateManagerComponent->OnStateEnd.AddUObject(this, &ACombatCharacter::CharacterStateEnd);
StateManagerComponent->OnActionBegin.AddUObject(this, &ACombatCharacter::CharacterActionBegin);
StateManagerComponent->OnActionEnd.AddUObject(this, &ACombatCharacter::CharacterActionEnd);
Health = 100.f; Health = 100.f;
PelvisBoneName = TEXT("pelvis"); PelvisBoneName = TEXT("pelvis");
@ -111,7 +113,7 @@ float ACombatCharacter::TakeDamage(float Damage, FDamageEvent const& DamageEvent
if (CanReceiveHitReaction()) if (CanReceiveHitReaction())
{ {
StateManagerComponent->SetState(ECharacterState::Disable); StateManagerComponent->SetCurrentState(ECharacterState::Disable);
//Play Animation //Play Animation
PlayAnimMontage(HitMontage); PlayAnimMontage(HitMontage);
@ -126,7 +128,7 @@ void ACombatCharacter::ContinueAttack_Implementation()
{ {
CombatComponent->SetIsAttackSaved(false); CombatComponent->SetIsAttackSaved(false);
if (StateManagerComponent->GetCurrentState() == ECharacterState::Attacking) if (StateManagerComponent->GetCurrentState() == ECharacterState::Attacking)
StateManagerComponent->SetState(ECharacterState::Nothing); StateManagerComponent->SetCurrentState(ECharacterState::Nothing);
AttackEvent(); AttackEvent();
} }
} }
@ -140,7 +142,7 @@ void ACombatCharacter::ResetCombat_Implementation()
{ {
CombatComponent->ResetAttack(); CombatComponent->ResetAttack();
StateManagerComponent->ResetState(); StateManagerComponent->ResetState();
StateManagerComponent->SetState(ECharacterState::Nothing); StateManagerComponent->SetCurrentAction(ECharacterAction::Nothing);
} }
FRotator ACombatCharacter::GetDesiredRotation_Implementation() FRotator ACombatCharacter::GetDesiredRotation_Implementation()
@ -169,7 +171,7 @@ void ACombatCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerIn
{ {
//Jumping //Jumping
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump); EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACombatCharacter::Jumping);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping); EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
//Moving //Moving
@ -228,6 +230,17 @@ void ACombatCharacter::Look(const FInputActionValue& Value)
} }
} }
void ACombatCharacter::Jumping(const FInputActionValue& Value)
{
if (!CanJumping())
return;
StopAnimMontage();
StateManagerComponent->ResetState();
CombatComponent->ResetAttack();
Super::Jump();
}
void ACombatCharacter::Interact(const FInputActionValue& Value) void ACombatCharacter::Interact(const FInputActionValue& Value)
{ {
bool bInput = Value.Get<bool>(); bool bInput = Value.Get<bool>();
@ -310,6 +323,64 @@ void ACombatCharacter::CharacterStateEnd(ECharacterState CharState)
} }
} }
void ACombatCharacter::CharacterActionBegin(ECharacterAction CharAction)
{
switch (CharAction)
{
case ECharacterAction::Nothing:
break;
case ECharacterAction::GeneralAction:
break;
case ECharacterAction::LightAttack:
break;
case ECharacterAction::HeavyAttack:
break;
case ECharacterAction::ChargedAttack:
break;
case ECharacterAction::FallingAttack:
break;
case ECharacterAction::SprintAttack:
break;
case ECharacterAction::Dodge:
break;
case ECharacterAction::EnterCombat:
break;
case ECharacterAction::ExitCombat:
break;
default:
break;
}
}
void ACombatCharacter::CharacterActionEnd(ECharacterAction CharAction)
{
switch (CharAction)
{
case ECharacterAction::Nothing:
break;
case ECharacterAction::GeneralAction:
break;
case ECharacterAction::LightAttack:
break;
case ECharacterAction::HeavyAttack:
break;
case ECharacterAction::ChargedAttack:
break;
case ECharacterAction::FallingAttack:
break;
case ECharacterAction::SprintAttack:
break;
case ECharacterAction::Dodge:
break;
case ECharacterAction::EnterCombat:
break;
case ECharacterAction::ExitCombat:
break;
default:
break;
}
}
void ACombatCharacter::ToggleCombatEvent() void ACombatCharacter::ToggleCombatEvent()
{ {
ABaseWeapon* baseWeapon = CombatComponent->GetMainWeapon(); ABaseWeapon* baseWeapon = CombatComponent->GetMainWeapon();
@ -320,9 +391,9 @@ void ACombatCharacter::ToggleCombatEvent()
return; return;
if (!CombatComponent->GetCombatEnabled()) if (!CombatComponent->GetCombatEnabled())
PlayAnimMontage(baseWeapon->GetEnterCombat()); PerformAction(ECharacterState::GeneralActionState, ECharacterAction::EnterCombat, 0);
else else
PlayAnimMontage(baseWeapon->GetExitCombat()); PerformAction(ECharacterState::GeneralActionState, ECharacterAction::ExitCombat, 0);
} }
void ACombatCharacter::AttackEvent() void ACombatCharacter::AttackEvent()
@ -331,7 +402,7 @@ void ACombatCharacter::AttackEvent()
return; return;
if (CombatComponent->GetCombatEnabled()) if (CombatComponent->GetCombatEnabled())
PerformAttack(CombatComponent->GetAttackCount()); PerformAttack(GetDesiredAttackType(), CombatComponent->GetAttackCount());
else else
ToggleCombatEvent(); ToggleCombatEvent();
} }
@ -342,7 +413,7 @@ void ACombatCharacter::CharacterTakeDamage(float InDamage)
Health = UKismetMathLibrary::Clamp(tmp, 0, Health); Health = UKismetMathLibrary::Clamp(tmp, 0, Health);
if (Health <= 0) if (Health <= 0)
StateManagerComponent->SetState(ECharacterState::Dead); StateManagerComponent->SetCurrentState(ECharacterState::Dead);
} }
void ACombatCharacter::ApplyHitReactionPhysicsVelocity(float InitialSpeed) void ACombatCharacter::ApplyHitReactionPhysicsVelocity(float InitialSpeed)
@ -366,25 +437,40 @@ void ACombatCharacter::EnableRagdoll()
GetMesh()->SetAllBodiesBelowPhysicsBlendWeight(PelvisBoneName, 1.f); GetMesh()->SetAllBodiesBelowPhysicsBlendWeight(PelvisBoneName, 1.f);
} }
void ACombatCharacter::PerformAttack(int32 attackIndex) void ACombatCharacter::PerformAttack(ECharacterAction attackType, int32 attackIndex)
{ {
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon(); ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
if (!CurrentWeapon) if (!CurrentWeapon)
return; return;
if (!CurrentWeapon->GetAttackMontage().IsValidIndex(attackIndex))
return; TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(attackType);
UAnimMontage* attackMontage = CurrentWeapon->GetAttackMontage()[attackIndex]; int32 attackIdx = attackIndex;
if(!IsValid(attackMontage))
if (montages.Num() <= attackIdx)
attackIdx = 0;
if (!montages.IsValidIndex(attackIdx))
return; return;
StateManagerComponent->SetState(ECharacterState::Attacking); UAnimMontage* attackMontage = montages[attackIdx];
PlayAnimMontage(attackMontage); if (!IsValid(attackMontage))
{
int idx = attackIndex + 1; FString debugStr = FString::Printf(TEXT("Index %d Is NOT VALID!!"), attackIdx);
if (idx > CurrentWeapon->GetAttackMontage().Num()) GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, debugStr);
idx = 0; return;
}
else
{
StateManagerComponent->SetCurrentState(ECharacterState::Attacking);
StateManagerComponent->SetCurrentAction(attackType);
CombatComponent->SetAttackCount(idx); PlayAnimMontage(attackMontage);
int idx = attackIdx + 1;
if (idx > montages.Num())
idx = 0;
CombatComponent->SetAttackCount(idx);
}
} }
//인자값은 나중에 필요하면 추가 PerformDodge(int32 dodgeIndex) //인자값은 나중에 필요하면 추가 PerformDodge(int32 dodgeIndex)
@ -394,22 +480,53 @@ void ACombatCharacter::PerformDodge()
if (!CurrentWeapon) if (!CurrentWeapon)
return; return;
int32 montageIndex = 0; int32 montageIndex = 0;
TArray<UAnimMontage*> montages = CurrentWeapon->GetDodgeMontage(); TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(ECharacterAction::Dodge);
if (montages.Num() > montageIndex) if (montages.Num() <= montageIndex)
montageIndex = 0; montageIndex = 0;
if (!montages.IsValidIndex(montageIndex)) if (!montages.IsValidIndex(montageIndex))
return; return;
UAnimMontage* dodgeMontage = CurrentWeapon->GetDodgeMontage()[montageIndex]; UAnimMontage* dodgeMontage = montages[montageIndex];
if (IsValid(dodgeMontage)) if (IsValid(dodgeMontage))
{ {
StateManagerComponent->SetState(ECharacterState::Dodging); StateManagerComponent->SetCurrentState(ECharacterState::Dodging);
StateManagerComponent->SetCurrentAction(ECharacterAction::Dodge);
PlayAnimMontage(dodgeMontage); PlayAnimMontage(dodgeMontage);
} }
else else
{ {
FString str = FString::Printf(TEXT("Dodge Index %n is NOT VALID!!"), montageIndex); FString str = FString::Printf(TEXT("Dodge Index %n is NOT VALID!!"), montageIndex);
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Blue, str); GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str);
}
}
bool ACombatCharacter::PerformAction(ECharacterState characterState, ECharacterAction characterAction, int32 montageIndex)
{
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
if (!CurrentWeapon)
return false;
TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(characterAction);
int32 montageIdx = montageIndex;
if (montages.Num() <= montageIdx)
montageIdx = 0;
if (!montages.IsValidIndex(montageIdx))
return false;
UAnimMontage* actionMontage = montages[montageIdx];
if (IsValid(actionMontage))
{
StateManagerComponent->SetCurrentState(characterState);
StateManagerComponent->SetCurrentAction(characterAction);
PlayAnimMontage(actionMontage);
return true;
}
else
{
FString str = FString::Printf(TEXT("Dodge Index %n is NOT VALID!!"), montageIdx);
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str);
return false;
} }
} }
@ -498,5 +615,17 @@ bool ACombatCharacter::CanReceiveHitReaction()
return ReturnValue; return ReturnValue;
} }
ECharacterAction ACombatCharacter::GetDesiredAttackType()
{
if (GetCharacterMovement()->IsFalling())
return ECharacterAction::FallingAttack;
//TODO : Movement Speed Mode Ãß°¡
//TODO : Heavy Attack Ãß°¡
return ECharacterAction::LightAttack;
}

View File

@ -89,14 +89,17 @@ protected:
//Input Funcs //Input Funcs
void Move(const FInputActionValue& Value); void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value); void Look(const FInputActionValue& Value);
void Jumping(const FInputActionValue& Value);
void Interact(const FInputActionValue& Value); void Interact(const FInputActionValue& Value);
void ToggleCombat(const FInputActionValue& Value); void ToggleCombat(const FInputActionValue& Value);
void LightAttack(const FInputActionValue& Value); void LightAttack(const FInputActionValue& Value);
void Dodge(const FInputActionValue& Value); void Dodge(const FInputActionValue& Value);
private: private://Delegate
void CharacterStateBegin(ECharacterState CharState); void CharacterStateBegin(ECharacterState CharState);
void CharacterStateEnd(ECharacterState CharState); void CharacterStateEnd(ECharacterState CharState);
void CharacterActionBegin(ECharacterAction CharAction);
void CharacterActionEnd(ECharacterAction CharAction);
private: private:
void ToggleCombatEvent(); void ToggleCombatEvent();
@ -105,17 +108,19 @@ private:
void ApplyHitReactionPhysicsVelocity(float InitialSpeed); void ApplyHitReactionPhysicsVelocity(float InitialSpeed);
void EnableRagdoll(); void EnableRagdoll();
void PerformAttack(int32 attackIndex); private:
void PerformAttack(ECharacterAction attackType, int32 attackIndex);
void PerformDodge(); void PerformDodge();
bool PerformAction(ECharacterState characterState, ECharacterAction characterAction, int32 montageIndex);
UFUNCTION(BlueprintCallable) //임시
void PerformDeath(); void PerformDeath();
private:
bool CanPerformToggleCombat(); bool CanPerformToggleCombat();
bool CanPerformAttack(); bool CanPerformAttack();
bool CanPerformDodge(); bool CanPerformDodge();
bool CanJumping(); bool CanJumping();
bool CanReceiveHitReaction(); bool CanReceiveHitReaction();
ECharacterAction GetDesiredAttackType();
public: public:
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components", meta=(AllowPrivateAccess="true")) UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components", meta=(AllowPrivateAccess="true"))
TObjectPtr<class UCombatComponent> CombatComponent; TObjectPtr<class UCombatComponent> CombatComponent;

View File

@ -32,7 +32,7 @@ void UStateManagerComponent::TickComponent(float DeltaTime, ELevelTick TickType,
// ... // ...
} }
void UStateManagerComponent::SetState(ECharacterState NewState) void UStateManagerComponent::SetCurrentState(ECharacterState NewState)
{ {
if (NewState != CurrentState) if (NewState != CurrentState)
{ {
@ -55,4 +55,24 @@ void UStateManagerComponent::ResetState()
bool UStateManagerComponent::IsCurrentStateEqualToAny(TArray<ECharacterState> StatesToCheck) bool UStateManagerComponent::IsCurrentStateEqualToAny(TArray<ECharacterState> StatesToCheck)
{ {
return StatesToCheck.Contains(CurrentState); return StatesToCheck.Contains(CurrentState);
}
void UStateManagerComponent::SetCurrentAction(ECharacterAction NewAction)
{
if (CurrentAction != NewAction)
{
OnActionEnd.Broadcast(CurrentAction);
CurrentAction = NewAction;
OnActionBegin.Broadcast(CurrentAction);
}
}
ECharacterAction UStateManagerComponent::GetCurrentAction()
{
return CurrentAction;
}
bool UStateManagerComponent::IsCurrentActionEqualToAny(TArray<ECharacterAction> ActionToCheck)
{
return ActionToCheck.Contains(CurrentAction);
} }

View File

@ -17,8 +17,25 @@ enum class ECharacterState : uint8
Disable UMETA(DisplayName = "Disable") Disable UMETA(DisplayName = "Disable")
}; };
UENUM(BlueprintType)
enum class ECharacterAction : uint8
{
Nothing UMETA(DisplayName = "Nothing"),
GeneralAction UMETA(DisplayName = "GeneralAction"),
LightAttack UMETA(DisplayName = "LightAttack"),
HeavyAttack UMETA(DisplayName = "HeavyAttack"),
ChargedAttack UMETA(DisplayName = "ChargedAttack"),
FallingAttack UMETA(DisplayName = "FallingAttack"),
SprintAttack UMETA(DisplayName = "SprintAttack"),
Dodge UMETA(DisplayName = "Dodge"),
EnterCombat UMETA(DisplayName = "EnterCombat"),
ExitCombat UMETA(DisplayName = "ExitCombat"),
};
DECLARE_MULTICAST_DELEGATE_OneParam(FStateBegin, ECharacterState); DECLARE_MULTICAST_DELEGATE_OneParam(FStateBegin, ECharacterState);
DECLARE_MULTICAST_DELEGATE_OneParam(FStateEnd, ECharacterState); DECLARE_MULTICAST_DELEGATE_OneParam(FStateEnd, ECharacterState);
DECLARE_MULTICAST_DELEGATE_OneParam(FActionBegin, ECharacterAction);
DECLARE_MULTICAST_DELEGATE_OneParam(FActionEnd, ECharacterAction);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class D1_API UStateManagerComponent : public UActorComponent class D1_API UStateManagerComponent : public UActorComponent
@ -38,16 +55,22 @@ public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
public: public:
void SetState(ECharacterState NewState); void SetCurrentState(ECharacterState NewState);
ECharacterState GetCurrentState(); ECharacterState GetCurrentState();
void ResetState(); void ResetState();
bool IsCurrentStateEqualToAny(TArray<ECharacterState> StatesToCheck); bool IsCurrentStateEqualToAny(TArray<ECharacterState> StatesToCheck);
void SetCurrentAction(ECharacterAction NewAction);
ECharacterAction GetCurrentAction();
bool IsCurrentActionEqualToAny(TArray<ECharacterAction> ActionToCheck);
public: //Delegate public: //Delegate
FStateBegin OnStateBegin; FStateBegin OnStateBegin;
FStateEnd OnStateEnd; FStateEnd OnStateEnd;
FActionBegin OnActionBegin;
FActionEnd OnActionEnd;
private: private:
ECharacterState CurrentState; ECharacterState CurrentState;
ECharacterAction CurrentAction;
}; };