diff --git a/Content/CombatSystem/Blueprints/AI/BT_BossEnemy.uasset b/Content/CombatSystem/Blueprints/AI/BT_BossEnemy.uasset index 22bd1560..c4425ebe 100644 Binary files a/Content/CombatSystem/Blueprints/AI/BT_BossEnemy.uasset and b/Content/CombatSystem/Blueprints/AI/BT_BossEnemy.uasset differ diff --git a/Content/CombatSystem/Blueprints/AI/BT_MobEnemy.uasset b/Content/CombatSystem/Blueprints/AI/BT_MobEnemy.uasset index b40461c3..88ae2d53 100644 Binary files a/Content/CombatSystem/Blueprints/AI/BT_MobEnemy.uasset and b/Content/CombatSystem/Blueprints/AI/BT_MobEnemy.uasset differ diff --git a/Content/CombatSystem/Blueprints/BP_CombatCharacter.uasset b/Content/CombatSystem/Blueprints/BP_CombatCharacter.uasset index 1f2aeae1..a4f8ee5b 100644 Binary files a/Content/CombatSystem/Blueprints/BP_CombatCharacter.uasset and b/Content/CombatSystem/Blueprints/BP_CombatCharacter.uasset differ diff --git a/Content/CombatSystem/Blueprints/Timeline/RotateToTargetCurve.uasset b/Content/CombatSystem/Blueprints/Timeline/RotateToTargetCurve.uasset new file mode 100644 index 00000000..53332ff8 Binary files /dev/null and b/Content/CombatSystem/Blueprints/Timeline/RotateToTargetCurve.uasset differ diff --git a/Source/D1/AI/BossEnemy.cpp b/Source/D1/AI/BossEnemy.cpp index 8eab5019..85712622 100644 --- a/Source/D1/AI/BossEnemy.cpp +++ b/Source/D1/AI/BossEnemy.cpp @@ -7,6 +7,7 @@ #include "Components/CollisionComponent.h" #include "Components/WidgetComponent.h" #include "Components/CombatComponent.h" +#include "Components/StateManagerComponent.h" #include "DamageType/AttackDamageType.h" #include "Kismet/GameplayStatics.h" #include "UI/UI_HealthBar.h" @@ -108,7 +109,8 @@ void ABossEnemy::SetBossHealthVisibility(bool IsVisible) if(!IsValid(BossHealthWidget)) return; - if(IsVisible) + FGameplayTagContainer GameplayTagContainer(FCombatGameplayTags::Get().Character_State_Dead); + if(IsVisible && !StateManagerComponent->IsCurrentStateEqualToAny(GameplayTagContainer)) BossHealthWidget->SetVisibility(ESlateVisibility::Visible); else BossHealthWidget->SetVisibility(ESlateVisibility::Hidden); diff --git a/Source/D1/AI/MasterAI.cpp b/Source/D1/AI/MasterAI.cpp index 3e1e7c4b..58596328 100644 --- a/Source/D1/AI/MasterAI.cpp +++ b/Source/D1/AI/MasterAI.cpp @@ -95,6 +95,9 @@ AMasterAI::AMasterAI() WalkingSpeed = 200.f; JoggingSpeed = 500.f; SprintSpeed = 700.f; + + //Settings OwnedGameplayTags + OwnedGameplayTags.AddTag(FCombatGameplayTags::Get().Character_Enemy); PelvisBoneName = TEXT("pelvis"); } @@ -215,7 +218,9 @@ float AMasterAI::PerformAction(FGameplayTag ActionTag, FGameplayTag StateTag, in { StateManagerComponent->SetCurrentState(StateTag); StateManagerComponent->SetCurrentAction(ActionTag); - actionDuration = PlayAnimMontage(actionMontage); + if(!IsValid(GetMesh()->GetAnimInstance())) + return 0.f; + actionDuration = GetMesh()->GetAnimInstance()->Montage_Play(actionMontage); } else { @@ -250,7 +255,9 @@ float AMasterAI::PerformAttack(FGameplayTag AttackType, int32 AttackIndex, bool StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Attacking); StateManagerComponent->SetCurrentAction(AttackType); - attackDuration = PlayAnimMontage(attackMontage); + if(!IsValid(GetMesh()->GetAnimInstance())) + return 0.f; + attackDuration = GetMesh()->GetAnimInstance()->Montage_Play(attackMontage); int idx = attackIdx + 1; if (idx >= montages.Num()) @@ -424,7 +431,11 @@ void AMasterAI::ApplyImpactEffect(EDamageType InDamageType) void AMasterAI::PerformDeath() { EnableRagdoll(); - ApplyHitReactionPhysicsVelocity(2000.f); //충돌을 좀 더 그럴싸하게 하기 위해서 뒷방향으로 충격 + + if(bHitFront) //충돌을 좀 더 그럴싸하게 하기 위해서 피격방향으로 충격 + ApplyHitReactionPhysicsVelocity(2000.f); + else + ApplyHitReactionPhysicsVelocity(-2000.f); FTimerHandle deathTimer; GetWorld()->GetTimerManager().SetTimer(deathTimer, FTimerDelegate::CreateLambda([&]() diff --git a/Source/D1/AI/MasterAI.h b/Source/D1/AI/MasterAI.h index a4f1ab26..ed7a3a74 100644 --- a/Source/D1/AI/MasterAI.h +++ b/Source/D1/AI/MasterAI.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" +#include "GameplayTagAssetInterface.h" #include "Components/StatsComponent.h" #include "GameFramework/Character.h" #include "Interface/CombatInterface.h" @@ -12,7 +13,7 @@ #include "MasterAI.generated.h" UCLASS() -class D1_API AMasterAI : public ACharacter, public ICombatInterface, public ITargetingInterface +class D1_API AMasterAI : public ACharacter, public ICombatInterface, public ITargetingInterface, public IGameplayTagAssetInterface { GENERATED_BODY() @@ -71,6 +72,9 @@ public: // Inherited via ITargetingInterface virtual bool CanBeTargeted() override; virtual void OnTargeted(bool bIsTargeted) override; + + // Inherited via IGameplayTagAssetInterface + virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override { TagContainer = OwnedGameplayTags; } public: //Using Child Class virtual void OnTargetSet(AActor* NewTarget); public: @@ -152,6 +156,8 @@ private: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Patrol", meta = (AllowPrivateAccess = "true")) TArray> PatrolPoints; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayTags", meta = (AllowPrivateAccess = "true")) + FGameplayTagContainer OwnedGameplayTags; private: FTimerHandle StaminaTimerHandle; diff --git a/Source/D1/CombatPlayerCharacter.cpp b/Source/D1/CombatPlayerCharacter.cpp index 11c89028..92604ef7 100644 --- a/Source/D1/CombatPlayerCharacter.cpp +++ b/Source/D1/CombatPlayerCharacter.cpp @@ -102,6 +102,12 @@ ACombatPlayerCharacter::ACombatPlayerCharacter() ChargeAttackTime = 0.18f; PelvisBoneName = TEXT("pelvis"); + //Setting Timeline + FOnTimelineFloat CurveFloatCallback; + CurveFloatCallback.BindUFunction(this, FName("RotateToTargetUpdate")); + RotateToTargetTimeLineComponent = CreateDefaultSubobject(TEXT("RotateToTargetTimeLineComponent")); + RotateToTargetTimeLineComponent->SetTimelineLength(5.0f); + RotateToTargetTimeLineComponent->AddInterpFloat(CurveFloatTimeline, CurveFloatCallback); } void ACombatPlayerCharacter::BeginPlay() @@ -127,6 +133,11 @@ void ACombatPlayerCharacter::BeginPlay() SpawnItem->OnEquipped(); } +void ACombatPlayerCharacter::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); +} + float ACombatPlayerCharacter::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) { float fDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser); @@ -282,12 +293,13 @@ float ACombatPlayerCharacter::PerformAction(FGameplayTag ActionTag, FGameplayTag { StateManagerComponent->SetCurrentState(StateTag); StateManagerComponent->SetCurrentAction(ActionTag); - actionDuration = PlayAnimMontage(actionMontage); - return true; + if(!IsValid(GetMesh()->GetAnimInstance())) + return 0.f; + actionDuration = GetMesh()->GetAnimInstance()->Montage_Play(actionMontage); } else { - FString str = FString::Printf(TEXT("Dodge Index %n is NOT VALID!!"), montageIdx); + FString str = FString::Printf(TEXT("Dodge Index %d is NOT VALID!!"), montageIdx); GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str); } return actionDuration; @@ -324,9 +336,11 @@ float ACombatPlayerCharacter::PerformAttack(FGameplayTag AttackType, int32 Attac { StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Attacking); StateManagerComponent->SetCurrentAction(AttackType); - - attackDuration = PlayAnimMontage(attackMontage); - + + if(!IsValid(GetMesh()->GetAnimInstance())) + return 0.f; + attackDuration = GetMesh()->GetAnimInstance()->Montage_Play(attackMontage); + int idx = attackIdx + 1; if (idx >= montages.Num()) idx = 0; @@ -521,7 +535,9 @@ void ACombatPlayerCharacter::CharacterStateBegin(FGameplayTag CharState) if (FGameplayTag::EmptyTag == CharState) {/*None*/} else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState) - {/*None*/} + { + RotateToTarget(); + } else if (FCombatGameplayTags::Get().Character_State_Dodging == CharState) {/*None*/} else if (FCombatGameplayTags::Get().Character_State_GeneralActionState == CharState) @@ -539,7 +555,9 @@ void ACombatPlayerCharacter::CharacterStateEnd(FGameplayTag CharState) if (FGameplayTag::EmptyTag == CharState) {/*None*/} else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState) - {/*None*/} + { + StopRotateToTarget(); + } else if (FCombatGameplayTags::Get().Character_State_Dodging == CharState) {/*None*/} else if (FCombatGameplayTags::Get().Character_State_GeneralActionState == CharState) @@ -693,11 +711,39 @@ void ACombatPlayerCharacter::ApplyImpactEffect(EDamageType InDamageType) UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), HitEmitter, LastHitInfo.Location); } +void ACombatPlayerCharacter::RotateToTarget() +{ + RotateToTargetTimeLineComponent->PlayFromStart(); +} + +void ACombatPlayerCharacter::StopRotateToTarget() +{ + RotateToTargetTimeLineComponent->Stop(); +} + +void ACombatPlayerCharacter::RotateToTargetUpdate(float Output) +{ + if(!IsValid(TargetingComponent)) + return; + + const FRotator CurrentRotator = GetActorRotation(); + FRotator TargetRotator = UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), TargetingComponent->GetTargetActor()->GetActorLocation()); + double deltaTime = UGameplayStatics::GetWorldDeltaSeconds(GetOwner()); + FRotator NewRotator = FMath::RInterpTo(CurrentRotator, TargetRotator, deltaTime, RotateToTargetInterpSpeed); + NewRotator.Roll = CurrentRotator.Roll; + NewRotator.Pitch = CurrentRotator.Pitch; + SetActorRotation(NewRotator); +} + void ACombatPlayerCharacter::PerformDeath() { EnableRagdoll(); - ApplyHitReactionPhysicsVelocity(2000.f); //충돌을 좀 더 그럴싸하게 하기 위해서 뒷방향으로 충격 + if(bHitFront) //충돌을 좀 더 그럴싸하게 하기 위해서 피격방향으로 충격 + ApplyHitReactionPhysicsVelocity(2000.f); + else + ApplyHitReactionPhysicsVelocity(-2000.f); + if (IsValid(CombatComponent->GetMainWeapon())) CombatComponent->GetMainWeapon()->SimulateWeaponPhysics(); //무기의 충돌킴 diff --git a/Source/D1/CombatPlayerCharacter.h b/Source/D1/CombatPlayerCharacter.h index a4057266..032367c0 100644 --- a/Source/D1/CombatPlayerCharacter.h +++ b/Source/D1/CombatPlayerCharacter.h @@ -11,6 +11,7 @@ #include "Components/StateManagerComponent.h" #include "Components/StatsComponent.h" #include "Components/TargetingComponent.h" +#include "Components/TimelineComponent.h" #include "Definitions/GameEnums.h" #include "CombatPlayerCharacter.generated.h" @@ -85,6 +86,7 @@ protected: // APawn interface virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; virtual void BeginPlay() override; + virtual void Tick(float DeltaSeconds) override; virtual float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override; public: @@ -158,6 +160,11 @@ private: void ApplyHitReaction(EDamageType InDamageType); void ApplyImpactEffect(EDamageType InDamageType); + //Timeline + void RotateToTarget(); + void StopRotateToTarget(); + void RotateToTargetUpdate(float Output); + private: void PerformDeath(); bool PerformHitStun(); @@ -210,6 +217,13 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage", meta = (AllowPrivateAccess = "true")) TObjectPtr KnockdownBackMontage; +private: //Timeline + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Timeline", meta = (AllowPrivateAccess = "true")) + TObjectPtr RotateToTargetTimeLineComponent; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Timeline", meta = (AllowPrivateAccess = "true")) + TObjectPtr CurveFloatTimeline; + const float RotateToTargetInterpSpeed = 9.f; + private: FTimerHandle StaminaTimerHandle; diff --git a/Source/D1/Components/CollisionComponent.cpp b/Source/D1/Components/CollisionComponent.cpp index c10a727e..f45fd54d 100644 --- a/Source/D1/Components/CollisionComponent.cpp +++ b/Source/D1/Components/CollisionComponent.cpp @@ -48,7 +48,7 @@ void UCollisionComponent::CollisionTrace() for (auto hitResult : OutHits) { LastHit = hitResult; - if (!AlreadyHitActors.Contains(LastHit.GetActor())) + if (CanHitActor(LastHit.GetActor())) { HitActor = LastHit.GetActor(); AlreadyHitActors.Add(HitActor); @@ -65,10 +65,14 @@ void UCollisionComponent::ActivateCollision() bool UCollisionComponent::CanHitActor(AActor* InActor) { + bool result = false; IGameplayTagAssetInterface* tagInterface = Cast(InActor); if(tagInterface) { - + result &= !tagInterface->HasAnyMatchingGameplayTags(GameplayTagsIgnore); //Ignore Tag + result &= !AlreadyHitActors.Contains(InActor); //Alread Hit + result &= !ActorsToIgnore.Contains(InActor); //Ignore actor } + return result; } diff --git a/Source/D1/Components/CollisionComponent.h b/Source/D1/Components/CollisionComponent.h index f309bac9..89ebe7bd 100644 --- a/Source/D1/Components/CollisionComponent.h +++ b/Source/D1/Components/CollisionComponent.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" +#include "GameplayTagContainer.h" #include "Components/ActorComponent.h" #include "Kismet/KismetSystemLibrary.h" #include "CollisionComponent.generated.h" @@ -48,6 +49,7 @@ public: FORCEINLINE void SetEndSocketName(FName InpuEndSocketName) { EndSocketName = InpuEndSocketName; } FORCEINLINE void SetCollisionObjectTypes(TArray> InputCollisionObjectTypes) { CollisionObjectTypes = InputCollisionObjectTypes; } FORCEINLINE void SetDrawDebugType(TEnumAsByte InputDrawDebugType) { DrawDebugType = InputDrawDebugType; } + FORCEINLINE void SetGameplayTagsIgnore(FGameplayTagContainer& InputGameplayTagContainer) { GameplayTagsIgnore = InputGameplayTagContainer; } public: UFUNCTION(BlueprintCallable) FORCEINLINE void ActivateCollision(); @@ -58,27 +60,29 @@ public: private: UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Initialization", meta=(AllowPrivateAccess="true")) float TraceRadius; - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Initialization", meta = (AllowPrivateAccess = "true")) FName StartSocketName; - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Initialization", meta = (AllowPrivateAccess = "true")) FName EndSocketName; - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Initialization", meta = (AllowPrivateAccess = "true")) TArray> CollisionObjectTypes; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Initialization", meta = (AllowPrivateAccess = "true")) TEnumAsByte DrawDebugType; - + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Initialization", meta = (AllowPrivateAccess = "true")) + FGameplayTagContainer GameplayTagsIgnore; + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components", meta = (AllowPrivateAccess = "true")) TObjectPtr CollisionMeshComponent; - bool bCollisionEnabled; + UPROPERTY() TArray> AlreadyHitActors; + UPROPERTY() TArray> ActorsToIgnore; - FHitResult LastHit; + UPROPERTY() TObjectPtr HitActor; + + bool bCollisionEnabled; + FHitResult LastHit; public: FOnHit OnHitDelegate; diff --git a/Source/D1/Components/StateManagerComponent.cpp b/Source/D1/Components/StateManagerComponent.cpp index d3a0bd41..58ca4721 100644 --- a/Source/D1/Components/StateManagerComponent.cpp +++ b/Source/D1/Components/StateManagerComponent.cpp @@ -52,7 +52,7 @@ void UStateManagerComponent::ResetState() CurrentState = FGameplayTag::EmptyTag; } -bool UStateManagerComponent::IsCurrentStateEqualToAny(FGameplayTagContainer StatesToCheck) +bool UStateManagerComponent::IsCurrentStateEqualToAny(FGameplayTagContainer& StatesToCheck) { return StatesToCheck.HasTagExact(CurrentState); } @@ -72,7 +72,7 @@ FGameplayTag UStateManagerComponent::GetCurrentAction() return CurrentAction; } -bool UStateManagerComponent::IsCurrentActionEqualToAny(FGameplayTagContainer ActionToCheck) +bool UStateManagerComponent::IsCurrentActionEqualToAny(FGameplayTagContainer& ActionToCheck) { return ActionToCheck.HasTagExact(CurrentAction); } \ No newline at end of file diff --git a/Source/D1/Components/StateManagerComponent.h b/Source/D1/Components/StateManagerComponent.h index a5b5fb86..78670b1d 100644 --- a/Source/D1/Components/StateManagerComponent.h +++ b/Source/D1/Components/StateManagerComponent.h @@ -34,11 +34,11 @@ public: void SetCurrentState(FGameplayTag NewState); FGameplayTag GetCurrentState(); void ResetState(); - bool IsCurrentStateEqualToAny(FGameplayTagContainer StatesToCheck); + bool IsCurrentStateEqualToAny(FGameplayTagContainer& StatesToCheck); void SetCurrentAction(FGameplayTag NewAction); FGameplayTag GetCurrentAction(); - bool IsCurrentActionEqualToAny(FGameplayTagContainer ActionToCheck); + bool IsCurrentActionEqualToAny(FGameplayTagContainer& ActionToCheck); public: //Delegate FStateBegin OnStateBegin; diff --git a/Source/D1/Definitions/CombatGameplayTags.cpp b/Source/D1/Definitions/CombatGameplayTags.cpp index 9ac68613..03f67f87 100644 --- a/Source/D1/Definitions/CombatGameplayTags.cpp +++ b/Source/D1/Definitions/CombatGameplayTags.cpp @@ -12,12 +12,17 @@ void FCombatGameplayTags::InitializeNativeGameplayTags() return; /** - * Player + * Character */ GameplayTags.Character_Player = UGameplayTagsManager::Get().AddNativeGameplayTag( FName("Character.Player"), FString("Player") ); + + GameplayTags.Character_Enemy = UGameplayTagsManager::Get().AddNativeGameplayTag( + FName("Character.Enemy"), + FString("Enemy") + ); /** * State diff --git a/Source/D1/Definitions/CombatGameplayTags.h b/Source/D1/Definitions/CombatGameplayTags.h index 09df1051..e3768048 100644 --- a/Source/D1/Definitions/CombatGameplayTags.h +++ b/Source/D1/Definitions/CombatGameplayTags.h @@ -14,6 +14,7 @@ public: public: //Character FGameplayTag Character_Player; + FGameplayTag Character_Enemy; //State FGameplayTag Character_State_Attacking;