diff --git a/Content/CombatSystem/Blueprints/Actor/BP_ToughSword.uasset b/Content/CombatSystem/Blueprints/Actor/BP_ToughSword.uasset index d8ac7fb7..0ba055ff 100644 Binary files a/Content/CombatSystem/Blueprints/Actor/BP_ToughSword.uasset and b/Content/CombatSystem/Blueprints/Actor/BP_ToughSword.uasset differ diff --git a/Content/CombatSystem/Blueprints/BP_CombatCharacter.uasset b/Content/CombatSystem/Blueprints/BP_CombatCharacter.uasset index 4aecd32d..ab8dd0e2 100644 Binary files a/Content/CombatSystem/Blueprints/BP_CombatCharacter.uasset and b/Content/CombatSystem/Blueprints/BP_CombatCharacter.uasset differ diff --git a/Content/__ExternalActors__/Maps/ThirdPersonMap/7/YS/ZFVZV5RGVXFDJDFNO8TVNW.uasset b/Content/__ExternalActors__/Maps/ThirdPersonMap/7/YS/ZFVZV5RGVXFDJDFNO8TVNW.uasset index 7da89d1a..1758e104 100644 Binary files a/Content/__ExternalActors__/Maps/ThirdPersonMap/7/YS/ZFVZV5RGVXFDJDFNO8TVNW.uasset and b/Content/__ExternalActors__/Maps/ThirdPersonMap/7/YS/ZFVZV5RGVXFDJDFNO8TVNW.uasset differ diff --git a/Source/D1/Actor/BaseWeapon.cpp b/Source/D1/Actor/BaseWeapon.cpp index 40b1844d..467781d5 100644 --- a/Source/D1/Actor/BaseWeapon.cpp +++ b/Source/D1/Actor/BaseWeapon.cpp @@ -61,3 +61,42 @@ void ABaseWeapon::SimulateWeaponPhysics() GetItemMesh()->SetCollisionProfileName(TEXT("PhysicsActor"), true); GetItemMesh()->SetSimulatePhysics(true); } + +TArray ABaseWeapon::GetActionMontage(ECharacterAction characterAction) +{ + TArray 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; +} diff --git a/Source/D1/Actor/BaseWeapon.h b/Source/D1/Actor/BaseWeapon.h index 19728a60..5c9605e9 100644 --- a/Source/D1/Actor/BaseWeapon.h +++ b/Source/D1/Actor/BaseWeapon.h @@ -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& GetAttackMontage() { return AttackMontage; } - FORCEINLINE TArray& GetDodgeMontage() { return DodgeMontage; } + TArray GetActionMontage(ECharacterAction characterAction); protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Initialization") @@ -45,7 +43,6 @@ protected: UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components") TObjectPtr CombatComponent; - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components") TObjectPtr CollisionComponent; @@ -54,9 +51,17 @@ protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") TObjectPtr ExitCombat; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") - TArray> AttackMontage; + TArray> LightAttackMontage; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") TArray> DodgeMontage; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") + TArray> HeavyAttackMontage; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") + TArray> ChargedAttackMontage; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") + TArray> FallingAttackMontage; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") + TArray> SprintAttackMontage; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stat") float Damage; diff --git a/Source/D1/CombatCharacter.cpp b/Source/D1/CombatCharacter.cpp index 2bc2c848..c4239375 100644 --- a/Source/D1/CombatCharacter.cpp +++ b/Source/D1/CombatCharacter.cpp @@ -65,6 +65,8 @@ ACombatCharacter::ACombatCharacter() StateManagerComponent = CreateDefaultSubobject(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(); @@ -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 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 montages = CurrentWeapon->GetDodgeMontage(); - if (montages.Num() > montageIndex) + TArray 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 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; +} + diff --git a/Source/D1/CombatCharacter.h b/Source/D1/CombatCharacter.h index e01f8ac4..1c952a22 100644 --- a/Source/D1/CombatCharacter.h +++ b/Source/D1/CombatCharacter.h @@ -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 CombatComponent; diff --git a/Source/D1/Components/StateManagerComponent.cpp b/Source/D1/Components/StateManagerComponent.cpp index 30517cec..b6e0af0b 100644 --- a/Source/D1/Components/StateManagerComponent.cpp +++ b/Source/D1/Components/StateManagerComponent.cpp @@ -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 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 ActionToCheck) +{ + return ActionToCheck.Contains(CurrentAction); } \ No newline at end of file diff --git a/Source/D1/Components/StateManagerComponent.h b/Source/D1/Components/StateManagerComponent.h index fc84abd7..12f952dd 100644 --- a/Source/D1/Components/StateManagerComponent.h +++ b/Source/D1/Components/StateManagerComponent.h @@ -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 StatesToCheck); + void SetCurrentAction(ECharacterAction NewAction); + ECharacterAction GetCurrentAction(); + bool IsCurrentActionEqualToAny(TArray ActionToCheck); + public: //Delegate FStateBegin OnStateBegin; FStateEnd OnStateEnd; + FActionBegin OnActionBegin; + FActionEnd OnActionEnd; private: ECharacterState CurrentState; - + ECharacterAction CurrentAction; };