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

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

View File

@ -65,6 +65,8 @@ ACombatCharacter::ACombatCharacter()
StateManagerComponent = CreateDefaultSubobject<UStateManagerComponent>(TEXT("StateManagerComponent"));
StateManagerComponent->OnStateBegin.AddUObject(this, &ACombatCharacter::CharacterStateBegin);
StateManagerComponent->OnStateEnd.AddUObject(this, &ACombatCharacter::CharacterStateEnd);
StateManagerComponent->OnActionBegin.AddUObject(this, &ACombatCharacter::CharacterActionBegin);
StateManagerComponent->OnActionEnd.AddUObject(this, &ACombatCharacter::CharacterActionEnd);
Health = 100.f;
PelvisBoneName = TEXT("pelvis");
@ -111,7 +113,7 @@ float ACombatCharacter::TakeDamage(float Damage, FDamageEvent const& DamageEvent
if (CanReceiveHitReaction())
{
StateManagerComponent->SetState(ECharacterState::Disable);
StateManagerComponent->SetCurrentState(ECharacterState::Disable);
//Play Animation
PlayAnimMontage(HitMontage);
@ -126,7 +128,7 @@ void ACombatCharacter::ContinueAttack_Implementation()
{
CombatComponent->SetIsAttackSaved(false);
if (StateManagerComponent->GetCurrentState() == ECharacterState::Attacking)
StateManagerComponent->SetState(ECharacterState::Nothing);
StateManagerComponent->SetCurrentState(ECharacterState::Nothing);
AttackEvent();
}
}
@ -140,7 +142,7 @@ void ACombatCharacter::ResetCombat_Implementation()
{
CombatComponent->ResetAttack();
StateManagerComponent->ResetState();
StateManagerComponent->SetState(ECharacterState::Nothing);
StateManagerComponent->SetCurrentAction(ECharacterAction::Nothing);
}
FRotator ACombatCharacter::GetDesiredRotation_Implementation()
@ -169,7 +171,7 @@ void ACombatCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerIn
{
//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);
//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)
{
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()
{
ABaseWeapon* baseWeapon = CombatComponent->GetMainWeapon();
@ -320,9 +391,9 @@ void ACombatCharacter::ToggleCombatEvent()
return;
if (!CombatComponent->GetCombatEnabled())
PlayAnimMontage(baseWeapon->GetEnterCombat());
PerformAction(ECharacterState::GeneralActionState, ECharacterAction::EnterCombat, 0);
else
PlayAnimMontage(baseWeapon->GetExitCombat());
PerformAction(ECharacterState::GeneralActionState, ECharacterAction::ExitCombat, 0);
}
void ACombatCharacter::AttackEvent()
@ -331,7 +402,7 @@ void ACombatCharacter::AttackEvent()
return;
if (CombatComponent->GetCombatEnabled())
PerformAttack(CombatComponent->GetAttackCount());
PerformAttack(GetDesiredAttackType(), CombatComponent->GetAttackCount());
else
ToggleCombatEvent();
}
@ -342,7 +413,7 @@ void ACombatCharacter::CharacterTakeDamage(float InDamage)
Health = UKismetMathLibrary::Clamp(tmp, 0, Health);
if (Health <= 0)
StateManagerComponent->SetState(ECharacterState::Dead);
StateManagerComponent->SetCurrentState(ECharacterState::Dead);
}
void ACombatCharacter::ApplyHitReactionPhysicsVelocity(float InitialSpeed)
@ -366,25 +437,40 @@ void ACombatCharacter::EnableRagdoll()
GetMesh()->SetAllBodiesBelowPhysicsBlendWeight(PelvisBoneName, 1.f);
}
void ACombatCharacter::PerformAttack(int32 attackIndex)
void ACombatCharacter::PerformAttack(ECharacterAction attackType, int32 attackIndex)
{
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
if (!CurrentWeapon)
return;
if (!CurrentWeapon->GetAttackMontage().IsValidIndex(attackIndex))
return;
UAnimMontage* attackMontage = CurrentWeapon->GetAttackMontage()[attackIndex];
if(!IsValid(attackMontage))
TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(attackType);
int32 attackIdx = attackIndex;
if (montages.Num() <= attackIdx)
attackIdx = 0;
if (!montages.IsValidIndex(attackIdx))
return;
StateManagerComponent->SetState(ECharacterState::Attacking);
PlayAnimMontage(attackMontage);
int idx = attackIndex + 1;
if (idx > CurrentWeapon->GetAttackMontage().Num())
idx = 0;
UAnimMontage* attackMontage = montages[attackIdx];
if (!IsValid(attackMontage))
{
FString debugStr = FString::Printf(TEXT("Index %d Is NOT VALID!!"), attackIdx);
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, debugStr);
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)
@ -394,22 +480,53 @@ void ACombatCharacter::PerformDodge()
if (!CurrentWeapon)
return;
int32 montageIndex = 0;
TArray<UAnimMontage*> montages = CurrentWeapon->GetDodgeMontage();
if (montages.Num() > montageIndex)
TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(ECharacterAction::Dodge);
if (montages.Num() <= montageIndex)
montageIndex = 0;
if (!montages.IsValidIndex(montageIndex))
return;
UAnimMontage* dodgeMontage = CurrentWeapon->GetDodgeMontage()[montageIndex];
UAnimMontage* dodgeMontage = montages[montageIndex];
if (IsValid(dodgeMontage))
{
StateManagerComponent->SetState(ECharacterState::Dodging);
StateManagerComponent->SetCurrentState(ECharacterState::Dodging);
StateManagerComponent->SetCurrentAction(ECharacterAction::Dodge);
PlayAnimMontage(dodgeMontage);
}
else
{
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;
}
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
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
void Jumping(const FInputActionValue& Value);
void Interact(const FInputActionValue& Value);
void ToggleCombat(const FInputActionValue& Value);
void LightAttack(const FInputActionValue& Value);
void Dodge(const FInputActionValue& Value);
private:
private://Delegate
void CharacterStateBegin(ECharacterState CharState);
void CharacterStateEnd(ECharacterState CharState);
void CharacterActionBegin(ECharacterAction CharAction);
void CharacterActionEnd(ECharacterAction CharAction);
private:
void ToggleCombatEvent();
@ -105,17 +108,19 @@ private:
void ApplyHitReactionPhysicsVelocity(float InitialSpeed);
void EnableRagdoll();
void PerformAttack(int32 attackIndex);
private:
void PerformAttack(ECharacterAction attackType, int32 attackIndex);
void PerformDodge();
UFUNCTION(BlueprintCallable) //임시
bool PerformAction(ECharacterState characterState, ECharacterAction characterAction, int32 montageIndex);
void PerformDeath();
private:
bool CanPerformToggleCombat();
bool CanPerformAttack();
bool CanPerformDodge();
bool CanJumping();
bool CanReceiveHitReaction();
ECharacterAction GetDesiredAttackType();
public:
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components", meta=(AllowPrivateAccess="true"))
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)
{
@ -55,4 +55,24 @@ void UStateManagerComponent::ResetState()
bool UStateManagerComponent::IsCurrentStateEqualToAny(TArray<ECharacterState> StatesToCheck)
{
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")
};
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(FStateEnd, ECharacterState);
DECLARE_MULTICAST_DELEGATE_OneParam(FActionBegin, ECharacterAction);
DECLARE_MULTICAST_DELEGATE_OneParam(FActionEnd, ECharacterAction);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class D1_API UStateManagerComponent : public UActorComponent
@ -38,16 +55,22 @@ public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
public:
void SetState(ECharacterState NewState);
void SetCurrentState(ECharacterState NewState);
ECharacterState GetCurrentState();
void ResetState();
bool IsCurrentStateEqualToAny(TArray<ECharacterState> StatesToCheck);
void SetCurrentAction(ECharacterAction NewAction);
ECharacterAction GetCurrentAction();
bool IsCurrentActionEqualToAny(TArray<ECharacterAction> ActionToCheck);
public: //Delegate
FStateBegin OnStateBegin;
FStateEnd OnStateEnd;
FActionBegin OnActionBegin;
FActionEnd OnActionEnd;
private:
ECharacterState CurrentState;
ECharacterAction CurrentAction;
};